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 스테이지 사이의 의존성이 없으므로, 두개의 스테이지는 병렬로 동시에 실행된다.
참고: