
결론
build-cache는 Dockerfile의 메세지의 체크섬으로 캐싱을 진행한다.
build-cache는 독립된 실행환경 간 공유되지 않아서 CI/CD 환경에서는 스토리지를 공유하거나 build-kit을 사용해야 한다.
build-cache과 build-kit은 기본값에서는 성능 차이가 발생하지 않는다.
build-cache와 build-kit은 이미지 레이어의 변경이 일체 없더라도, 7.6배 정도의 성능 차이가 발생한다.
build-cache와 build-kit 간 성능 차이는 이미지의 크기와는 무관하다.
Multi-stage build 환경에서는 build-kit이 훨씬 빠르게 완료될 수 있으나,
이 부분은 본문의 주제를 벗어나는 범위이기에 별도의 포스트에서 다루겠습니다.
Docker Build-Cache
Docker는 컨테이너 이미지를 계층(layer) 형태로 쌓아서 만듭니다.
Builder는 Dockerfile을 읽고 각 명령어를 계층화하고 스택으로 구성합니다.
이때,
Builder는 이미지 게층 빌드를 최소화하기 위해서 build-cache[1]를 사용합니다.
변경 유무를 파악하기 위해서는 명령어의 입력에 대한 체크섬(sha256sum)을 사용합니다.
FROM ubuntu
RUN apt update && apt install -y curl
즉,
초기에 설치된 apt update가 주기적으로 이루어지지 않기 때문에,
설치된 curl은 항상 최신 버전이 아니라는 점을 주의해야 합니다.
이런 build-cache는 로컬 파일시스템(RootFS)에 캐싱됩니다.
docker build -t sample-ubuntu .
docker inspect sample-ubuntu | jq -r '.[0].RootFS.Layers'
따라서,
매번 새로운 빌드머신을 사용하는 경우 build-cache가 작동하지 않습니다.
이때는 각 빌드머신들이 build-cache의 경로를 공유(캐싱)하도록 설정해야 합니다.
이 경우,
Build-Kit을 사용하면 원격 이미지 저장소의 캐시계층을 활용할 수 있습니다.
Docker build-kit
build-kit[2]을 사용하면 빌드 결과를 cache storage backend에 저장합니다.
여러 종류의
backend의 위치는 type=으로 정의할 수 있으며 export, import이
build-kit[2]을 사용하면 빌드 결과를 자체 내부 캐시에 저장할 수 있습니다.
이 결과를 외부 위치로 보내거나 불러오는 export, import도 지원됩니다.
아래 예시에서는 Docker Hub에 캐시를 보내거나 불러오는 방식을 보여줍니다.
docker buildx build --push -t unchaptered/sample-ubuntu \
--cache-to type=registry,ref=unchaptered/sample-ubuntu \
--cache-from type=registry,ref=unchaptered/sample-ubuntu .
만약 GitHub Actions을 사용한다면 아래와 같이 gha를 사용할 수 있습니다.
아래 코드는 docker/build-push-action@v2의 일부 예시입니다.
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: user/app:latest
cache-from: type=gha
cache-to: type=gha,mode=max
혹은 Circle CI나 AWS CodeBuild 등을 사용할 때에는 S3를 사용할 수 있습니다.
Docker build-cache vs build-kit 성능 비교
build-cache, build-kit의 기본 성능은 큰 차이가 없습니다.
다만 외부 backend를 사용하면 이미지 푸쉬가 없더라도 build-kit이 7.6배 느려집니다. 문제는
build-cache[1]와 build-kit[2]의 기본설정은 모두 local에 저장됩니다.
따라서 기본적으로 성능 상 우열은 크게 존재하지 않습니다.
build-cache[1]
docker build -t unchaptered/sample-ubuntu . # [+] Building 0.4s (6/6) FINISHED docker:desktop-linux docker images unchaptered/sample-ubuntu --format "{{.Size}}" # 239MB
build-kit[2]
docker buildx build -t unchaptered/sample-ubuntu . # docker buildx build -t unchaptered/sample-ubuntu . --cache-to type=local,dest=./cache/ \ --cache-from type=local,src=./cache/ . # [+] Building 0.5s (6/6) FINISHED docker images unchaptered/sample-ubuntu --format "{{.Size}}" # 239MB
하지만 외부 백앤드를 사용하면 네트워크 지연시간이 발생하여,
build-kit[2]이 build-cache[1]보다 약 8배 느려지는 것을 볼 수 있습니다.
build-kit[3] + Docker Hub
docker buildx build -t unchaptered/sample-ubuntu \ --cache-to type=registry,ref=unchaptered/sample-ubuntu \ --cache-from type=registry,ref=unchaptered/sample-ubuntu . # [+] Building 3.8s (9/9) FINISHED docker:desktop-linux docker images unchaptered/sample-ubuntu --format "{{.Size}}" # 239MB
Docker Builder는 결국 체크섬 값을 기준으로 하기 때문에,
컨테이너 이미지의 크기가 build-kit[2]과 build-cache[1]의 차이를
크게 만들지는 않을 것이라고 판단해서 1.41GB 가량의 이미지를 만들었습니다.
FROM ubuntu:latest
# 패키지 목록 업데이트 및 필수 패키지 설치
RUN apt update && apt install -y \
build-essential \ # C/C++ 개발 도구 (100MB+)
curl \ # 네트워크 요청 도구
git \ # Git (약 50MB)
python3 \ # Python 3 (약 100MB)
python3-pip # Python 패키지 관리자 (약 50MB)
# 500MB짜리 빈 파일 추가 (이미지 크기 조정용)
RUN dd if=/dev/zero of=/bigfile bs=1M count=500
# 작업 디렉터리 설정
WORKDIR /app
이후 동일한 테스트를 다시 반복했습니다.
build-cache[1]
docker build -t unchaptered/sample-ubuntu-2 . # [+] Building 0.4s (6/6) FINISHED docker:desktop-linux docker images unchaptered/sample-ubuntu --format "{{.Size}}" # 884MB
build-kit[2]
docker buildx build -t unchaptered/sample-ubuntu . # docker buildx build -t unchaptered/sample-ubuntu . --cache-to type=local,dest=./cache/ \ --cache-from type=local,src=./cache/ . # [+] Building 0.5s (6/6) FINISHED docker images unchaptered/sample-ubuntu --format "{{.Size}}" # 884MB
build-kit[2] + Docker Hub
docker buildx build -t unchaptered/sample-ubuntu-2 \ --cache-to type=registry,ref=unchaptered/sample-ubuntu-2 \ --cache-from type=registry,ref=unchaptered/sample-ubuntu-2 . # [+] Building 3.8s (9/9) FINISHED docker:desktop-linux docker images unchaptered/sample-ubuntu-2 --format "{{.Size}}" # 1.41GB