본문 바로가기
Framework and Tool/Docker

Docker - Multi stage

by ocwokocw 2023. 6. 20.

- 출처: https://docs.docker.com/build/guide/multi-stage/

- Multi stage

Docker는 multi-stage라는 기능을 제공한다. 왜 Multi-stage를 사용해야할까?

  • build를 병렬로 수행할 수 있다.
  • 마지막 이미지 크기를 더 작게 만들 수 있다.

- Add stages

그렇다면 build stage란 무엇인가? build stage는 Dockerfile에서 FROM 지시어에 해당한다. 이전 Section(https://ocwokocw.tistory.com/302) 의 Dockerfile은 단일 stage에 해당하는데 최종 image가 프로그램을 컴파일하는데 사용되는 resource 때문에 용량이 큰것을 확인할 수 있다.

% docker images | grep buildme
buildme                                   5                                            c5650458b890   24 hours ago    410MB
buildme                                   4                                            9f43dd927be3   24 hours ago    410MB
buildme                                   3                                            9260d12d7690   24 hours ago    410MB
buildme                                   2                                            ad54f8e8cc3c   24 hours ago    410MB
buildme                                   latest                                       ad54f8e8cc3c   24 hours ago    410MB

Multi-stage build를 사용하면 build와 runtime 환경에 따라 서로 다른 기본 image를 선택할 수 있다. Build stage에서 최종 산출물만 runtime stage로 복사하면 된다.

 

아래와 같이 Dockerfile을 수정해보자.

% cat Dockerfile
# syntax=docker/dockerfile:1
FROM golang:1.20-alpine
WORKDIR /src
COPY go.mod go.sum .
RUN go mod download
COPY . .
RUN go build -o /bin/client ./cmd/client
RUN go build -o /bin/server ./cmd/server

FROM scratch
COPY --from=0 /bin/client /bin/server /bin
ENTRYPOINT [ "/bin/server" ]

추가된 stage (FROM 절) 에서 기본 image를 초소형인 scratch image로 변경하였다. scratch stage 에서는 이전 stage 에서 build된 binary 들만 복사해왔다.

% docker build -t buildme .
[+] Building 9.3s (19/19) FINISHED
...
% docker images | grep buildme
buildme                                   latest                                       09db1cefa293   7 seconds ago   7.45MB
buildme                                   5                                            c5650458b890   24 hours ago    410MB
buildme                                   4                                            9f43dd927be3   24 hours ago    410MB
buildme                                   3                                            9260d12d7690   24 hours ago    410MB
buildme                                   2                                            ad54f8e8cc3c   24 hours ago    410MB

Docker build를 수행해보면 410MB -> 7MB 가량으로 줄어든것을 확인할 수 있다.


- Parallelism

Image의 크기를 줄였으니 multi-stage의 병렬성을 활용하여 build 속도를 향상시켜보도록 하자. 현재는 binary를 순차적으로 생성한다. 하지만 사실 client, server binary 생성시에 순서를 지켜야할 필요는 없다. 이 부분을 별도 stage로 분리하여 build 한 뒤 마지막 scratch stage 에서 각 stage의 binary 들을 복사해주면 된다. 이때 각 stage 들을 병렬로 실행시킬 수 있다.

 

client, server binary를 생성할 때 Go 의 compile 도구와 application 의존성이 필요하다. 이 부분은 공통된 image로 정의해서 재활용하면된다. 설명을 하는게 더 어려우니 곧바로 예제를 보도록 하자.

% cat Dockerfile
# syntax=docker/dockerfile:1
FROM golang:1.20-alpine as base
WORKDIR /src
COPY go.mod go.sum .
RUN go mod download
COPY . .

FROM base as build-client
RUN go build -o /bin/client ./cmd/client

FROM base as build-server
RUN go build -o /bin/server ./cmd/server

FROM scratch
COPY --from=build-client /bin/client /bin/
COPY --from=build-server /bin/server /bin/
ENTRYPOINT [ "/bin/server" ]

FROM base as build-client 부분과 FROM base as build-server 부분이 동시에 build 되는것을 확인할 수 있을것이다.


- Build targets

stage 추가로 image의 크기를 줄이고 parallelism 으로 속도도 향상 시켰다. 그런데 일반적으로 server와 client binary가 하나의 image에 있는게 맞는가?

 

하나의 Dockerfile 에서 여러 개의 image를 만들 수 있다. --target flag를 이용하면 build의 target stage를 지정할 수 있다. Dockerfile 은 이름이 붙지 않은 FROM scratch 절을 client와 server라는 이름이 붙은 stage로 수정하면 된다.

% cat Dockerfile
# syntax=docker/dockerfile:1
FROM golang:1.20-alpine as base
WORKDIR /src
COPY go.mod go.sum .
RUN go mod download
COPY . .

FROM base as build-client
RUN go build -o /bin/client ./cmd/client

FROM base as build-server
RUN go build -o /bin/server ./cmd/server

FROM scratch as client
COPY --from=build-client /bin/client /bin/
ENTRYPOINT ["/bin/client"]

FROM scratch as server
COPY --from=build-server /bin/server /bin/
ENTRYPOINT [ "/bin/server" ]

--target flag를 이용해서 build 해보자. --target을 client로 지정하고 build 하면 docker는 build-sever와 server stage가 필요하지 않으므로 건너뛴다.

% docker build -t buildme-client --target=client .
[+] Building 8.0s (18/18) FINISHED
...                                                                        0.0s

% docker build -t buildme-server --target=server .
[+] Building 6.6s (16/16) FINISHED
...

% docker images | grep buildme-
buildme-server                            latest                                       350b924e9acb   2 minutes ago    7.45MB
buildme-client                            latest                                       ae5d71cc2e65   8 minutes ago    7.58MB

 

'Framework and Tool > Docker' 카테고리의 다른 글

Docker - build arguments  (0) 2023.06.28
Docker - Mounts  (0) 2023.06.21
Docker image and layer  (0) 2023.06.18
Docker resource constraints  (0) 2023.06.18
Docker logging  (0) 2023.06.17

댓글