애플리케이션 개발 시에 개발 환경에서 사용한 라이브러리나 개발 지원 툴이 제품 환경에서 반드시 사용되는 것은 아닙니다. 제품 환경에는 애플리케이션을 실행하기 위해 최소한으로 필요한 실행 모듈만 배치하는 것이 컴퓨팅 리소스를 효율적으로 활용할 수 있다는 점에서 보안 관점으로 볼 때 바람직합니다.

멀티스테이지 빌드란?

컨테이너 이미지를 만들면서 빌드 등에는 필요하지만, 최종 컨테이너 이미지에는 필요 없는 환경을 제거할 수 있도록 단계를 나누어 기반 이미지를 만드는 방법

Untitled

멀티스테이지 빌드를 사용하게 되면 위의 그림처럼 컨테이너 실행 시에는 빌드에 사용한 파일 및 디렉토리과 같은 의존 파일들이 모두 삭제된 상태로 컨테이너가 실행되게 됩니다. 결론적으로 좀 더 가벼운 크기의 컨테이너를 사용할 수 있게 됩니다.

Dockerfile 생성

# 1. Build Image
FROM golang:1.13 AS builder
 
# Install dependencies
WORKDIR /go/src/github.com/asashiho/dockertext-greet
RUN go get -d -v github.com/urfave/cli

# Build modules
COPY main.go .
RUN GOOS=linux go build -a -o greet .
 
# ------------------------------
# 2. Production Image
FROM busybox
WORKDIR /opt/greet/bin
 
# Deploy modules
COPY --from=builder /go/src/github.com/asashiho/dockertext-greet/ .
ENTRYPOINT ["./greet"]

위의 Dockerfile은 두 개의 부분으로 구성되어 있습니다.

1. 개발 환경용 Docker 이미지

Go의 버전 1.13을 베이스 이미지로 하고 'builder'라는 이름을 붙입니다. 그리고 개발에 필요한 것들을 모두 설치하여 로컬환경의 소스코드를 컨테이너로 복사합니다. 이 후 이 소스코드를 go build를 통하여 'greet'이라는 실행 가능한 바이너리 파일을 생성합니다.

2. 제품 환경용 Docker 이미지

제품 환경용 Docker 이미지의 베이스 이미지는 'busybox'를 사용합니다.

COPY --from=builder /go/src/github.com/asashiho/dockertext-greet/ .

이후 개발용 환경의 Docker 이미지로 빌드한 'greet'이라는 이름의 바이너리 파일(실행파일)을 제품 환경의 Docker 이미지로 복사합니다. 이 때 --from 옵션을 사용하여 **'builder'**라는 이름의 이미지로 부터 복사한다는 것을 선언합니다. 그리고 복사한 실행 파일을 실행하는 명령어를 적습니다.

이렇게 두 개의 Docker 이미지를 생성할 수 있는 Dockerfile을 작성하였습니다.

Dockerfile 빌드

작성한 Docekrfile을 바탕으로 Docker 이미지를 빌드합니다. (Dockerfile이 저장된 디렉토리에서 실행하세요.)

# docker build -t greet .

빌드 결과

Sending build context to Docker daemon  3.584kB
Step 1/9 : FROM golang:1.13 AS builder
1.13: Pulling from library/golang
...생략...
 ---> d6f3656320fe
Step 2/9 : WORKDIR /go/src/github.com/asashiho/dockertext-greet
 ---> Running in 2fff632654fd
Removing intermediate container 2fff632654fd
 ---> 1402883766eb
Step 3/9 : RUN go get -d -v github.com/urfave/cli
 ---> Running in 074623bde944
github.com/urfave/cli (download)
github.com/cpuguy83/go-md2man (download)
Removing intermediate container 074623bde944
 ---> bd25b6a92b46
Step 4/9 : COPY main.go .
 ---> 564233d46ae8
Step 5/9 : RUN GOOS=linux go build -a -o greet .
 ---> Running in a124ea2555fd
Removing intermediate container a124ea2555fd
 ---> 8fb64c0839d4
Step 6/9 : FROM busybox
...생략...
Step 7/9 : WORKDIR /opt/greet/bin
 ---> Running in a0e4798c47c3
Removing intermediate container a0e4798c47c3
 ---> 277d6a3d35ac
Step 8/9 : COPY --from=builder /go/src/github.com/asashiho/dockertext-greet/ .
 ---> 91bf7d5707a3
Step 9/9 : ENTRYPOINT ["./greet"]
 ---> Running in 026f6dce8aee
Removing intermediate container 026f6dce8aee
 ---> 047a37ee0f00
Successfully built 047a37ee0f00
Successfully tagged greet:latest