Docker-in-Docker 빌드에서 Docker 레이어 캐시 활용하기

Docker-in-Docker 빌드에서 Docker 레이어 캐시 활용하기 (Cache Docker layers in Docker-in-Docker builds)

Docker-in-Docker를 사용하면 매 빌드마다 Docker가 이미지의 모든 레이어를 내려받아요. 다행히 Docker 1.13 이상은 docker build 단계에서 기존 이미지를 캐시로 쓸 수 있어서, 빌드 과정을 꽤 많이 빠르게 만들어 줍니다.

출처: 문서

본문

Docker가 docker build를 실행하면 Dockerfile의 각 명령이 하나의 레이어를 만들어요. Docker는 이 레이어들을 캐시로 보관했다가 변경 사항이 없으면 재사용합니다. 한 레이어가 바뀌면 그 뒤의 모든 레이어는 다시 빌드돼요. docker build의 캐시 소스로 태그된 이미지를 쓰려면 --cache-from 인자를 넘기면 되고, 캐시 소스를 여러 개 지정하려면 --cache-from을 여러 번 쓰면 됩니다.

사전 요구사항 (Prerequisites)

Docker 27.0.1 이상에서는 containerd 이미지 스토어가 활성화되어 있을 때만 기본 docker 빌드 드라이버가 캐시 백엔드를 지원해요. 다음 중 하나를 선택하세요.

  • Docker 데몬 구성에서 containerd 이미지 스토어를 활성화.
  • 다른 빌드 드라이버를 선택.

인라인 캐싱 사용하기 (Use inline caching)

기본 docker build 명령과 함께 inline 캐시 백엔드를 쓰는 방법이 캐싱을 시작하기에 가장 간단해요. 캐시가 이미지 자체에 저장되어 별도의 캐시 이미지가 필요 없거든요. 다만 복잡한 빌드 흐름이나 멀티 스테이지 빌드에서는 레지스트리 캐싱을 쓰는 게 낫습니다. 자세한 내용은 인라인 캐싱 옵션을 참고하세요.

[!NOTE] --build-arg BUILDKIT_INLINE_CACHE=1 인자는 필수예요. 이 인자는 Docker가 이미지에 캐시 메타데이터를 넣도록 지시하고, 이후 빌드에서 --cache-from으로 그걸 캐시 소스로 쓰게 해줍니다. 이 인자가 없으면 캐싱이 조용히 실패해요.

파이프라인에서 인라인 캐싱을 쓰려면 다음 .gitlab-ci.yml 구성을 프로젝트에 추가합니다.

default:
  image: docker:27.4.1-cli
  services:
    - docker:27.4.1-dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY

variables:
  # Use TLS https://docs.gitlab.com/ci/docker/using_docker_build/#tls-enabled
  DOCKER_HOST: tcp://docker:2376
  DOCKER_TLS_CERTDIR: "/certs"

build:
  stage: build
  script:
    - docker pull $CI_REGISTRY_IMAGE:latest || true
    - docker build --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $CI_REGISTRY_IMAGE:latest
      --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --tag $CI_REGISTRY_IMAGE:latest .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker push $CI_REGISTRY_IMAGE:latest

build job의 script를 하나씩 보면 이렇습니다.

  • 첫 번째 명령은 캐시 소스로 쓸 이미지를 레지스트리에서 내려받아요. --cache-from으로 쓰는 이미지는 반드시 먼저 docker pull로 받아야 합니다.
  • 두 번째 명령은 내려받은 이미지를 캐시로 사용해서(--cache-from $CI_REGISTRY_IMAGE:latest) Docker 이미지를 빌드하고 태깅합니다. --build-arg BUILDKIT_INLINE_CACHE=1 플래그가 빌드 캐시를 이미지에 넣어줘요.
  • 마지막 두 명령은 두 태그의 이미지를 컨테이너 레지스트리에 푸시해서 이후 빌드에서 캐시로 쓸 수 있게 합니다.

레지스트리 캐싱 사용하기 (Use registry caching)

docker buildx build와 함께 registry 캐시 백엔드를 쓰면 빌드 캐시를 애플리케이션 이미지와 분리된 전용 캐시 이미지에 저장할 수 있어요. 이 방식은 멀티 스테이지 빌드나 복잡한 빌드 흐름에서 인라인 캐싱보다 확장성이 좋습니다. 자세한 내용은 캐시 백엔드 옵션을 참고하세요.

파이프라인에서 레지스트리 캐싱을 쓰려면 다음 .gitlab-ci.yml 구성을 프로젝트에 추가합니다.

default:
  image: docker:27.4.1-cli
  services:
    - docker:27.4.1-dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY

variables:
  # Use TLS https://docs.gitlab.com/ci/docker/using_docker_build/#tls-enabled
  DOCKER_HOST: tcp://docker:2376
  DOCKER_TLS_CERTDIR: "/certs"

build:
  stage: build
  script:
    - docker context create my-builder
    - docker buildx create my-builder --driver docker-container --use
    - docker buildx build --push -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
      --cache-to type=registry,ref=$CI_REGISTRY_IMAGE/cache-image,mode=max
      --cache-from type=registry,ref=$CI_REGISTRY_IMAGE/cache-image .

build job의 script를 보면 이렇습니다.

  • 처음 두 명령은 registry 캐시 백엔드를 지원하는 docker-container BuildKit 드라이버를 만들고 구성해요.
  • 세 번째 명령은 Docker 이미지를 빌드하고 푸시합니다. --cache-from으로 전용 캐시 이미지를 읽고, --cache-to로 그 캐시 이미지를 갱신해요. max 모드는 모든 중간 레이어를 캐시합니다.

더 알아보기

캐시 전략은 빌드 복잡도에 따라 고르면 돼요. 단순한 빌드는 인라인 캐싱이 손쉽고, 멀티 스테이지나 복잡한 흐름이라면 레지스트리 캐싱이 확장성 면에서 유리합니다. 어떤 방식을 쓰든 --cache-from에 넘길 이미지를 먼저 pull 해둬야 한다는 점, 그리고 BUILDKIT_INLINE_CACHE=1 같은 필수 인자를 빼먹지 않는 점을 꼭 기억해 두세요.