Docker Image Registry

도커 Registry

  • 도커 Registry는 말 그대로 Image 저장소이다. 도커 허브는 도커 Registry중 가장 유명한 Registry이다. Default로 도커 엔진은 로컬에 없는 이미지를 내려받으려고 할때 , 도커 허브를 먼저 뒤져본다.

도커 이미지 이름

1
docker.io/diamol/golang:latest
  • docker.io : 이미지가 저장된 Registry의 도메인 이름 (도커 허브)
  • diamol : 이미지 작성자 , 단체의 이름
  • golang : 애플리케이션 이름
  • latest : 애플리케이션의 버전으로 이미지 태그라고 부르며 기본값은 latest이다.

도커 이미지 업로드

  • 도커 이미지 업로드 명령어는 다음과 같다.
    1
    docker image push $dockerId/앱이름:버전이름

  • 실제로 업로드를 하게 되면 도커 Registry도 로컬에서 동작하는 도커 엔진과 동일한 방식으로 실제 업로드 되는 것은 이미지 레이어들이다.

도커 이미지 태그 네이밍 권장사항

Read more

Docker Multi-Stage Build

멀티 스테이지 빌드

  • 각 빌드 단계는 FROM 명령어로 시작한다. 필요한 경우 AS 파라미터를 통해 이름을 붙일 수 있다.
  • 빌드가 여러 단계로 나뉘어져 있다고 하더라도 최종 산출물은 마지막 내용물을 담은 도커 이미지이다.
  • 각 빌드 단계는 격리되어서, 독립적으로 실행되지만 앞선 단계에서 만들어진 파일을 복사할 수 있다.
  • 예를 들면 아래와 같이 --from 인자를 사용해서 해당 파일이 호스트 컴퓨터의 파일 시스템이 아니라 , 앞선 빌드 단계의 파일 시스템에 파일을 복사할 것임을 명시할 수 있다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    FROM diamol/base AS build-stage 
    RUN echo 'Building...' > /build.txt

    FROM diamol/base AS test-stage
    COPY --from=build-stage /build.txt /build.txt
    RUN echo 'Testing...' >> /build.txt

    FROM diamol/base
    COPY --from=test-stage /build.txt /build.txt
    CMD cat /build.txt

Application 빌드 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
## 빌드 단계
FROM diamol/maven AS builder

WORKDIR /usr/src/iotd
COPY pom.xml .
RUN mvn -B dependency:go-offline

COPY . .
RUN mvn package
## 배포 단계
FROM diamol/openjdk

WORKDIR /app
COPY --from=builder /usr/src/iotd/target/iotd-service-0.1.0.jar .

EXPOSE 80
ENTRYPOINT ["java", "-jar", "/app/iotd-service-0.1.0.jar"]
  • 첫번쨰 빌드 단계에서는 Maven 이미지로 Maven을 이용해 의존 라이브러리를 내려받고 , mvn package를 통해 빌드&패키징을 수행한다.

  • 두번쨰 단계에서는 첫번쨰 빌드 단계의 파일 시스템에 존재하는 jar파일을 복사해서 포트를 외부 공개하고 , jar파일을 실행한다. ENTRYPOINT 명령어는 해당 이미지가 컨테이너로 실행될때 실행될 명령어이다.

  • 멀티 스테이지 빌드를 통해서 최종적으로 생성되는 Application 이미지에는 빌드도구를 포함시키지 않을 수 있다.

왜 멀티 스테이지 빌드를 사용해야하는가?

  • 애플리케이션 이미지 사이즈 감소 : 정말 필요한 리소스만 담아 ,이미지 크기를 줄일 수 있다.

Docker 이미지 레이어

Docker 이미지

  • Docker 이미지는 논리적으로는 하나의 대상이다.
    • Docker를 구성하는 각각의 파일을 이미지 레이어 라고 부른다. 도커 이미지는 물리적으로는 여러 개의 작은 파일로 구성돼 있다.
    • 하나의 이미지는 위와 같이 여러 이미지가 계층적으로 쌓인 형태로 저장된다. 정리하면 하나의 도커 이미지는 여러개의 이미지 레이어로 구성된다.
  • 도커가 이들 파일을 조립하여 컨테이너 내부 파일 시스템을 만들며 , 전체 이미지를 사용할 수 있게 된다.

