В чем ключевое отличие Dockerfile для компилируемых языков (Go, Rust, C++) от интерпретируемых (Python, Node.js)?

«В чем ключевое отличие Dockerfile для компилируемых языков (Go, Rust, C++) от интерпретируемых (Python, Node.js)?» — вопрос из категории Docker, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Основное отличие — использование многоэтапной сборки (multi-stage build) для компилируемых языков. Это позволяет создать минимальный итоговый образ, содержащий только скомпилированный бинарный файл, без компиляторов, исходного кода и промежуточных артефактов.

Dockerfile для компилируемого языка (Go):

# Первый этап (builder): среда для компиляции
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/app

# Второй этап (финальный): минимальный образ для запуска
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
# Копируем ТОЛЬКО бинарник из этапа builder
COPY --from=builder /myapp .
CMD ["./myapp"]

Dockerfile для интерпретируемого языка (Python):

# Обычно одноэтапная сборка
FROM python:3.11-slim
WORKDIR /app
# Копируем зависимости и устанавливаем их
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Копируем весь исходный код
COPY . .
CMD ["python", "main.py"]

Ключевые различия:

  1. Размер образа: Итоговый образ для Go (на основе alpine) может весить ~10-20 МБ. Образ для Python, даже на slim, с установленными пакетами легко достигает 100+ МБ.
  2. Безопасность: В финальном образе для компилируемого языка нет компилятора и исходного кода, что уменьшает поверхность для атаки.
  3. Содержимое: В Python-образе остаются все исходные файлы .py, что необходимо для работы интерпретатора.