Как организовать установку пакетов и сборку кода в одном слое Docker-образа?

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

Ответ

Не рекомендуется объединять установку и сборку в один слой RUN. Это противоречит лучшим практикам Docker, так как приводит к увеличению размера образа из-за кэширования промежуточных артефактов и оставшихся файлов зависимостей (например, node_modules).

Правильный подход — использование многоступенчатой сборки (multi-stage build):

  1. Этап сборки (builder): Установите зависимости и соберите приложение.
  2. Финальный этап: Скопируйте только необходимые артефакты (бинарники, статику) в чистый, минимальный образ.

Пример для Go-приложения:

# Этап 1: Сборка
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /myapp ./cmd/app

# Этап 2: Финальный образ
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /myapp .
CMD ["./myapp"]

Ключевые преимущества:

  • Минимальный размер финального образа: В нем нет компилятора, исходного кода и временных файлов.
  • Улучшенная безопасность: Уменьшается поверхность для атак (меньше пакетов и утилит).
  • Эффективное кэширование слоев: Изменения в исходном коде не инвалидируют кэш слоя с зависимостями (go mod download).