docker build를 돌릴 때마다 npm install이나 apt-get이 처음부터 다시 받아진다면 BuildKit 캐시를 제대로 못 쓰고 있는 것입니다. 한 줄만 바뀌어도 그 아래 모든 레이어가 무효화되기 때문입니다.
=> [4/6] RUN npm ci 142.3s
매 빌드 2분이 의존성 설치에만 쓰인다면, BuildKit의 캐시 마운트와 레이어 순서로 대부분 없앨 수 있습니다.
BuildKit이 켜져 있는지 확인
Docker 23+ 부터는 기본이지만, 출력에 => [internal]·=> 화살표가 보이면 BuildKit입니다. 아니라면 활성화합니다.
# 환경변수로 강제
DOCKER_BUILDKIT=1 docker build -t myapp .
# 또는 buildx 사용
docker buildx build -t myapp .
1. 캐시 마운트 — 패키지 캐시를 레이어 밖에 유지
RUN --mount=type=cache는 설치 캐시를 이미지 레이어가 아닌 별도 캐시에 보관합니다. 소스가 바뀌어도 다운로드 캐시는 살아남습니다.
# syntax=docker/dockerfile:1
FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci
COPY . .
RUN npm run build
apt도 동일하게 적용합니다.
RUN --mount=type=cache,target=/var/cache/apt \
apt-get update && apt-get install -y --no-install-recommends curl
첫 줄의 # syntax=docker/dockerfile:1 주석이 있어야 캐시 마운트 문법이 동작합니다.
2. 레이어 순서 — 자주 안 바뀌는 걸 위로
의존성 정의(package*.json)를 먼저 COPY하고 설치한 뒤, 소스를 나중에 COPY합니다. 소스만 바뀌면 설치 레이어 캐시가 그대로 재사용됩니다. 반대로 COPY . .를 맨 위에 두면 파일 하나만 바뀌어도 캐시가 전부 깨집니다.
3. CI에서 캐시 내보내기·불러오기
CI는 매번 새 러너라 로컬 레이어 캐시가 없습니다. 캐시를 레지스트리나 파일로 빼서 다음 빌드에 주입합니다.
docker buildx build \
--cache-to type=registry,ref=myrepo/app:cache,mode=max \
--cache-from type=registry,ref=myrepo/app:cache \
-t myrepo/app:latest --push .
mode=max는 중간 레이어까지 모두 캐시해 적중률을 높입니다.
효과 확인
# 빌드 시간·단계별 캐시 적중 확인
docker buildx build --progress=plain -t myapp .
CACHED 표시가 뜬 단계는 재실행되지 않은 것입니다. npm ci 단계에 CACHED가 보이면 성공입니다.
체크리스트
docker version | grep -i buildkit # BuildKit 사용 여부
head -1 Dockerfile # syntax 주석 존재
docker buildx build --progress=plain . # CACHED 단계 확인
캐시 마운트와 레이어 순서를 직접 바꿔보며 빌드 시간 차이를 측정하는 실습은 도커 트랙에서 회원가입 없이 무료로 해볼 수 있습니다.