Docker 이미지와 이미지 레이어

  • 도커 이미지란 이미지 레이어가 모인 논리적 대상이다.
  • 도커 이미지 레이어란 무엇일까? 아래의 명령어를 입력하면 이미지 레이어에 대한 정보가 출력된다.
1
docker image history web-ping

CREATED BY 칼럼은 해당 이미지 레이어를 구성하는 Dockerfile 스크립트의 명령어이다. 즉 , Dokcerfile의 명령어와 이미지 레이어는 1:1로 매핑된다.

  • 구체적으로 도커 이미지 레이어란 도커 엔진의 캐시에 물리적으로 저장된 파일이다.
    • 이미지 레이어는 여러 이미지와 컨테이너에서 공유된다.
    • 만약 Nodejs 런타임 이미지 레이어를 가진 컨테이너를 여러개 실행하면 이 컨테이너들은 모두 동일한 Nodejs 런타임이 들어 있는 이미지 레이어를 공유한다.
  • 앞서 이미지 레이어는 여러 이미지에서 공유된다고 하였다.
    1
    docker image ls 
    상기의 명령어를 입력하면 이미지의 “논리적” 크기를 확인할 수 있는데 , 마치 각각의 이미지가 75.3 MB을 잡아먹는것처럼보인다.

하지만 실제 이미지가 디스크에서 얼마나 차지하는지는 아래의 명령어로 확인이 가능하다.
실제로는 이미지 레이어는 이미지와 컨테이너에서 재사용되기 때문에 150.6MB가 아닌 75.3MB만 디스크에서 공간을 차지하고 있는것을 확인할 수 있다.

1
docker system df

공유 자원 : 이미지 레이어

  • 이미지 레이어는 앞서 정리한대로 여러 이미지에서 공유되는 자원이다. 따라서 공유 자원을 수정하면 이를 사용하고 있는 모든 이미지에게 영향이 갈것이다.
  • 도커는 이미지를 읽기 전용으로 만들어 이런 문제를 방지한다. 즉 이미지 레이어는 수정할 수 없다.

Dockerfile 스크립트 최적화

  • 도커는 캐시에 일치하는 레이어가 있는지 확인하기 위해 해시값을 사용하는데,
  1. Dockerfile의 명령어
  2. Dockerfile의 명령어에 의해 복사되는 파일의 내용
    위 2개로부터 계산된다. 즉 명령어가 일치하고 , 파일의 내용 역시 일치한다면 동일한 이미지 레이어 캐시를 사용한다는 말이다.
    만약 캐시 미스라면 실제로 Dockerfile의 명령어가 실행되고 , 해당 Dockerfile 스크립트 아래부터는 수정된것이 없다라도 모두 실행된다.

따라서 Dockerfile 스크립트의 명령어는 잘 수정하지 않는 명령어가 앞으로 오고 자주수정되는 명령어는 뒤로 오도록 배치해야지만 캐시된 이미지 레이어를 많이 재사용할 수 있다.
이는 빌드 시간을 줄이고 , 차지하는 디스크 용량 , 또 이미지를 네트워크를 통해 받는다면 네트워크 대역폭까지 줄일 수 있다.

예시

  • 아래의 Dockerfile은 명령어 위치만 변경하더라도 개선할 수 있다. app.js가 자주 수정되는 앱이라고 가정하였을때
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    FROM diamol/node

    ENV TARGET="blog.sixeyed.com"
    ENV METHOD="HEAD"
    ENV INTERVAL="3000"

    WORKDIR /web-ping
    COPY app.js .

    CMD ["node", "/web-ping/app.js"]

위 코드는 app.js 수정시마다 , CMD ["node", "/web-ping/app.js"] 이미지 레이어 역시 다시 빌드한다.
아래와 같이 스크립트를 개선하면 app.js 수정이 발생했을때 마지막 레이어를 제외하고는 모두 캐시된 이미지 레이어를 사용한다.

1
2
3
4
5
6
7
8
9
10
FROM diamol/node

CMD ["node", "/web-ping/app.js"]

ENV TARGET="blog.sixeyed.com" \
METHOD="HEAD" \
INTERVAL="3000"

WORKDIR /web-ping
COPY app.js .