Инструкции и практики написания Dockerfile

Справочник: Инструкции Dockerfile с примерами. Порядок инструкций влияет на кэширование слоёв.

Основные инструкции

ИнструкцияОписаниеПример
FROMБазовый образ. Первая инструкция.FROM node:20-alpine
WORKDIRЗадает рабочую директорию для след. команд.WORKDIR /app
COPYКопирует файлы с хоста в образ.COPY . .
ADDКак COPY, но умеет распаковывать tar и качать URL.ADD https://.../file.tar.gz /src
RUNВыполняет команду при сборке (создает слой).RUN npm install
CMDКоманда по умолчанию при запуске. Можно переопределить.CMD ["node", "app.js"]
ENTRYPOINTОсновной процесс. Аргументы CMD летят к нему.ENTRYPOINT ["/app/start.sh"]
ENVПеременная окружения (доступна в build и runtime).ENV PORT=3000
ARGПеременная сборки (доступна ТОЛЬКО в build).ARG VERSION=1.0
EXPOSEДокументирует порт (не публикует его!).EXPOSE 80
VOLUMEОбъявляет точку монтирования (создает анонимный том).VOLUME /var/lib/mysql
USERПереключает пользователя (Security).USER node
HEALTHCHECKПроверка здоровья контейнера.HEALTHCHECK CMD curl -f ...
ONBUILDТриггер для дочерних образов (использовать с осторожностью).ONBUILD COPY . .

Свод правил для создания безопасных, компактных и быстрых в сборке образов.

1. Базовые принципы

Один процесс — один контейнер

Контейнер должен запускать только одно приложение. Это упрощает масштабирование и переиспользование. Не используйте supervisord без крайней необходимости.

Эфемерность

Контейнеры должны быть готовы к уничтожению в любой момент. Храните состояние (данные) в Volumes, а конфигурацию — в ENV.

Используйте .dockerignore

Всегда создавайте файл .dockerignore в корне проекта. Исключайте .git, node_modules, build, dist и файлы секретов (.env). Это уменьшает контекст сборки (Build Context) и ускоряет COPY . ..

2. Безопасность (Security)

Не используйте Root

По умолчанию Docker запускает процессы от root. Это небезопасно. Правильно:

RUN groupadd -r app && useradd -r -g app app
USER app

Исключение: Установка пакетов (apt-get) должна выполняться от root, переключение USER делайте в конце.

Используйте доверенные Base Images

Избегайте FROM node:latest. Используйте конкретные версии и дистрибутивы (Alpine/Slim).

  • FROM python
  • FROM python:3.11-slim-bookworm

Не храните секреты в ENV/ARG

Никогда не передавайте пароли через ENV или ARG во время сборки — они останутся в истории слоев. Используйте механизм RUN --mount=type=secret.

3. Оптимизация слоев (Caching)

Docker кэширует слои. Порядок инструкций критичен.

Сначала зависимости, потом код

Сначала копируйте файлы описания зависимостей (package.json, go.mod, requirements.txt), устанавливайте их, и только потом копируйте исходный код.

Правильно:

COPY package.json package-lock.json ./
RUN npm ci
COPY . .

Неправильно:

COPY . .
RUN npm ci

(Во втором случае любое изменение в коде инвалидирует кэш установки пакетов).

Объединение команд (Chaining)

Объединяйте связанные команды в один RUN, чтобы уменьшить количество слоев и удалить временные файлы в том же слое.

Правильно:

RUN apt-get update && apt-get install -y \
    curl \
    git \
    && rm -rf /var/lib/apt/lists/*

(Если rm будет в отдельном RUN, файлы удалятся из финального образа, но останутся в истории предыдущего слоя).

4. Multi-Stage Builds

Всегда используйте многоэтапную сборку для компилируемых языков (Go, Java, C++) и сборки фронтенда.

Пример (Go):

# Stage 1: Build
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .
 
# Stage 2: Runtime
FROM alpine:3.18
WORKDIR /app
COPY --from=builder /app/myapp .
CMD ["./myapp"]

Результат: Образ весит 10MB вместо 800MB.

5. Инструкции (Syntax)

COPY вместо ADD

Используйте COPY для копирования локальных файлов. ADD имеет “магические” функции (распаковка tar, скачивание URL), которые могут привести к непредсказуемому поведению. Используйте ADD только если вам действительно нужна авто-распаковка архива.

ENTRYPOINT + CMD

Используйте ENTRYPOINT для основного исполняемого файла, а CMD — для аргументов по умолчанию.

ENTRYPOINT ["/app/bin/server"]
CMD ["--help"]

Это позволяет пользователю запускать контейнер как бинарник: docker run my-img --version.

Exec Form (JSON array)

Всегда используйте JSON-массив для CMD и ENTRYPOINT.

  • CMD ["npm", "start"] — процесс получает PID 1, сигналы проходят корректно.
  • CMD npm start — запускается через sh -c, сигналы (SIGTERM) не доходят до приложения.

6. Linting

Используйте линтер Hadolint в CI/CD или редакторе кода. Он автоматически находит нарушения этих правил.