본문 바로가기
카테고리 없음

Docker Multi-stage로 이미지 빌드하기

by 홍띠 2023. 10. 28.

Multi-stage Build?

  • Multi-stage build는 하나의 Dockerfile에 여러개의 FROM절을 가짐
  • 각 FROM절은 다른 base이미지를 사용 가능하고, 각각의 stage가 됨
  • stage의 결과는 다른 stage로 COPY 되고, 최종 이미지에서는 각 stage의 필요한 것만 가져옴
  • 가장 아래의 FROM절이 최종 실행 이미지로 빌드됨

Multi-stage build 장점

  • 실행에 필요한 것만 골라서 가져오기 때문에 이미지가 경량화
  • Buildkit을 사용하면 의존성이 없는 스테이지들은 병렬로 처리 가능
  • 빌드, 배포 속도가 향상됨

How to use?

  • 지정된 이름이 없다면, 각 FROM절은 위->아래로 0부터 번호가 부여됨
  • as <name>으로 스테이지의 이름을 지정 할 수 있음
  • COPY --from=<name>으로 스테이지의 필요 파일만 복사해서 사용

기본 사용법

# syntax=docker/dockerfile:1
FROM golang:1.21 as build
WORKDIR /src
COPY <<EOF /src/main.go
package main

import "fmt"

func main() {
  fmt.Println("hello, world")
}
EOF
RUN go build -o /bin/hello ./main.go

FROM scratch
COPY --from=build /bin/hello /bin/hello
CMD ["/bin/hello"]

build 스테이지에서 golang 베이스 이미지를 이용해서 /bin/hello를 빌드한다. 이후 scratch 베이스 이미지를 최종 이미지로 사용하여 앞의 build 스테이지의 /bin/hello 결과물만 복사한다.

이렇게 되면 golang이미지의 불필요한 Go SDK과 중간 부산물들은 최종 이미지에는 포함되지 않고 가벼운 최종 이미지를 만들 수 있다.

Multi-stage의 특정 stage만 빌드

  • --target <name>을 이용하면 타겟 스테이지 까지만 빌드
docker build --target build -t hello .

병렬 처리

  • BuildKit은 stage간의 의존성이 없는 경우에는 각 stage들을 병령로 실행 시킴
  • 활용이 안되는 스테이지를 알아서 감별하고 skip 함
  • Docker engine 23.0 부터는 BuildKit을 기본 빌더로 사용
  • Legacy 도커엔진 빌더를 사용하는 경우, DOCKER_BUILDKIT=1 를 명시해 주어야함(참고)
# 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" ]

여기서 build-client 스테이지와 build-server 스테이지 사이의 의존성이 없으므로, 두개의 스테이지는 병렬로 동시에 실행된다.

 

 

참고:

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

https://docs.docker.com/build/building/multi-stage/