야 Docker 이리나와!! [5] - Multi-Stage builds

김진성·2021년 10월 11일
1

Docker

목록 보기
5/6
post-thumbnail

Multi-Stage builds

  • Multi-Stage builds는 Dockerfile들을 최적화하는 과정에서 읽고 유지보수하기 쉽게 만들기 위해 사용된다.

Multi-Stage 이전에는 어땠을까?

  • Dockerfile의 각 명령어는 하나의 이미지에 대응해 Layer가 추가되기 때문에 더해지는 쓰면 쓸수록 전체적인 Layer의 수가 증가할 뿐만 아니라 용량도 증가한다.

예시 1) Dockerfile.build

FROM golang:1.16
WORKDIR /go/src/github.com/alexellis/href-counter/
COPY app.go ./
RUN go get -d -v golang.org/x/net/html \
  && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
  • 여기 파일을 보면 RUN 명령어 안에 && 연산자를 사용해 이미지 안에 추가적인 Layer가 생성되는 것을 피하고 있다. 이것은 유지보수하기가 어렵다.
  • 대신, \ 철자를 사용하여 다른 명령어를 집어넣어 까먹지 않게 할 수 있다.

예시 2) Dockerfile와 build.sh

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app ./
CMD ["./app"]  
#!/bin/sh
echo Building alexellis2/href-counter:build

docker build --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy \  
    -t alexellis2/href-counter:build . -f Dockerfile.build

docker container create --name extract alexellis2/href-counter:build  
docker container cp extract:/go/src/github.com/alexellis/href-counter/app ./app  
docker container rm -f extract

echo Building alexellis2/href-counter:latest

docker build --no-cache -t alexellis2/href-counter:latest .
rm ./app

build.sh를 살펴보면 첫 이미지를 복사해 컨테이너를 생성하고 그 다음 두번째 이미지를 빌드한다. 앞에 생성된 이미지를 복사하고 컨테이너를 또 생성해야되는 문제로 시스템 내 차지하는 용량이 점점 커지고 관리가 불편해진다. 이러한 이유로 Multi-Stage builds가 대두된 것이다.

Multi-Stage Builds 사용하기

  • Multi-Stage Builds에서 대표적인 특징은 여러 개의 FROM 명령문을 쓴다는 것이다.
  • 빌드 시작점에 FROM으로 각각 다른 기초가 되는 베이스 라인을 가져오고 최종 이미지를 만들게 된다.

예시) Dockerfile

FROM golang:1.16
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go ./
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app ./
CMD ["./app"]  
  • 예시에 나온 것처럼 하나의 Dockerfile로 Build 할 수 있다는 큰 장점이 있다.
$ docker build -t alexellis2/href-counter:latest .
  • 이 과정을 통해 최종 이미지를 생성하기 위한 여러 개의 중간 이미지들을 만들 필요가 없고 편리하다.

Build Stages 명칭

  • 일반적으로 각 단계는 이름이 붙여지지 않지만 첫 FROM 명령어를 시작으로 0부터 정수형으로 지칭되게 된다.
  • 만약 단계에 명칭을 붙이고 싶다면 FROM * AS NAME의 형태로 명칭을 붙일 수 있다.

예시) Dockerfile

FROM golang:1.16 AS builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go    ./
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app ./
CMD ["./app"]  

특정 단계에서 멈추기

  • 이미지를 빌드하는 과정에서 모든 단계가 포함된 Dockerfile을 매번 실행할 필요가 없다. 그래서 특정 단계만 실행하고 멈추는 방법이 있다.
$ docker build --target builder -t alexellis2/href-counter:latest .
  • 이 방법은 특정 시나리오들에 대해서 매우 유용하다.
  1. 특정 빌드 단계를 수정할 경우
  2. 디버깅 상징이나 도구들이 사용가능한 상태에서 Debug 단계를 사용하고 Production 단계로 갈 경우
  3. test 데이터를 활용해 testing 단계를 사용해보고 실제 데이터로 제품 테스트를 할 경우

외부 이미지를 "stage"로 사용하기

  • 우리가 Multi-Stage Build를 진행할 때 외부 이미지를 사용할 때 Dockerfile 내에서 미리 생성해 놓은 단계들을 COPY하는 것은 한계가 없다.
  • COPY --from 명령어를 사용해 특정 이미지를 복사해 가져오면 된다.
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf

이전 단계를 새로운 단계로 사용하기

  • Dockerfile 예시를 보면 처음 생성된 builder가 나중에 build2로 바뀌어 사용되게 된다.

예시) Dockerfile

FROM alpine:latest AS builder
RUN apk --no-cache add build-base

FROM builder AS build1
COPY source1.cpp source.cpp
RUN g++ -o /binary source.cpp

FROM builder AS build2
COPY source2.cpp source.cpp
RUN g++ -o /binary source.cpp
profile
https://medium.com/@jinsung1048 미디엄으로 이전하였습니다.

0개의 댓글