1. 명령어 순서 최적화 (캐시 활용)

변경 빈도가 낮은 명령어를 앞에, 높은 명령어를 뒤에 배치하세요. 특정 레이어의 캐시가 무효화되면 그 이후의 모든 레이���가 다시 빌드됩니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# ❌ 잘못된 예: 코드 수정 시마다 의존성을 다시 설치
FROM node:18-alpine
WORKDIR /app
COPY . .                          # 코드는 자주 변경됨
RUN npm ci --production           # 의존성은 거의 변경되지 않음
CMD ["node", "server.js"]

# ✅ 올바른 예: 의존성 변경 시에만 다시 설치
FROM node:18-alpine
WORKDIR /app
COPY package.json package-lock.json ./  # 의존성 정의 (거의 변경되지 않음)
RUN npm ci --production                  # 의존성 설치 (거의 변경되지 않음)
COPY . .                                 # 코드 (자주 변경됨)
CMD ["node", "server.js"]
1
2
# 캐시 적중 여부 확인
docker build -t myapp . 2>&1 | grep -E "(CACHED|Using cache)"

2. .dockerignore로 컨텍스트 축소

docker build는 .git, node_modules 등 불필요한 파일을 포함한 전체 디렉터리를 Docker daemon으로 전송합니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# .dockerignore
.git
.github
.vscode
.idea
node_modules
dist
build
*.md
*.log
.env
.env.local
.DS_Store
coverage
.nyc_output
1
2
3
4
# 컨텍스트 크기 확인
docker build --no-cache -t test . 2>&1 | grep "Sending build context"
# 최적화 전: Sending build context to Docker daemon  1.2GB
# 최적화 후: Sending build context to Docker daemon  50MB

3. 경량 베이스 이미지 사용

베이스 이미지 크기 설명
ubuntu:22.04 ~77MB 완전한 Ubuntu
python:3.11 ~1GB 완전한 Python 포함
python:3.11-slim ~130MB 경량 버전
python:3.11-alpine ~50MB Alpine 버전
1
2
3
4
5
6
7
8
# ❌ 너무 큼
FROM python:3.11          # 1GB

# ✅ 추천
FROM python:3.11-slim     # 130MB

# ✅ 최소 크기 (musl 호환성 문제 주의)
FROM python:3.11-alpine   # 50MB

Alpine은 glibc 대신 musl을 사용하므로 일부 소프트웨어가 호환되지 않을 수 있습니다. 문제가 발생하면 slim 버전을 선택하세요.

4. RUN 명령어 병합으로 레이어 수 감소

각 RUN은 새로운 레이어를 생성하며, 레이어가 많을수록 빌드가 느려지고 이미지가 커집니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# ❌ 5개 레이어
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y wget
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*

# ✅ 1개 레이어
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        curl \
        wget && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*
1
2
# 이미지 레이어 수 확인
docker history myapp:latest

5. 멀티스테이지 빌드 사용

한 스테이지에서 빌드하고 다른 스테이지에서 실행하여 필요한 파일만 복사합니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 스테이지 1: 빌드
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o app .

# 스테이지 2: 실행 (~15MB)
FROM alpine:3.18
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/app .
EXPOSE 8080
CMD ["./app"]
방식 이미지 크기 빌드 시간
싱글 스테이지 ~1.2GB 5분
멀티 스테이지 ~15MB 2분

6. BuildKit으로 빌드 가속화

BuildKit은 병렬 빌드와 고급 캐싱을 지원합니다.

1
2
3
4
5
6
7
# BuildKit 활성화
export DOCKER_BUILDKIT=1

# 또는 /etc/docker/daemon.json에 영구 설정
# {
#   "features": { "buildkit": true }
# }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 캐시 마운트 사용 (BuildKit 기능)
# syntax=docker/dockerfile:1
FROM node:18-alpine
WORKDIR /app
COPY package.json package-lock.json ./
# 캐시 마운트로 매번 의존성을 다시 다운로드하지 않음
RUN --mount=type=cache,target=/root/.npm \
    npm ci --production
COPY . .
CMD ["node", "server.js"]
1
2
# BuildKit 활성화 여부 확인
docker build --progress=plain -t myapp . 2>&1 | head -5

7. 캐시 가져오기/내보내기 사용

