[Docker] 15. 도커 컨테이너 설계, 멀티 스테이지 빌드
카테고리: DOCKER
태그: docker
[Docker] 15. 도커 컨테이너 설계, 멀티 스테이지 빌드
🔔 도커 컨테이너 설계
(1) 1 컨테이너당 1 프로세스
-
컨테이너는 애플리케이션과 해당 애플리케이션을 실행하기 위한 실행 환경을 패키징함으로써 애플리케이션을 쉽게 실행하기 위한 도구로, 애플리케이션에서 중요한 역할을 한다.
-
주변 에코시스템도 이 사상을 바탕으로 만들어진 것들이 많으므로, 이를 무시하고 도커 컨테이너에 여러 프로세스를 기동하도록 만들면 주변 에코시스템과 맞지 않거나 관리가 힘들어진다.
(2) 변경 불가능한 인프라(Immutable infrastructure) 이미지로 생성한다.
변경 불가능한 인프라는 “환경을 변경할 때 오래된 환경은 없애고 새로운 환경을 만든다”, “한번 만들어진 환경은 절대 변경되지 않게 한다”라는 개념
-
전자는 쿠버네티스를 사용하는 경우 컨테이너의 상태를 모니터링하고 필요에 따라 새로운 컨테이너를 생성하거나 삭제 등의 작업을 통해 자동으로 환경을 다시 만들지만, 후자는 컨테이너 이미지 관리자가 고려해야 한다.
-
컨테이너 이미지는 애플리케이션을 실행하는데 필요한 바이너리나 리소스를 포함하는 파일로, 외부에서 변경되지 않도록 만들어야 한다. 그렇지 않으면 컨테이너의 실행 결과가 예상과 다르게 될 수 있다.
(3) 경량의 도커 이미지로 생성한다.
-
컨테이너를 실행할 때 노드상에서 사용할 도커 이미지가 없다면 외부에서 이미지를 Pull하기 때문에 가급적 경량인 상태로 만들어야 한다.
-
바로 실행 가능한 방법으로 def/yum/apt로 패키지를 설치한 후 저장소 패키지 목록 등의 캐시 파일을 삭제하는 것을 생각해 볼 수 있다.
-
또 멀티 스테이지 빌드를 활용하면 이미지에 필요한 파일만 추가할 수 있고, 기본 이미지가 경량인 배포판(Alpine Linux, Distroless 등) 이미지를 사용하는 것도 하나의 방법이다.
(4) 실행 계정은 root이외의 사용자로 한다.
- root 사용자를 사용하면 보안 사고로 이어질 수 있으므로 최대한 사용하지 않도록 한다.
🔔 멀티 스테이지 빌드
여러 컨테이너 이미지를 사용하여 처리하고 결과물만 실행용 컨테이너 이미지에 복사하는 구조
-
멀티 스테이비 빌드는 Dockferfile에서 “AS 키워드”를 사용해서 각 스테이지에 이름을 붙일 수 있고, “COPY –from=스테이지명” 명령어를 통해 다른 스테이지에서 파일을 복사할 수 있다.
-
이렇게 하면 빌드 과정에서 필요한 것들은 빌드용 이미지에만 남기고 실행용 이미지에는 필요한 것들만 넣는 것으로 최종 이미지의 크기를 줄여 배포나 실행 시간을 단축하고 보안 문제를 줄일 수 있는 장점이 있다.
(1) 예시 1 - 멀티 스테이지 빌드 (X)
$ cat Dockerfile
# Alpine 3.7 버전 golang 1.10.1 이미지를 사용
FROM golang:1.10.1-alpine3.7
# 8080 포트 오픈
EXPOSE 8080
# 빌드할 머신에 있는 main.go 파일을 컨테이너에 복사
COPY ./main.go ./
# 컨테이너 내부에서 명령어 실행
RUN go build -o ./go-app ./main.go
# 실행 계정을 nobody로 변경
USER nobody
# 컨테이너가 기동할 때 실행할 명령어 정의
ENTRYPOINT ["./go-app"]
$ docker image build -t sample-image:0.1 .
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
sample-image 0.1 ecf04bf34dd2 8 seconds ago 377MB
alpine3 3.11 4666da2f166f 2 weeks ago 5.61MB
golang 1.14.1-alpine3.11 ecf04bf34dd2 8 seconds ago 377MB
(2) 예시 1 - 멀티 스테이지 빌드 (O)
$ cat Dockerfile-MultiStage
# Stage 1 컨테이너(애플리케이션 빌드)
FROM golang:1.10.1-alpine3.7 as builder
COPY ./main.go ./
RUN go build -o /go-app ./main.go
# Stage 2 컨테이너(빌드된 바이너리를 포함한 실행용 컨테이너 생성)
FROM alpine:3.7
EXPOSE 8080
# Stage 1에서 빌드된 결과물을 복사
COPY --from=builder /go-app .
USER nobody
ENTRYPOINT ["./go-app"]
$ docker image build -t sample-image:0.2 -f Dockerfile-MultiStage .
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
sample-image 0.2 e6ea3f7d6f4f 17 seconds ago 13.1MB
sample-image 0.1 ecf04bf34dd2 2 minutes ago 377MB
alpine3 3.11 4666da2f166f 2 weeks ago 5.61MB
golang 1.14.1-alpine3.11 ecf04bf34dd2 8 months ago 377MB
# alpine 이미지 대신 scratch 이미지를 사용하면 용량을 더 줄일 수 있다.
$ docker image build -t sample-image:0.3 -f Dockerfile-MultiStage .
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
sample-image 0.3 963429e4a862 12 seconds ago 7.41MB
sample-image 0.2 e6ea3f7d6f4f 8 minutes ago 13.1MB
sample-image 0.1 ecf04bf34dd2 About a minute ago 377MB
alpine3 3.11 4666da2f166f 2 weeks ago 5.61MB
golang 1.14.1-alpine3.11 ecf04bf34dd2 8 months ago 377MB
🔔 이미지 레이어 통합과 이미지 축소
다이브(Dive)는 도커 이미지를 조사하는 도구로, 각각의 레이어에서 어느 파일에 변경이 있어 어느 정도의 용량이 소비되고 있는지를 CUI에서 조사할 수 있다.
레이어마다 같은 파일의 변경이 많은 경우 “docker image build”시 “–squash”(스쿼시) 옵션을 사용하면 최종 파일 상태를 가진 한 개의 레이어에 하나로 합쳐지므로 컨테이너 이미지를 축소할 수 있다.
댓글 남기기