Docker BuildKit 캐시 최적화

BuildKit이란?

Docker BuildKit은 Docker 18.09에서 도입된 차세대 빌드 엔진으로, 기존 빌드 엔진 대비 병렬 빌드, 캐시 마운트, 시크릿 마운트 등 강력한 최적화 기능을 제공합니다. Docker 23.0부터 기본 빌드 엔진으로 채택되었으며, CI/CD 파이프라인에서 빌드 시간을 50~80% 단축할 수 있는 핵심 기술입니다.

이 글에서는 BuildKit의 캐시 전략, 시크릿 관리, 병렬 빌드 패턴, 그리고 CI 환경에서의 원격 캐시 활용까지 깊이 있게 다루겠습니다.

BuildKit 활성화

# 환경 변수로 활성화 (Docker 23.0 미만)
export DOCKER_BUILDKIT=1
docker build -t myapp .

# Docker 23.0+에서는 기본 활성화
# docker buildx를 사용하면 BuildKit 자동 사용
docker buildx build -t myapp .

# daemon.json으로 영구 설정
{
  "features": {
    "buildkit": true
  }
}

RUN –mount=type=cache: 게임 체인저

--mount=type=cache는 BuildKit의 가장 강력한 기능입니다. 패키지 매니저의 캐시 디렉토리를 빌드 간에 유지하여 의존성 재다운로드를 완전히 제거합니다.

# syntax=docker/dockerfile:1

# ❌ 기존 방식: 레이어 캐시 미스 시 전체 재설치
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci              # 캐시 미스 → 전체 다운로드 (~60초)
COPY . .
RUN npm run build

# ✅ BuildKit 캐시 마운트: 패키지 캐시 영구 유지
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm 
    npm ci              # npm 캐시 재사용 (~5초)
COPY . .
RUN --mount=type=cache,target=/app/.next/cache 
    npm run build       # Next.js 빌드 캐시도 유지

언어별 캐시 마운트 설정

언어/도구 캐시 경로 마운트 설정
npm /root/.npm --mount=type=cache,target=/root/.npm
pnpm /root/.local/share/pnpm/store --mount=type=cache,target=/root/.local/share/pnpm/store
Maven /root/.m2/repository --mount=type=cache,target=/root/.m2/repository
Gradle /root/.gradle/caches --mount=type=cache,target=/root/.gradle/caches
pip /root/.cache/pip --mount=type=cache,target=/root/.cache/pip
Go /go/pkg/mod --mount=type=cache,target=/go/pkg/mod

실전 Dockerfile: Spring Boot + Gradle

# syntax=docker/dockerfile:1

# 1단계: 의존성 해석 (캐시 최대 활용)
FROM eclipse-temurin:21-jdk-alpine AS deps
WORKDIR /app
COPY build.gradle.kts settings.gradle.kts gradle.properties ./
COPY gradle ./gradle

RUN --mount=type=cache,target=/root/.gradle/caches 
    --mount=type=cache,target=/root/.gradle/wrapper 
    ./gradlew dependencies --no-daemon

# 2단계: 빌드 (소스 변경 시에만 재실행)
FROM deps AS build
COPY src ./src

RUN --mount=type=cache,target=/root/.gradle/caches 
    --mount=type=cache,target=/root/.gradle/wrapper 
    --mount=type=cache,target=/app/build 
    ./gradlew bootJar --no-daemon -x test

