연재 시리즈

도커이미지레이어 3편 빌드캐시

악분 2021. 12. 25. 13:49
반응형


이 글은 영상으로도 만나볼 수 있습니다.
https://youtu.be/eQ7cETBMlWc

 

1. 빌드캐시란?

빌드캐시는 도커 빌드(docker build명령어 수행)과정에서 사용되는 개념입니다. 빌드캐시는 빌드 중에 동일한 이미지 레이어가 있으면 빌드하지 않고 재사용합니다. 빌드캐시는 ①빌드속도 향상과 중복된 이미지레이어 저장을 막음으로써도커 이미지레이어 저장공간을 최소화 하는 목적이 있습니다.

 

2. 빌드캐시 과정

처음 docker build명령어를 실행했다면 레이어 저장공간에 이미지 레이어가 없으므로 빌드를 수행합니다. 빌드를 수행하면 이미지 레이어가 생성되고 레이어 저장공간에 저장됩니다. 정확한 표현은 각 레이어가 docker commit을 수행하여 도커 이미지가 생성됩니다.


첫 번째 docker build이후에, 동일하게 docker build를 수행했다고 가정해봅시다. 레이어 저장공간에 이전에 생성한 동일 이미지 레이어가 있어서 빌드를 수행하지 않고 이미지 레이어를 재사용합니다. 빌드를 수행하지 않았으므로 빌드속도가 빨라집니다.

 

3. 빌드캐시 확인

아래처럼 Dockerfile을 작성합니다.

FROM busybox:1.34.1 RUN echo "helloworld"


그리고 docker build를 수행하면 빌드를 수행하고 이미지 레이어를 생성합니다.

docker build -t example:v1 ./


빌드를 성공한 후, 다시 docker build를 수행해보면 Using Cache라는 문자열이 보입니다. 기존이 동일한 이미지 레이어 정보가 있어서 빌드를 수행하지 않고 캐싱(재사용)했습니다.

 

4. 이미지 레이어 체인과 빌드캐시 연관관계

빌드 캐시는 이미지 레이어의 데이터(Dockerfile 명령어)뿐만 아니라 부모 이미지 레이어 정보도 영향이 있습니다. 공식문서(https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache)에서 설명처럼 이미지 레이어가 변경되면 하위 이미지 레이어는 데이터가 같을지라도 캐시를 사용하지 못합니다. 이미지 레이어는 데이터와 parent정보로 구성되어 있기 때문에 이미지 레이어 전체 데이터가 변경되기 때문입니다.

이미지 레이어 설명은 https://malwareanalysis.tistory.com/234에서 자세히 살펴볼수 있습니다.


예제를 살펴보겠습니다. 아래 Dockerfile은 간단히 echo 리눅스 명령어를 사용해서 문자열을 stdout으로 출력합니다. 각 이미지 레이어는 parent이미지 레이어를 가지고 있습니다. busybox(parent)<-second, first(parent)<-second, second(parent)<-third 순서로 이미지 레이어는 연결되어 있습니다.


2번째 Dockerfile명령어를 수정하면 2번째 이미지 레이어가 새로 생성됩니다. 그리고 3번째 레이어는 parent이미지 레이어가 수정되서 데이터가 같을지라도 이미지 레이어가 새로 생성됩니다. 4번째 레이어도 3번째 레이어가 수정되서 이미지 레이어가 생성됩니다. 결과적으로, 수정한 2번째 레이어부터 캐시를 사용하지 않고 이미지 레이어가 생성됩니다.

 

5. 적절한 빌드캐시 사용

이미지 레이어 수정이 빈번한 dockerfile명령어는 최대한 마지막에 쓰는 것이 중요합니다. 만약, 제일 위에 빈번한 수정이 있는 이미지 레이어가 있다면, 수정할 때마다 대부분의 이미지 레이어를 생성합니다. 그만큼 빌드속도가 느리고 이미지 레이어 저장공간을 차지합니다.

아래그림은 빌드가 10분걸리는 Dockerfile명령어를 마지막 줄로 변경했을 때 예입니다. 변경 전에는 2번째 줄에 위치해서, 2번째 레이어가 수정되면 2,3,4줄 모두 빌드를 수행합니다. 하지만, 마지막 줄로 옮긴다면 마지막 줄만 빌드를 수행하므로 빌드속도 증가와 디스크 공간을 적게 쓸수 있습니다.


예제 Dockerfile을 통해 Dockerfile위치에 따른 빌드속도를 직접 체감해봅시다. Dockerfile과 entrypoint.sh파일이 필요합니다. Dockerfile은 간단히 git을 설치하고 entrypoint.sh파일을 실행하는 예제입니다.

FROM alpine:3.15 

COPY ./entrypoint.sh /

RUN apk fix 
RUN apk --update add git git-lfs less openssh \ 
	&& git lfs install \ 
    && rm -rf /var/lib/apt/lists/* \ 
    && rm /var/cache/apk/* 
    
RUN chmod +x ./entrypoint.sh 
ENTRYPOINT ["/entrypoint.sh"]


entrypoint.sh파일은 echo명령어를 수행하는 간단한 스크립트입니다.

#!/bin/sh
echo "helloworld"


docker build를 실행하여 도커 이미지를 생성해봅시다.

docker build -t example:v2 ./


빌드가 성공하면 entrypoint.sh파일을 수정해봅시다. 간단히 echo명령어를 한번 더 실행한 것뿐이에요.

#!/bin/sh
echo "helloworld"
echo "helloworld2"


그리고 다시 docker build를 해봅시다. 결과가 어떻게 나오죠? entrypoint.sh파일을 수정만 했는데 다시 git을 설치하는 것을 볼 수 있습니다.

docker build -t example:v2 ./


파일만 몇바이트 크기를 수정했을 뿐인데 COPY이미지 레이어부터 끝까지 모든 이미지 레이어를 다시 생성합니다. 그래서 파일 수정부분만 이미지 레이어를 수정할 수 있도록 COPY명령어를 Dockerfile 마지막으로 위치를 옮겼습니다.

FROM alpine:3.15

RUN apk fix 
RUN apk --update add git git-lfs less openssh \ 
	&& git lfs install \ 
    && rm -rf /var/lib/apt/lists/* \ 
    && rm /var/cache/apk/* 
    
COPY ./entrypoint.sh / 
RUN chmod +x ./entrypoint.sh 

ENTRYPOINT ["/entrypoint.sh"]


docker build를 한번 실행합니다.

docker build -t example:v2 ./


그리고 entrypoint.sh파일을 수정해봅시다.

#!/bin/sh 

echo "helloworld" 
echo "helloworld2" 
echo "helloworld3"


다시 docker build를 수행해봅시다. 이전과 다르게 git을 설치하는 이미지 레이어는 캐시를 사용했습니다. 그리고 copy명령어와 RUN, ENTRYPOINT명령어만 새로 이미지 레이어를 생성했습니다. 또한, 빌드속도가 엄청 빨라진 것을 느낄 수 있습니다.

docker build -t example:v2 ./

 

참고자료

[1] dockerfile 공식문서: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

반응형