CI/CD 환경에서는 매 빌드 후 캐시가 손실됩니다. 캐시 가져오기/내보내기를 사용하면 빌드 간 캐시를 유지할 수 있습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 로컬로 내보내기
docker build \
  --cache-from type=local,src=/tmp/cache \
  --cache-to type=local,dest=/tmp/cache \
  -t myapp .

# 레지스트리를 캐시로 사용
docker build \
  --cache-from type=registry,ref=myregistry/myapp:cache \
  --cache-to type=registry,ref=myregistry/myapp:cache,mode=max \
  -t myapp .
1
2
3
4
5
6
7
8
9
# GitHub Actions 예제
- name: Build and push
  uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: myapp:latest
    cache-from: type=gha
    cache-to: type=gha,mode=max

8. 의존성 병렬 설치

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Node.js - npm ci가 npm install보다 빠름
RUN npm ci --production

# Python - 캐시 비활성화
RUN pip install --no-cache-dir -r requirements.txt

# Go - 모듈 병렬 다운로드
RUN go mod download

# Rust - cargo-chef 사용
COPY --from=chef /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json

9. 빌드 캐시 및 임시 파일 정리

동일한 RUN 명령어 내에서 정리하여 파일이 레이어에 남지 않도록 합니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# ✅ 올바른 예: 동일 레이어에서 정리
RUN apt-get update && \
    apt-get install -y --no-install-recommends build-essential && \
    make && make install && \
    apt-get purge -y --auto-remove build-essential && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# ❌ 잘못된 예: 정리가 다른 레이어에서 수행되어 파일이 여전히 존재함
RUN apt-get update && apt-get install -y build-essential
RUN make && make install
RUN apt-get clean  # 너무 늦었습니다! 이��� 레이어에 파일이 이미 포함됨

언어별 정리 명령어:

1
2
3
4
5
6
7
8
# Node.js
RUN npm ci --production && npm cache clean --force

# Python
RUN pip install --no-cache-dir -r requirements.txt

# Go
RUN go build -o app . && go clean -cache

10. 특정 버전 태그 사용

1
2
3
4
5
6
7
8
# ❌ latest는 변경될 수 있어 캐시를 예측할 수 없음
FROM node:latest

# ✅ 특정 버전
FROM node:18.17.0-alpine3.18

# ✅ 더 안전: SHA256 다이제스트 사용
FROM node:18.17.0-alpine3.18@sha256:abc123...

이미지 레이어 상세 설명

1
2
# 이미지 레이어 히스토리 확인
docker history <image-name>

새 레이어를 생성하는 명령어: FROM, RUN, COPY, ADD

레이어를 생성하지 않는 명령어 (메타데이터만): CMD, ENTRYPOINT, ENV, EXPOSE, WORKDIR, USER, LABEL, ARG, VOLUME

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 4개 레이어 생성
FROM node:18-alpine        # 레이어 1
RUN apk add --no-cache curl # 레이어 2
COPY package.json .         # 레이어 3
RUN npm install             # 레이어 4

# 이 명령어들은 레이어를 추가하지 않음
WORKDIR /app               # 레이어 없음
ENV NODE_ENV=production    # 레이어 없음
EXPOSE 3000               # 레이어 없음
CMD ["node", "server.js"]  # 레이어 없음

최적화 효과 비교

1
2
3
4
5
6
7
8
9
# ❌ 최적화 전 (8분, 1.2GB)
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y python3 python3-pip
RUN apt-get install -y build-essential
WORKDIR /app
COPY . .
RUN pip3 install -r requirements.txt
CMD ["python3", "app.py"]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# ✅ 최적화 후 (1.5분, 150MB)
# syntax=docker/dockerfile:1
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install --no-cache-dir -r requirements.txt

FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY . .
EXPOSE 8000
CMD ["python", "app.py"]
지표 최적화 전 최적화 후 개선
빌드 시간 8분 1.5분 81% ⬇️
이미지 크기 1.2GB 150MB 87% ⬇️
컨텍스트 크기 500MB 50MB 90% ⬇️
캐시 적중률 10% 90% 80% ⬆️

모니터링 및 디버깅

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 상세 빌드 시간 표시
time docker build -t myapp .

# BuildKit 상세 진행 상황
DOCKER_BUILDKIT=1 docker build --progress=plain -t myapp .

# 이미지 레이어 분석
docker history myapp:latest

# dive 도구로 심층 분석
dive myapp:latest