# 3단계: 런타임 (최소 이미지)
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=build /app/build/libs/*.jar app.jar

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Secret 마운트: 빌드 시 민감 정보

# 프라이빗 레지스트리 접근, API 키 등을
# 이미지 레이어에 남기지 않고 안전하게 사용

# Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./

# .npmrc에 프라이빗 레지스트리 토큰이 필요한 경우
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc 
    npm ci

COPY . .
RUN npm run build

# 빌드 명령
docker buildx build 
  --secret id=npmrc,src=$HOME/.npmrc 
  -t myapp .

# SSH 키가 필요한 경우 (프라이빗 Git 의존성)
RUN --mount=type=ssh 
    git clone git@github.com:company/private-lib.git

docker buildx build 
  --ssh default=$SSH_AUTH_SOCK 
  -t myapp .

병렬 빌드: Multi-Stage 최적화

BuildKit은 Multi-Stage Build에서 독립적인 스테이지를 자동으로 병렬 실행합니다.

# syntax=docker/dockerfile:1

# 이 3개 스테이지는 서로 의존하지 않으므로 병렬 실행!
FROM node:20-alpine AS frontend
WORKDIR /app/frontend
COPY frontend/package*.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY frontend/ .
RUN npm run build

FROM eclipse-temurin:21-jdk-alpine AS backend
WORKDIR /app
COPY build.gradle.kts settings.gradle.kts ./
RUN --mount=type=cache,target=/root/.gradle/caches 
    ./gradlew dependencies --no-daemon
COPY src ./src
RUN --mount=type=cache,target=/root/.gradle/caches 
    ./gradlew bootJar --no-daemon

FROM nginx:alpine AS docs
COPY docs/ /tmp/docs/
RUN apk add --no-cache hugo && hugo -s /tmp/docs -d /out

# 최종 이미지: 3개 스테이지 결과 합침
FROM eclipse-temurin:21-jre-alpine
COPY --from=backend /app/build/libs/*.jar app.jar
COPY --from=frontend /app/frontend/dist /static
COPY --from=docs /out /docs
ENTRYPOINT ["java", "-jar", "app.jar"]

# 빌드 시간: 순차 ~180초 → 병렬 ~80초

원격 캐시: CI/CD 파이프라인

# GitHub Actions에서 레지스트리 캐시 활용
# 로컬 캐시는 CI에서 사라지지만, 원격 캐시는 영구 유지

# 1. 레지스트리 캐시 (가장 범용적)
docker buildx build 
  --cache-from type=registry,ref=ghcr.io/myorg/myapp:cache 
  --cache-to type=registry,ref=ghcr.io/myorg/myapp:cache,mode=max 
  -t ghcr.io/myorg/myapp:latest 
  --push .

# 2. GitHub Actions 캐시
docker buildx build 
  --cache-from type=gha 
  --cache-to type=gha,mode=max 
  -t myapp .

# 3. S3 캐시 (대용량)
docker buildx build 
  --cache-from type=s3,region=ap-northeast-2,bucket=my-cache 
  --cache-to type=s3,region=ap-northeast-2,bucket=my-cache,mode=max 
  -t myapp .

# mode=max: 중간 레이어까지 모두 캐시 (권장)
# mode=min: 최종 레이어만 캐시 (기본)

GitHub Actions 실전 워크플로우

# .github/workflows/build.yml
name: Build and Push
on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - uses: docker/setup-buildx-action@v3
      
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      - uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max
          secrets: |
            npmrc=${{ secrets.NPMRC }}

캐시 디버깅

# 빌드 캐시 현황 확인
docker buildx du

# 캐시 정리
docker buildx prune --all

# 빌드 과정 상세 로그 (캐시 히트/미스 확인)
docker buildx build --progress=plain -t myapp .

# 특정 스테이지만 빌드 (디버깅용)
docker buildx build --target=deps -t myapp:deps .

운영 주의사항

항목 주의 대응
캐시 크기 cache mount 무한 증가 가능 정기적 docker buildx prune
캐시 무효화 COPY 순서가 캐시에 영향 변경 빈도 낮은 파일 먼저 COPY
보안 원격 캐시에 민감 레이어 포함 가능 secret mount 사용, mode=min 검토
CI 호환성 일부 CI에서 buildx 미지원 setup-buildx-action 추가

마무리

Docker BuildKit의 캐시 마운트와 원격 캐시는 CI/CD 파이프라인의 빌드 시간을 획기적으로 단축합니다. --mount=type=cache 하나만 추가해도 의존성 설치 시간을 90% 이상 줄일 수 있으며, 레지스트리 캐시와 조합하면 CI 환경에서도 로컬 빌드 수준의 속도를 달성할 수 있습니다. Dockerfile을 작성할 때 BuildKit 기능을 적극 활용하세요.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux