BuildKit cache mount, 이거 모르는 분 꽤 많더라
오늘 PR 리뷰하다가 Dockerfile에서 발견한 게 있어서 짧게 적어둔다. CI 빌드 시간 길다고 투덜대는 동료가 있는데, 정작 Dockerfile에는 RUN apt-get install -y ... 한 줄만 덩그러니 있더라. cache mount를 안 쓰고 있었다.
이게 좀 의외였다. Docker 18.09에서 BuildKit 들어온 게 한참 됐고, cache mount도 새 기능이 아닌데, 의외로 실무에서 안 쓰는 사람이 많다. 한번 적용하면 빌드 시간이 절반 이하로 떨어지는 걸 자주 본다.
그래서 뭐하는 건데
RUN --mount=type=cache,target=... 한 줄을 RUN 명령에 붙이면, BuildKit이 그 디렉토리를 빌드 간에 영속화해준다. 캐시 레이어가 깨져서 RUN을 다시 돌려도, target 디렉토리 내용은 살아남는다. apt 캐시, pip wheel 캐시, npm/pnpm store, Go 모듈 캐시 — 매 빌드마다 다시 다운받던 것들을 다 재활용할 수 있다.
핵심은 --cache-from이랑 다른 레벨이라는 거다. --cache-from은 레이어 단위 캐시인데, 의존성 파일 한 줄만 바뀌어도 그 RUN 레이어는 통째로 무효화된다. cache mount는 레이어 안쪽에서 동작해서, 레이어가 깨져도 다운받은 패키지는 그대로다. 한 번 깔린 의존성을 재사용한다는 점에서 두 캐시는 보완 관계다. 사실 내가 처음 이 둘을 헷갈려서 "왜 둘 다 쓰지?"라고 물었던 적이 있다.
적용 예시
apt 기준:
# syntax=docker/dockerfile:1.7
FROM ubuntu:24.04
RUN rm -f /etc/apt/apt.conf.d/docker-clean && \
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt/lists,sharing=locked \
apt-get update && apt-get install -y curl jq
sharing=locked가 중요하다. 기본값은 shared인데, apt는 동시 접근에 취약해서 병렬 빌드 시 패키지 DB가 깨질 수 있다. 작년에 한 번 이걸로 멘탈 나간 적 있다. CI에서 매트릭스 빌드 돌리는데 가끔 dpkg 락 에러가 떠서 한참 헤맸다. apt, yum, dnf, pacman 같은 시스템 패키지 매니저는 무조건 locked로 잡아라.
Go 빌드는 더 단순하다:
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
go build -o /app ./cmd/server
이 두 줄로 모듈 다운로드, 컴파일 캐시 둘 다 보존된다. 의존성 안 바뀐 빌드는 거의 즉시 끝난다.
CI에서 한 가지 주의할 점
cache mount는 빌드 머신에 로컬로 저장된다. GitHub Actions, GitLab runner 같은 ephemeral 환경은 잡 끝나면 캐시도 같이 날아간다. 이 경우 docker buildx build --cache-to=type=registry,ref=...,mode=max --cache-from=type=registry,ref=... 식으로 레지스트리에 캐시를 export하면 cache mount 내용까지 같이 보존된다. mode=max가 핵심이다. 기본값(min)은 cache mount 메타데이터를 export하지 않는다. 이거 모르고 며칠을 "왜 캐시가 안 먹지" 하고 삽질했다.
self-hosted runner라면 그냥 BuildKit daemon을 띄워서 쓰면 된다. 캐시 디렉토리가 디스크에 그대로 남으니 별다른 export 설정도 필요 없다.
cache mount 한 줄로 빌드 30분이 3분 되는 걸 보면 좀 허무하기까지 하다. 혹시 아직 안 쓰고 있다면 오늘 PR로 하나 올려보시라.