Как собираешь Docker-образы?

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

Ответ

Мой процесс сборки Docker-образов нацелен на безопасность, минимальный размер и воспроизводимость. Вот ключевые практики:

1. Оптимизация Dockerfile:

  • Многоступенчатая сборка (Multi-stage): Отделяю этап сборки (builder) от финального рантайм-образа, чтобы не тащить в продакшен компиляторы и исходный код.
  • Минимизация слоев: Объединяю связанные RUN, COPY команды и очищаю кэш пакетных менеджеров в одном слое.
  • Использование .dockerignore: Обязательно исключаю ненужные файлы (.git, node_modules, лог-файлы), чтобы они не попали в контекст сборки.

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

# STAGE 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 /myapp ./cmd/main.go

# STAGE 2: Рантайм
FROM alpine:latest
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /root/
COPY --from=builder /myapp .
# Копируем только бинарник, а не исходники
EXPOSE 8080
USER nobody
CMD ["./myapp"]

3. Безопасность:

  • Использую минимальные базовые образы (alpine, distroless).
  • Запускаю приложение от непривилегированного пользователя (USER nobody).
  • Интегрирую сканирование уязвимостей (Trivy, Grype) в CI/CD пайплайн до пуша в реестр.

4. CI/CD интеграция: Сборка происходит в пайплайне (например, GitLab CI). Использую кэширование слоев (--cache-from) для ускорения, тегирую образы по хешу коммита и семантическому версионированию, затем пушим в приватный реестр (Harbor, GitLab Container Registry).