Как быстро внести изменения в тяжелый Docker образ

«Как быстро внести изменения в тяжелый Docker образ» — вопрос из категории Docker, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Чтобы минимизировать время пересборки тяжелого Docker-образа, я применяю несколько практик:

  1. Использую многоступенчатые сборки (multi-stage builds). Это позволяет отделить среду сборки от финального рантайм-образа, копируя только необходимые артефакты.

    # Stage 1: Builder
    FROM golang:1.20-alpine AS builder
    WORKDIR /app
    COPY go.mod go.sum ./
    RUN go mod download
    COPY . .
    RUN CGO_ENABLED=0 GOOS=linux go build -o /myapp
    
    # Stage 2: Final lightweight image
    FROM alpine:latest
    RUN apk --no-cache add ca-certificates
    WORKDIR /root/
    COPY --from=builder /myapp .
    CMD ["./myapp"]
  2. Оптимизирую порядок инструкций и кэширование слоев. Сначала добавляю редко меняющиеся файлы (зависимости), затем часто меняющийся код.

    # Сначала зависимости – слой кэшируется надолго
    COPY package.json package-lock.json ./ 
    RUN npm ci --only=production
    
    # Потом исходный код – этот слой пересобирается при каждом изменении
    COPY src/ ./src
  3. Объединяю команды RUN для уменьшения количества слоев и очистки кэша пакетного менеджера в одной инструкции.

    RUN apt-get update && 
        apt-get install -y --no-install-recommends some-package && 
        apt-get clean && 
        rm -rf /var/lib/apt/lists/*
  4. Тщательно настраиваю .dockerignore, чтобы исключить из контекста сборки ненужные файлы (.git, node_modules, лог-файлы, временные файлы редактора), что значительно ускоряет передачу контекста Docker-демону.

  5. Использую BuildKit ( DOCKER_BUILDKIT=1 ) для более эффективного кэширования и параллельного выполнения этапов сборки.