Писал ли Dockerfile

Ответ

Да, постоянно. Dockerfile — это основа моей работы по упаковке приложений. Я пишу их с учетом безопасности, производительности и воспроизводимости сборок.

Типичный рабочий процесс и лучшие практики, которые я применяю:

  1. Выбор минимального базового образа: Например, alpine или -slim-версии, чтобы уменьшить размер и поверхность для атак.
  2. Многоступенчатые сборки (Multi-stage builds): Для отделения среды сборки (со всеми компиляторами и dev-зависимостями) от финального рантайм-образа.
  3. Правильное копирование и кеширование слоев: Сначала копирую файлы зависимостей (requirements.txt, package.json), устанавливаю их, а уже потом копирую весь исходный код. Это позволяет использовать кеш Docker при изменении только кода.
  4. Использование .dockerignore: Исключаю из образа ненужные файлы (логи, временные файлы, .git), что ускоряет сборку и повышает безопасность.
  5. Запуск от непривилегированного пользователя: Создаю и переключаюсь на не-root пользователя внутри контейнера для повышения безопасности.

Пример Dockerfile для Go-приложения с multi-stage build:

# Этап сборки
FROM golang:1.19-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

# Финальный этап
FROM alpine:latest
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /root/
# Копируем только бинарник из этапа сборки
COPY --from=builder /myapp .
# Меняем владельца файла
RUN chown appuser:appgroup /root/myapp
USER appuser
CMD ["./myapp"]

Ответ 18+ 🔞

А, Dockerfile? Да постоянно с ними работаю, это ж основа всей этой движухи с контейнерами. Пишу их так, чтобы и безопасно было, и быстро собиралось, и чтобы другой инженер не охренел, когда будет разбираться.

Вот как я обычно действую и на что обращаю внимание, чтобы не накосячить:

  1. Базовый образ беру по-минималке. Alpine или эти -slim версии — чтобы образ не раздувался как пузо после пива и дыр в безопасности было меньше. Зачем тащить в продакшен целый дистрибутив, если нужно только рантайм?
  2. Многоступенчатые сборки — это святое. Одна сцена — для всей возни с компиляцией и зависимостями, а вторая, чистая — только для готового приложения. Получается элегантно и безопасно, как шпион в смокинге.
  3. Кеширование слоёв — отдельная магия. Сначала копирую файлы с зависимостями (go.mod, package.json), ставлю их, а уж потом весь остальной код. Так при правке пары строк в коде Docker не будет заново качать все библиотеки из интернета, что экономит кучу времени и нервов.
  4. .dockerignore — мой лучший друг. Без него в образ могут пролезть всякие .git, логи и временные файлы. Образ распухает, безопасность летит к чёрту. Настраиваю его одним из первых, чтобы не было потом мучительно больно.
  5. Запускаю от обычного пользователя, а не от root. Это как не ходить по тёмному переулку в ночи — просто базовое правило безопасности. Создаю внутри контейнера своего юзера и переключаюсь на него.

Вот смотри, пример для Go-приложения, где вся эта философия видна как на ладони:

# Этап сборки: тут компилируем
FROM golang:1.19-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

# Финальный этап: тут уже только готовый бинарник
FROM alpine:latest
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /root/
# Копируем только бинарник из этапа сборки
COPY --from=builder /myapp .
# Меняем владельца файла
RUN chown appuser:appgroup /root/myapp
USER appuser
CMD ["./myapp"]

Видишь разницу? В первом образе — весь компилятор и dev-зависимости, а во втором — голый Alpine и один бинарник. Красота, ёпта! Весит в разы меньше, атаковать почти нечего.