Docker build(2)

홍석희·2023년 12월 21일
0

docker

목록 보기
3/6
post-thumbnail

Multi-stage

build stage는 Dockerfile의 FROM 명령어로 표현됨

FROM: base image로부터 새로운 build stage를 생성

multi-stage build 사용 이유

  • 병렬수행, build pipeline을 더 빠르고 효율적이게 한다
  • 프로그램 실행에 필요한 것들만을 포함하여 더 작은 크기의 이미지 생성
docker build --tag=buildme .
docker images buildme
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
buildme      latest    c021c8a7051f   5 seconds ago   150MB

이전 포스트의 multi-stage를 사용하지 않은 Dockerfile로, 컴파일 시에 사용된 resource들의 영향으로 image의 크기가 매우 커진 것을 보여준다

이 이미지를 multi-stage를 사용하여 크기와 빌드 속도를 높혀보자

Add stages

FROM 명령어를 사용하여 build-time과 run-time stage를 구분할 수 있다

multi-stage build를 사용하면 build-time과 run-time에 서로 다른 base images를 선택할 수 있다.

build-time stage의 결과로 생성된 build artifacts(실행 가능한 파일, 라이브러리, 구성 파일 등을 포함)를 run-time stage로 복사할 수 있다

아래의 Dockerfile에서는 scratch 이미지를 사용하여 run-time stage를 생성한다. 예제의 scratch stage에서는 COPY --from=0 를 통해 이전 stage에서 빌드된 이진파일이 새로운 stage의 파일시스템에 복사된다. COPY --from=0 는 첫 번째 빌드단계의 stage를 가리킨다

FROM scratch

빈 이미지로 base image 또는 초경량 이미지(only contains a single binary)의 빌드에 사용됨

  # syntax=docker/dockerfile:1
  FROM golang:1.21-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" ]

build된 이미지의 크기를 살펴보면 binaries 만을 포함하여, 크기가 현저하게 줄어듦을 확인할 수 있다

docker build --tag=buildme .
docker images buildme
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
buildme      latest    436032454dd8   7 seconds ago   8.45MB

Parallelism

parallelism을 사용하면 이미지 빌드 속도를 높힐 수 있다

stage를 나누는 것으로 binary-building step을 분리할 수 있다

FROM image AS stage_name 을 사용하여 stage에 이름 부여 가능, 다른 FROM 명령어에 있는 stage 이름을 통해 stage를 참조하는 것이 가능.
아래의 예제에서는 FROM base AS build-client 를 통해 base stage를 참조하여 두 번째 build-time stage를 실행

COPY --from=stae_name 명령어에서 볼 수 있듯, binary-building stage에도 stage 이름을 지정 가능

  # syntax=docker/dockerfile:1
- FROM golang:1.21-alpine
+ FROM golang:1.21-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=0 /bin/client /bin/server /bin/
+ COPY --from=build-client /bin/client /bin/
+ COPY --from=build-server /bin/server /bin/
  ENTRYPOINT [ "/bin/server" ]

위와 같이 Dockerfile을 구성하면 client 측과 server 측 이진파일의 빌드가 하나하나씩 이루어 지지 않고, build-clientbuild-server 이 병렬적으로 실행된다

Build targets

하나의 Dockerfile로 서로 다른 여러 개의 이미지를 만들 수도 있다
--target flag를 통해 target stage를 명시한다
여기서는 이름을 지정하지 않았던 FROM scratch stage를 client server 라는 이름으로 stage를 분리한다

  # syntax=docker/dockerfile:1
  FROM golang:1.21-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 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" ]

이 Dockerfile을 실행하면 다음과 같이 2개의 이미지가 생성된다

docker build --tag=buildme-client --target=client .
docker build --tag=buildme-server --target=server .
docker images "buildme*" 
REPOSITORY       TAG       IMAGE ID       CREATED          SIZE
buildme-client   latest    659105f8e6d7   20 seconds ago   4.25MB
buildme-server   latest    666d492d9f13   5 seconds ago    4.2MB

build target을 사용하면 필요한 stage들만 빌드하고 나머지는 생략할 수 있다

Summary

Multi-stage build는 생성되는 이미지가 너무 큰 용량을 차지하지 않도록 도와주고 더 빠르게 동작하도록 해준다

참고자료:

https://docs.docker.com/build/guide/multi-stage/
https://hub.docker.com/_/scratch/

profile
개발 기록

0개의 댓글