Как написать эффективный Dockerfile для Go-приложения? Приведите пример.

Ответ

Да, для создания эффективного Docker-образа для Go-приложения лучше всего использовать мультистадийную сборку (multi-stage build). Это позволяет получить минимальный по размеру и более безопасный финальный образ.

Пример Dockerfile с мультистадийной сборкой:

# --- Этап 1: Сборка (builder) ---
# Используем полный образ Go для компиляции
FROM golang:1.21-alpine AS builder

# Устанавливаем рабочую директорию
WORKDIR /app

# Копируем файлы модулей и загружаем зависимости
# Этот шаг кэшируется и не будет выполняться, если go.mod/go.sum не изменились
COPY go.mod go.sum ./
RUN go mod download

# Копируем весь исходный код
COPY . .

# Собираем статически скомпонованный бинарник, чтобы он не зависел от системных библиотек
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /app/main .

# --- Этап 2: Финальный образ (final) ---
# Используем минимальный базовый образ без лишних зависимостей
FROM alpine:latest

WORKDIR /root/

# Копируем только скомпилированный бинарник из этапа сборки
COPY --from=builder /app/main .

# (Опционально) Сообщаем Docker, что контейнер будет слушать этот порт
EXPOSE 8080

# Команда для запуска приложения при старте контейнера
CMD ["./main"]

Ключевые преимущества этого подхода:

  1. Минимальный размер образа: Финальный образ содержит только бинарник и минимальные зависимости alpine (десятки мегабайт), а не весь Go SDK и исходный код (сотни мегабайт).
  2. Повышенная безопасность: В финальном образе отсутствуют исходный код, инструменты сборки и ненужные библиотеки, что уменьшает поверхность для атаки.
  3. Оптимизация кэширования: Сначала копируются и устанавливаются зависимости, и только потом — исходный код. Это позволяет Docker кэшировать слой с зависимостями, ускоряя последующие сборки, если менялся только код приложения.