Docker build-cache와 build-kit 비교하기

이민석's avatar
Feb 24, 2025
Docker build-cache와 build-kit 비교하기

결론

  1. build-cache는 Dockerfile의 메세지의 체크섬으로 캐싱을 진행한다.

  2. build-cache는 독립된 실행환경 간 공유되지 않아서 CI/CD 환경에서는 스토리지를 공유하거나 build-kit을 사용해야 한다.

  3. build-cache과 build-kit은 기본값에서는 성능 차이가 발생하지 않는다.

  4. build-cache와 build-kit은 이미지 레이어의 변경이 일체 없더라도, 7.6배 정도의 성능 차이가 발생한다.

  5. build-cache와 build-kit 간 성능 차이는 이미지의 크기와는 무관하다.

  6. Multi-stage build 환경에서는 build-kit이 훨씬 빠르게 완료될 수 있으나,
    이 부분은 본문의 주제를 벗어나는 범위이기에 별도의 포스트에서 다루겠습니다.

Docker Build-Cache

Docker는 컨테이너 이미지를 계층(layer) 형태로 쌓아서 만듭니다.

Builder는 Dockerfile을 읽고 각 명령어를 계층화하고 스택으로 구성합니다.

https://medium.com/@kuldeepkumawat195/understanding-docker-layered-architecture-06695c80806f
Understanding Docker Layered Architecture

이때,
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에 저장됩니다.
따라서 기본적으로 성능 상 우열은 크게 존재하지 않습니다.

  1. 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
  2. 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배 느려지는 것을 볼 수 있습니다.

  1. 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

이후 동일한 테스트를 다시 반복했습니다.

  1. 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
  2. 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
  3. 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

References.

  1. https://malwareanalysis.tistory.com/236

  2. https://creboring.net/blog/how-docker-divide-image-layer/

  3. https://www.auroria.io/optimizing-docker-images/

  4. https://fe-developers.kakaoent.com/2022/220414-docker-cache/

  5. Optimize Docker Build with Buildkit

Share article

Unchaptered