본문으로 건너뛰기

다중 단계 빌드

다중 단계 빌드는 Dockerfile을 최적화하면서도 읽기 쉽고 유지 관리하기 어려웠던 사람들에게 유용합니다.

다중 단계 빌드 없이

Docker 17.05 이전에는 일반적으로 Docker 이미지를 빌드하는 두 가지 방법이 있었습니다.

하나의 Dockerfile에 모두 포함

한 가지 방법은 프로젝트와 종속성의 컴파일, 테스트 및 패키징을 포함한 모든 빌드 단계를 단일 Dockerfile에 포함하는 것이었습니다. 이것은 다음과 같은 문제를 야기할 수 있습니다.

  • 여러 이미지 레이어, 더 큰 이미지 크기, 더 긴 배포 시간

  • 소스 코드 누출 위험

예를 들어 Hello World!를 출력하는 app.go 파일을 작성합니다.

package main

import "fmt"

func main(){
fmt.Printf("Hello World!");
}

Dockerfile.one 파일을 작성합니다.

FROM golang:alpine

RUN apk --no-cache add git ca-certificates

WORKDIR /go/src/github.com/go/helloworld/

COPY app.go .

RUN go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \
&& cp /go/src/github.com/go/helloworld/app /root

WORKDIR /root/

CMD ["./app"]

이미지를 빌드합니다.

$ docker build -t go/helloworld:1 -f Dockerfile.one .

여러 Dockerfile로 분할

다른 방법은 먼저 하나의 Dockerfile에서 프로젝트와 종속성을 컴파일, 테스트 및 패키징한 다음 다른 Dockerfile에서 아티팩트를 런타임 환경으로 복사하는 것이었습니다. 이 접근 방식에서는 두 개의 Dockerfile과 두 단계를 자동화하기 위한 일부 빌드 스크립트를 작성해야 했습니다. 이것은 배포 프로세스를 더 복잡하게 만들었지만 첫 번째 접근 방식의 위험은 피할 수 있었습니다.

예를 들어 Dockerfile.build 파일을 작성합니다.

FROM golang:alpine

RUN apk --no-cache add git

WORKDIR /go/src/github.com/go/helloworld

COPY app.go .

RUN go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

Dockerfile.copy 파일을 작성합니다.

FROM alpine:latest

RUN apk --no-cache add ca-certificates

WORKDIR /root/

COPY app .

CMD ["./app"]

build.sh 스크립트를 생성합니다.

#!/bin/sh
echo Building go/helloworld:build

docker build -t go/helloworld:build . -f Dockerfile.build

docker create --name extract go/helloworld:build
docker cp extract:/go/src/github.com/go/helloworld/app ./app
docker rm -f extract

echo Building go/helloworld:2

docker build --no-cache -t go/helloworld:2 . -f Dockerfile.copy
rm ./app

이제 이 스크립트를 실행하여 이미지를 빌드합니다.

$ chmod +x build.sh

$ ./build.sh

두 가지 접근 방식으로 생성된 이미지 크기를 비교합니다.

$ docker image ls

REPOSITORY TAG IMAGE ID CREATED SIZE
go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB

다중 단계 빌드 사용

위의 문제를 해결하기 위해 Docker v17.05에서 다중 단계 빌드 지원이 도입되었습니다. 다중 단계 빌드를 사용하면 앞서 언급한 문제를 쉽게 해결할 수 있으며, 단일 Dockerfile만 작성하면 됩니다.

예를 들어 Dockerfile 파일을 작성합니다.

FROM golang:alpine as builder

RUN apk --no-cache add git

WORKDIR /go/src/github.com/go/helloworld/

RUN go get -d -v github.com/go-sql-driver/mysql

COPY app.go .

RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest as prod

RUN apk --no-cache add ca-certificates

WORKDIR /root/

COPY --from=0 /go/src/github.com/go/helloworld/app .

CMD ["./app"]

이미지를 빌드합니다.

$ docker build -t go/helloworld:3 .

세 이미지의 크기를 비교합니다.

$ docker image ls

REPOSITORY TAG IMAGE ID CREATED SIZE
go/helloworld 3 d6911ed9c846 7 seconds ago 6.47MB
go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB

다중 단계 빌드를 사용하여 빌드된 이미지의 크기가 작으며, 앞서 언급한 문제도 완벽하게 해결했음을 알 수 있습니다.

이전 단계를 새 단계로 사용

다중 단계 빌드를 사용할 때 Dockerfile에서 이전에 생성한 단계에서만 복사하는 것이 아닙니다. COPY --from 명령을 사용하여 로컬 이미지 이름, Docker 레지스트리에서 사용 가능한 태그 또는 태그 ID를 사용하여 별도의 이미지에서 복사할 수 있습니다. Docker 클라이언트는 필요한 경