다중 단계 빌드
다중 단계 빌드는 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 클라이언트는 필요한 경