Ответ
Для сборки Docker-образов Go-приложений повсеместно используется подход многоэтапной сборки (multi-stage build). Его главная цель — создать минимальный по размеру и безопасный итоговый образ, исключив из него все сборочные зависимости (компилятор Go, исходный код, утилиты).
Пример Dockerfile
с многоэтапной сборкой:
# --- Этап 1: Сборка (Builder) ---
# Используем образ с установленным Go
FROM golang:1.21-alpine AS builder
# Устанавливаем рабочую директорию
WORKDIR /app
# Копируем файлы зависимостей для кэширования
COPY go.mod go.sum ./
# Скачиваем зависимости. Этот слой будет кэшироваться, если go.mod/go.sum не менялись
RUN go mod download
# Копируем весь исходный код
COPY . .
# Собираем статически скомпилированный бинарный файл
# CGO_ENABLED=0 отключает Cgo, что позволяет создать бинарник без зависимостей от системных библиотек C
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app/main .
# --- Этап 2: Запуск (Final) ---
# Используем минимально возможный образ. `scratch` - это пустой образ.
FROM scratch
# Устанавливаем рабочую директорию
WORKDIR /app
# Копируем только скомпилированный бинарник из этапа сборки
COPY --from=builder /app/main .
# (Опционально) Копируем необходимые файлы, например, конфиги или SSL-сертификаты
# COPY --from=builder /app/configs ./configs
# COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# Указываем команду для запуска приложения
CMD ["./main"]
Ключевые оптимизации и лучшие практики:
- Статическая компиляция:
CGO_ENABLED=0
создает полностью автономный бинарник. Флаги-s -w
убирают отладочную информацию и символьные таблицы, что дополнительно уменьшает размер. - Минимальный базовый образ:
scratch
: Пустой образ, идеален для статически скомпилированных бинарников. Максимальная безопасность и минимальный размер.alpine
: Очень маленький дистрибутив Linux, полезен, если приложению нужны системные утилиты (например,ca-certificates
).distroless
: Образы от Google (gcr.io/distroless/static-debian11
), которые содержат только необходимые для запуска приложения библиотеки и ничего лишнего. Хороший компромисс междуscratch
иalpine
.
- Оптимизация кэширования слоев: Порядок команд в
Dockerfile
важен. Сначала копируются и устанавливаются зависимости (go.mod
,go.sum
), и только потом — исходный код. Это позволяет Docker переиспользовать слой с зависимостями, если менялся только код приложения. - Безопасность: Запускайте приложение от имени непривилегированного пользователя. Для этого в финальном образе нужно создать пользователя и переключиться на него:
# ... в финальном образе FROM scratch # ... COPY --from=builder /etc/passwd /etc/passwd USER appuser:appuser # ... CMD ["./main"]