Инструкции и практики написания 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 или редакторе кода. Он автоматически находит нарушения этих правил.