Как ответить
Обычно Dockerfile пишу я — разработчик, который отвечает за свой сервис. На моём текущем проекте я сам создаю и поддерживаю Dockerfile для микросервиса на Go. Но это не значит, что я пишу его с нуля каждый раз: у команды есть репозиторий с типовыми шаблонами, которые мы адаптируем под конкретную задачу.
Для junior-разработчика важно не просто скопировать шаблон, а понимать, что делает каждый слой, и уметь обосновать выбор базового образа или инструкции. Например, я всегда использую многоступенчатую сборку, чтобы финальный образ был минимальным:
# build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server .
# run stage
FROM alpine:3.19
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/server /server
EXPOSE 8080
CMD ["/server"]Такой подход даёт образ ~10 МБ вместо 800 МБ. Кроме того, я добавляю .dockerignore, чтобы не тащить в контекст сборки .git, node_modules или временные файлы.
В некоторых случаях — когда сервис уже развёрнут на Kubernetes, а инфраструктурой занимается DevOps — финальную доводку Dockerfile (тегирование, проброс секретов, healthcheck) может делать инженер платформы. Но содержимое и логику сборки определяет разработчик. В команде есть code review для Dockerfile, как и для любого другого кода.
Мои основные принципы при написании Dockerfile:
- Базовый образ: предпочитаю официальные alpine или slim-версии, чтобы минимизировать поверхность атаки и размер.
- Кеширование слоёв: сначала копирую файлы зависимостей (
go.mod,go.sum), потом всё остальное — это ускоряет пересборку при изменении только исходников. - Экспорт портов: указываю только те порты, которые реально слушает приложение.
- Безопасность: не запускаю контейнер от root, добавляю непривилегированного пользователя в run-образе.
Если в проекте микросервисная архитектура, я также координируюсь с коллегами, чтобы единообразно настроить логирование, трейсинг и переменные окружения в Dockerfile. Так проще отлаживать и деплоить.