도커/쿠버네티스를 활용한 컨테이너 개발 실전 입문 - 09. 가벼운 도커 이미지 만들기

백근영·2020년 1월 28일
0
post-thumbnail

01. 가벼운 도커 이미지가 왜 필요할까

이미지 크기 증가에 따라 나타나는 문제

크기가 큰 도커 이미지를 다루다 보면 다음 작업을 실행하는 데 걸리는 시간이 점점 길어지게 된다.

  • 이미지 빌드 시간(기반 이미지 다운로드 시간 포함)
  • 이미지를 도커 레지스트리에 등록하는 시간
  • 컨테이너를 실행할 호스트 혹은 노드에서 이미지를 다운로드하는 시간

위와 같은 작업들의 실행시간 증가는 다음과 같은 문제를 일으킬 수 있다.

  • 클러스터를 구성하는 노드의 디스크 용량 낭비
  • CI 소요시간 증가
  • 개발 중 시행착오 소요 시간 증가로 인한 생산성 저하
  • 오토 스케일링으로 컨테이너가 투입되는 소요 시간 증가(노드에 이미지가 없는 경우 새로 받아와야 하므로)

그러므로 도커 이미지를 운영상 현실적으로 얼마나 가볍게 만들 수 있을 지는 중요한 문제이다.

02. 기반 이미지를 가볍게

모든 도커 이미지에는 기반 이미지가 존재하는데, 이 기반 이미지를 가능한 가볍게 하면 우리가 만들 이미지도 그만큼 가벼워질 것이다.

가벼운 기반 이미지

scratch

scratch는 빈 도커 이미지이다. 모든 도커 이미지를 따라가 보면 결국 이 scratch 이미지에 이르게 된다. 이 이미지는 모든 도커 이미지의 조상 격이라고 할 수 있다.
빈 이미지이므로 크기가 거의 0에 가깝지만, 셸이 없으므로 디버깅하기가 불편하고 패키지 관리자가 없기 때문에 의존성 설치를 위해 많은 수고를 감내해야 한다.

BusyBox

busybox는 임베디드 시스템에서 많이 사용하는 리눅스 배포판으로, 크기가 매우 작다는 것이 특징이다. busybox는 리눅스 기반의 유틸리티를 갖추고 있음에도 이미지의 크기가 1MB남짓밖에 되지 않는다.
scratch와는 다르게 셸을 포함하고 있기 때문에 컨테이너 안에서 디버깅 작업이 가능하다. 따라서 데이터 볼륨 컨테이너 등으로 활용될 여지가 있다. 하지만 scratch와 마찬가지로
패키지 관리자가 없으므로 그에 따른 불편함이 있을 수 있다.

alpine linux

알파인 리눅스는 bubybox를 기반으로 만든 리눅스 배포판으로, 많은 도커 이미지의 기반 이미지로 쓰이는 대표적인 리눅스 이미지이다. 알파인 리눅스 이미지의 장점은
위 두 이미지와는 다르게 apk라는 패키지 관리자를 가지고 있다는 것이고, 그럼에도 불구하고 이미지의 크기가 4MB로 다른 리눅스 이미지에 비하면 압도적으로 가벼운 크기이다.
패키지 관리자를 갖고 있으므로 API 서버나 웹 애플리케이션, Nginx 등의 미들웨어까지 다양한 상황에서 활용할 수 있다.

apk 사용법

apk update

로컬에 캐싱된 apk 패키지 인덱스를 업데이트하는 명령이다. 패키지 검색 및 설치는 이 로컬에 캐싱된 인덱스에 든 정보를 이용한다.

현재 사용할 수 있는 패키지를 검색하는 명령이다.

apk add

패키지를 설치하는 명령이다. 설치하려는 패키지 이름과 함께 사용하면 된다.
--no-cache 옵션을 사용하면 /var/cache/apk 디렉터리에 저장된 apk 인덱스 대신 새로 받아온 인덱스 정보를 이용해 패키지를 설치한다. 또한 /var/cache/apk 디렉토리에
캐시를 저장하지 않으므로, 가벼운 도커 이미지를 만들기 위한 dockerfile에서 패키지를 설치할 때는 --no-cache 옵션을 자주 사용한다.

apk del

설치된 패키지를 제거하는 명령이다. apk add --virtual 명령과 함께 사용하면 불필요한 패키지를 한 번에 지울 수 있다.

알파인 리눅스 기반 도커 이미지 만들기

이 책에서 다룬 todoapi의 dockerfile을 알파인 리눅스 기반으로 작성하면 다음과 같다.

FROM alpine:3.7

WORKDIR /
ENV GOPATH /go

# 1. 빌드 시에만 필요한 라이브러리 및 도구 설치 (--virtual 옵션 붙여서)
RUN apk add --no-cache --virtual=build-deps go git gcc g++

# 2. 실행 시에 필요한 라이브러리 및 도구 설치
RUN apk add --no-cache ca-certificates

# 3. todoapi를 빌드해 실행 파일을 만듬
COPY . /go/src/github.com/gihyodcocker/todoapi
RUN go get github.com/go-sql-driver/mysql
RUN go get gopkg.in/gorp.v1
RUN cd /go/src/github.com/gihyodocker/todoapi && go build -o bin/todoapi cmd/main.go
RUN cd /go/src/github.com/gihyodocker/todoapi && cp bin/todoapi /usr/local/bin/

# 4. 빌드 시에만 필요한 라이브러리 및 도구 제거
RUN api del --no-cache build-deps

CMD ["todoapi"]

04. 멀티 스테이지 빌드

빌드 컨테이너와 실행 컨테이너의 분리

도커 이미지를 빌드하는 과정에서 애플리케이션 빌드와 배포 과정이 모두 같은 컨테이너에서 이루어지는 경우가 많았다. 이 방식은 빌드 과정에만 필요한 라이브러리와
애플리케이션 실행에 불필요한 빌드 부산물을 완전히 제거할 수 없다는 한계가 있었다.(09-02장에서 배운 apk del 등의 방법을 쓴다고 하더라도) 멀티 스테이지 빌드를 통한
빌드 컨테이너와 실행 컨테이너의 분리는 이와 같은 문제점을 해결해 준다.

예시)

FROM golang:1.9 AS build

WORKDIR /
COPY . /go/src/github.com/gihyodcocker/todoapi
RUN go get github.com/go-sql-driver/mysql
RUN go get gopkg.in/gorp.v1
RUN cd /go/src/github.com/gihyodocker/todoapi && go build -o bin/todoapi cmd/main.go

FROM alpine:3.7

COPY --from=build /go/src/github.com/gihyodocker/todoapi/bin/todoapi /usr/local/bin/

CMD ["todoapi"]

멀티 스테이지 빌드의 가장 큰 특징은 하나의 dockefile에 FROM 인스트럭션이 두 번 등장한다는 것이다. 위의 FROM에서는 AS 명령어로 빌드 컨테이너의 이름을 build로 지정해주었으므로,
실행 컨테이너에서 이 이름을 통해 빌드 컨테이너를 참조할 수 있다. 멀티 스테이지 빌드는 빌드 후에 실행 컨테이너만 남기고 빌드 컨테이너는 폐기시키기 때문에 디스크 용량을 효율적으로
관리할 수 있고 실행 컨테이너의 크기를 가볍게 유지할 수 있다.

profile
서울대학교 컴퓨터공학부 github.com/BaekGeunYoung

0개의 댓글