본문 바로가기

Kubernetes

[Docker] Dockerfile cache 및 작성 Tip

 

Kubernetes에서 보통 컨테이너 런타임으로 Docker를 사용한다.

그 말은 즉, 이미지를 빌드 하는 영역은 Docker를 통해 진행이 되는데 이 때 사용되는 Dockerfile 작성 Tip을 알아본다.

 

1. Cache에 관하여

 기본적으로 Docker를 사용하는 사람들이 Dockerfile 각 명령어 한 줄 마다 layer가 쌓이고 캐싱이 된다라는 것 까지만 알고 있고, Dockerfile  best practice 공식 Document에 보면 정확히 어떤 기준으로 캐싱을 사용하는지 아는 것이 중요하다고 나와있음에도 불구하고 이를 잘 모르는 경우가 많다. Dockerfile 명령어 마다 캐싱을 사용하는 기준이 다른데, 핵심은 ADD와 COPY 명령어를 제외한 모든 명령어는 Dockerfile에 기재된 명령어 string만 동일하면 캐싱을 사용하고, ADD와 COPY 명령어는 Dockerfile에 기재된 명령어 string + 해당 파일의 변경 유무까지 파악해서 캐싱을 사용한다.

그리고 일단 한 번 캐싱을 사용할 수 없는 구간이 발생하면, in order로 진행되는 Docker의 빌드 특성상 그 아래 모든 layer는 캐싱을 사용할 수 없다.

 

아래 1번과 같은 Dockerfile을 최초 빌드에 사용하고 소스 코드를 변경한뒤 같은 내용으로 다시 빌드한다고 가정해보자.

 

-- Dockerfile --

FROM golang:1.13 AS builder

WORKDIR /app

COPY . .

RUN go build -o dockertest

CMD ["dockerTest"]

 

$ docker build -t docker-test .

최초 빌드시에는 시간이 상대적으로 오래걸리고, Cached라는 단어를 볼 수 없을 것이다.

 

다시 똑같은 명령어를 반복한다.

$ docker build -t docker-test .

이번에는 빌드 시간이 훨씬 단축되고, WORKDIR, COPY, RUN 이렇게 3 단계에서 Cached라는 단어가 나온다.

 

다음은, Dockerfile이 위치한 폴더 내 .go 파일의 내용을 아무거나 하나 바꿔본 뒤, 다시 같은 명령어를 반복한다.

$ docker build -t docker-test .

WORKDIR은 Cached가 나오지만 COPY, RUN은 Cached가 나오지 않는다.

해당 프로젝트의 checksum 값이 변경되면서 COPY 명령어를 실행할 때 Cache가 아닌 새로 빌드하는 과정이 추가되며, 그 아래 모든 명령어는 cache를 사용할 수 없다. ( multi-stage 제외 )

 

checksum의 경우 해싱을 통해 데이터의 위변조를 체크하는 것인데, 원래는 파일 하나 당 하나의 checksum이 나온다.

파일의 checksum은 아주 쉽게 테스트 해볼 수 있다.

windows의 경우, powershell을 키고 GET-FileHash를 한 다음 파일 아무거나 하나를 드래그해서 powershell에 드랍하면 된다.

보통 SHA256 알고리즘을 통해 해시 값을 구한다.

이러한 캐싱 기능을 활용해서 디팬던시를 따로 빼서 일부러 캐싱 레이어를 추가하기도 하고, RUN 명령어의 경우 &&를 활용해서 불필요한 캐싱 레이어를 줄이기도 한다.

 

2. Multi-Staging Build

From으로 base 이미지를 두 번 이상 가져와서,  Multi-Staging build를 실행할 수 있다.

이 행위의 목적은 단 한가지, 최종 버전의 이미지를 작게 만들어 배포를 더욱 빠르고 유연하게 하기 위함이다.

위에 dockerfile로 빌드를 진행하면 811MB의 크기의 이미지가 생성되는데, 아래와 같이 Dockerfile을 변경하여 12MB 언더로 최종 이미지의 크기를 줄일 수 있다.

 

FROM golang:1.13 AS builder

WORKDIR /app2

COPY . .

RUN CGO_ENABLED=0 go build -o dockerTest

CMD ["./dockerTest"]

 

FROM alpine:latests

WORKDIR /app3

COPY --from=builder /app2 .

ENTRYPOINT /app3/dockerTest

 

 

3. build context와 dockerignore

docker cli는 docker daemon으로 빌드 진행할 때 프로젝트 내 파일들을 보내고, COPY나 ADD 명령어가 실행되면 daemon이 받은 그 파일을 컨테이너로 복사하는 것이다. 이 때 보내지는 파일들의 범위를 Build-Context라고 한다. Build와 관련 없는 내용들은 전부 빼두는 것이 좋다.

 

4. ETC

COPY 명령어는 COPY [복사할 파일 경로] [목적지]로 이루어지는데, 복사할 파일 경로의 경우 build context 아래를 기준으로 한다. (천천히 생각해보면 사실 당연한 것이다!) 

따라서 ../hello.txt나 /home/hello 등은 불가능하다.

CMD는 docker run 명령에서 인자가 들어오면 무시되는데, ENTRYPOINT는 무조건 실행되고, 이 명령어는 가장 마지막에 적힌 하나만 실행된다.

ENTRYPOINT와 CMD는 list 형태로 사용하는 것을 권장한다. 그 이유로 원래 컨테이너에서 실행시키는 프로그램이 당연히 pid 1로 실행이 되는데, 만약 sh 스크립트를 실행할 경우 sh 아래에 sub로 실행되게 되니까 pid1이 안되게 된다. 그럴 경우 docker stop 등 명령어가 안 먹힌다고 공식 document에 나와있다.

ARG는 dockerfile 내에서만 가능한 변수이고, ENV는 컨테이너 안에서 사용 가능한 환경 변수이다.