https://docs.docker.com/build/building/multi-stage/
공식 문서를 번역하면서 공부한 것이라 오역이 있을 수 있습니다.
Use multi-stage builds
Multi-stage build는 이미지 빌드와 최종 결과물 사이를 깔끔하게 분리하여 최종 이미지 사이즈를 줄어준다.
Dockerfile 명령어를 여러 단계로 나누어 Dockerfile의 결과물이 애플리케이션을 실행하는 데 필요한 파일만 포함하도록 하자
multi-stage는 빌드 단계를 병렬로 실행하여 효율적으로 빌드할 수 있다.
빌드 프로세스의 마지막 단계에서 실제로 사용되는 이미지가 작성도므로, 빌드 캐시를 활용하여 이미지 레이어를 최소화할 수 있다.
예를 들어, 빌드에 여러 개의 레이어가 포함되어 있는 경우, 변경이 별로 없는 레이어 (빌드 캐시를 적극 활용할 수 있는 레이어)에서 자주 변경이 있어나는 레이어 순으로 지정할 수 있다.
- 애플리케이션 빌드를 위해 필요한 툴 설치
- 라이브러리 의존성 설치 또는 업데이트
- 애플리케이션 생성
아래 Go 애플리케이션 예제를 살펴보자
# syntax=docker/dockerfile:1
FROM golang:1.16-alpine AS build
# `docker build --no-cache .` 실행시 의존성 업데이트
RUN apk add --no-cache git
RUN go get github.com/golang/dep/cmd/dep
# Gopkg.toml Gopkg.lock 에 있는 프로젝트 의존성 나열
# 이러한 레이어는 GoPkg파일이 업데이트 되었을 때만 재 빌드 된다.
COPY Gopkg.lock Gopkg.toml /go/src/project/
WORKDIR /go/src/project/
# 라이브러리 의존성 설치
RUN dep ensure -vendor-only
# 전체 프로젝트를 복사하고 빌드
# 이 레이어는 프로젝트 디렉토리에 파일 변경이 있을 때만 다시 빌드됨
COPY . /go/src/project/
RUN go build -o /bin/project
# 이 결과물이 싱글 레이어 이미지에 들어감
FROM scratch
COPY --from=build /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
CMD ["--help"]
Multi-stage란
multi-stage는 Dockerfile을 읽기 쉽고 유지 관리하기 쉽게 유지하면서 최적하하는데 어려움을 줄여준다.
multi-stage를 사용하면 Dockerfile에서`FROM` 명령어를 여러번 사용할 수 있다.
`FROM` 명령어는 각각 다른 base 이미지를 사용할 수 있다. 그리고 각각 다른 base이미지를 사용하면 빌드의 새로운 stage를 시작할 수 있다.
한 stage에서 원하는 artifacts를 다른 stage로 복사할 수 있다.
다음 예시의 Dockerfile에서 2개의 분리된 stage가 있다.
- binary를 빌드하는 stage
- binary를 첫번째 stage에서 다음 stage로 복사하는 stage
# syntax=docker/dockerfile:1
FROM golang:1.23
WORKDIR /src
COPY <<EOF ./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=0 /bin/hello /bin/hello
CMD ["/bin/hello"]
multi-stage 동작 과정
두개의 stage를 생성하는데에 한개의 Dockerfile만 필요하고 build 명령도 한번만 입력하면 된다.
docker build -t hello .
위 빌드 결과물을 binary만 포함된 아주 작은 이미지이다. 애플리케이션을 빌드하는데 필요한 빌드 도구는 이미지 결과에 포함되지 않았다.
두번째 `FROM` 명령어에서 `scratch`를 base image로 새로운 stage를 빌드한다. `COPY --from=0` 라인에서 이전 stage에서 빌드된 산출물을 두번째 stage에 복사한다. 여기에 Go SDK와 다른 중간 산출물은 최종 이미지에 저장되지 않는다.
stage가 여러개라고 해서 도커 이미지가 여러개 생성되는 건 아니다. 가장 마지막에 실행된 stage작업이 도커 이미지로 생성된다.
위의 예로는 `scratch` 라는 베이스이미지만 최종 이미지로 생성되는 것이다.
ref
'DevOps > Docker' 카테고리의 다른 글
[Gitub Action] Pull Requst 했을 때 프로젝트 빌드 테스트하기 (1) | 2024.10.29 |
---|---|
[Docker] Build Context (0) | 2024.10.23 |
[Docker] Dockerfile 살펴보기 (0) | 2024.10.19 |
[Docker] 캐시를 사용한 효율적인 빌드 수행 방법(1) - Docker Build Cache 무효화 (3) | 2024.10.19 |