Docker Build Cache Design

Docker image를 반복 build할 때, source의 작은 변경 때문에 dependency installation 등 비용 큰 이전 단계까지 매번 다시 실행되는 경우가 있다. 이 pattern의 핵심은 cache를 무조건 오래 쓰는 것이 아니라, 변경된 입력 뒤의 layer만 다시 build하게 Dockerfile과 build context를 설계하는 것이다.

Cache가 깨지는 기준

설계 원칙

  1. Dockerfile instruction을 덜 자주 바뀌는 입력부터 배치한다. base image와 dependency manifest를 먼저 처리하고, 자주 바뀌는 application source는 뒤에 COPY한다.[3]
  2. .dockerignore로 build에 필요 없는 file과 directory를 build context에서 제외한다.[3:1] 이렇게 하면 context 전송량과 불필요한 cache invalidation이 줄어든다는 효과는 원문에 명시되지 않은 이 page의 해석이다.
  3. COPY/ADD와 bind mount의 입력 파일 metadata 변화가 cache key에 영향을 준다는 점을 고려해,[2:1] dependency manifest와 source tree를 같은 layer에 무분별하게 넣지 않는다. 공식 예시는 COPY를 package management file과 project source code 두 단계로 나눈다.[3:2]
  4. package manager처럼 반복 다운로드가 큰 작업에는 cache mount를, CI처럼 runner가 매번 바뀌는 환경에는 external cache를 검토한다.[3:3]

이 pattern의 설계 기준은 stable dependency boundary와 volatile application boundary를 Dockerfile에서 분리하는 것이라고 볼 수 있다. 예를 들어 dependency manifest가 바뀌지 않았다면 source code만 바꿔도 dependency install layer를 재사용할 수 있다.

절충점

CI cache는 correctness를 보장하는 source of truth가 아니라 build cost를 줄이는 optimization이라는 관점에서 다뤄야 한다고 보인다. cache miss가 늘었다면 layer order와 context를, stale dependency가 의심되면 base image refresh와 --no-cache 같은 explicit invalidation을 별도 점검한다. 특정 language package manager의 Dockerfile recipe는 이 page의 범위 밖이며 별도 source가 필요하다.

흔히 놓치는 지점

관련

출처

테스트 질문


  1. docker-build-cache-invalidation.md — "The builder begins by checking if the base image is already cached. Each subsequent instruction is compared against the cached layers. If no cached layer matches the instruction exactly, the cache is invalidated." ↩︎

  2. docker-build-cache-invalidation.md — "For the ADD and COPY instructions, and for RUN instructions with bind mounts... the builder calculates a cache checksum from file metadata... Aside from the ADD and COPY commands, cache checking doesn't look at the files in the container... just the command string itself is used to find a match." ↩︎ ↩︎

  3. docker-build-cache-optimization.md — "Order your layers"(L5-34): "try to make expensive steps appear near the beginning of the Dockerfile. Steps that change often should appear near the end of the Dockerfile", "First, copy over the package management files ... Then, install the dependencies. Finally, copy over the project source code, which is subject to frequent change."; "Keep the context small"(L36-47): "create a .dockerignore file in the root of your build context ... lets you exclude files and directories from the build context."; "Use cache mounts"(L103-160): "even if you need to rebuild a layer, you only download new or changed packages"; "Use an external cache"(L162-210): "External caches are especially useful for CI/CD pipelines, where the builders are often ephemeral". ↩︎ ↩︎ ↩︎ ↩︎