공부/Kubernetes

docker image 경량화 build 하기

토고미 2021. 9. 10. 18:55

지금도 도커/쿠버네티스의 초보지만 더 초보였던 시절,

이미지도 그저 생각없이 아래 비스무리하게 했다.

COPY . .

RUN go build

그러다보니 이미지의 크기가 정말 상상도 못하게 컸다.

이미지 크기 줄이기 전

1.19 기가가 말이 되는가?

이유는 쓸데없는 바이너리나 파일들이 이미지에 포함되어있기 때문이다.

어떻게하면 실행에만 필요한 파일들을 포함시킬 수 있을까?

 

operator-sdk에서 기본적으로 만들어주는 dockerfile에서 그 해답을 찾을 수 있었다.

핵심은 빌드와 실행을 분리하는 것이다

전체 dockerfile은 아래와 같다.

# 1. 빌드전용 이미지
FROM golang:1.15 as builder

WORKDIR /go/src

# 2. 필요한 패키지 다운로드
COPY go.mod go.mod
COPY go.sum go.sum
RUN go mod download

# 3. 소스(코드) 복사
COPY . .

# 4. 빌드
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -ldflags '-s' -o main .

# 5. 실행만 할 경량 베이스 이미지 사용
FROM gcr.io/distroless/static:nonroot
WORKDIR /go/src
COPY --from=builder /go/src .

# 6. 이미지 실행 방법
USER root
ENTRYPOINT ["/go/src/main"]

1~4에서는 빌드를 진행하고,

5~6에서는 실행을 위한 이미지를 만든다.

 

1. 빌드하는데 사용할 이미지를 베이스 이미지로 고른다.

위의 예시에서는 golang으로 만들어진 소스를 빌드하므로 golang 이미지를 선택했다.

 

2. 필요한 패키지를 다운로드한다.

3. 소스코드를 복사한다.

대게 import하는 패키지 종류의 변화보다는 소스코드 자체의 변화가 많다.

따라서, 소스코드는 나중에 복사하고 필요한 패키지는 미리 다운받도록 하면

도커 이미지 빌드의 캐시를 이용해 더욱 빨리 이미지를 만들 수 있다.

 

4. 빌드를 한다.

CGO_ENABLED=0 .... -a -ldflags 는 CGO를 사용하지 않고, 링커에게 옵션을 주어서 바이너리를 더 경량화하는 옵션이다.

자세한 내용은 구글링!!

 

5. 실제 실행할 컨테이너 베이스 이미지를 사용한다.

중요한 단계다. 빌드 열심히 해놓고 용량 큰 베이스 이미지를 사용하면 말짱도루묵이다.

그렇다면 용량이 가벼운 베이스 이미지는 무엇을 써야하나?

대표적으로 2가지가 있다.

 

  a. xxx-alpine 이미지

     : xxx 이미지를 경량화 시킨 이미지다. alpine 버전이 없는 이미지가 있을 수도 있다. golang같은 경우에는 golang-alpine이 있다.

  b. distrolesss 이미지

     : 오직 내가 실행할 어플리케이션과 런타임 dependency만 포함한 이미지다. 그 외에는 없다. 심지어 shell도 없어서 컨테이너에 직접 접근하는 것도 불가능하다!

 

그럼 2가지 중에 무엇을 써야할까? 그건 본인 선택에 달렸다. distroless 같은 경우에는 shell이 없으므로 컨테이너에 직접 접근이 불가능하다. 따라서 런타임에 현재 파일시스템이 어떤지 확인하기가 어렵다.

반면, golang-alpine이미지 같은 경우에는 bash는 없지만 sh은 있다. 따라서 확인이 가능하다.

하지만 모든 alpine이미지가 shell이 있는 게 아니다. 경량화를 모토로 한 이미지라 없을 가능성이 높다.

 

6. 어떻게 컨테이너를 실행할지 기술한다.

ENTRYPOINT는 컨테이너가 떴을 때, 어떻게 실행할지를 정한다. 일반적으로는 빌드한 바이너리 파일을 실행할테니 /main 에서 크게 벗어나지 않을 것이다.

위의 예시같은 경우에는 WORKDIR가 /go/src라서 /go/src/main을 전부 적어준 것이다.

 

이렇게 빌드를하면!!

베이스 이미지에 따른 크기 차이

이미지 크기가 기적적으로 줄어든다.

실제 실행하는 이미지에 쓸데없는 파일들을 전부 제거했기 때문이다.

 

위의 이미지는 golang-alpine을 베이스 이미지로한 이미지이고,

아래 이미지는 distroless를 베이스 이미지로한 이미지다.

golang-alpine은 꽤 무거운 이미지인가 보다.

 

혹시 예전의 나처럼 이미지를 만들었다면 지금 당장 고치자!

'공부 > Kubernetes' 카테고리의 다른 글

첫 PR 날려봤다  (0) 2021.09.16
Kubernetes Pod-to-Service 통신 매커니즘  (0) 2021.09.15
Kubernetes operator 메커니즘  (5) 2021.08.31
Kubernetes Audit (감사로그)  (3) 2021.08.09
nginx-ingress의 SSL/TLS/HTTPS 설정  (0) 2021.07.12