Dockerfile
| Тип | формат описания сборки образа |
|---|---|
| Разработчик | Docker, Inc. |
| Появился | 2013 |
| Сборщик по умолчанию | BuildKit (с Docker Engine 23.0, 2023) |
| Многоэтапная сборка | с Docker 17.05 (2017) |
| Альтернативное имя | Containerfile (Podman, Buildah) |
| Линтер | hadolint |
Dockerfile — текстовый файл с последовательностью инструкций, по которым Docker (или совместимый сборщик) собирает образ контейнера: с какого базового образа начать, какие файлы скопировать, какие команды выполнить и что запускать при старте контейнера. Dockerfile хранится в репозитории рядом с кодом и превращает сборку окружения в воспроизводимый, версионируемый процесс (см. Контейнеризация).
Формат появился вместе с Docker в 2013 году и стал стандартом де-факто: его понимают Podman и Buildah (там он может называться Containerfile), kaniko, облачные сборщики и CI-системы.
Пример
Типичный Dockerfile для веб-приложения на Python:
# syntax=docker/dockerfile:1
FROM python:3.13-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
# сначала только зависимости — этот слой кэшируется, пока они не меняются
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
# потом код приложения
COPY . .
RUN useradd --create-home app
USER app
EXPOSE 8000
HEALTHCHECK CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
docker build -t myapp:1.0 . # собрать образ из Dockerfile в текущем каталоге
docker run -p 8000:8000 myapp:1.0
Основные инструкции
| Инструкция | Назначение |
|---|---|
FROM |
базовый образ; начинает новый этап сборки |
RUN |
выполнить команду при сборке (установка пакетов) |
COPY |
скопировать файлы из контекста сборки в образ |
ADD |
как COPY, но умеет скачивать URL и распаковывать архивы |
WORKDIR |
рабочий каталог для последующих инструкций |
ENV |
переменная окружения в образе и контейнере |
ARG |
параметр, доступный только во время сборки |
USER |
от какого пользователя выполнять команды и запускать процесс |
EXPOSE |
документирует порт, который слушает приложение |
CMD |
команда по умолчанию при запуске контейнера |
ENTRYPOINT |
исполняемый файл, к которому добавляются аргументы CMD |
HEALTHCHECK |
команда проверки работоспособности |
LABEL |
метаданные образа |
Полное описание — в справочнике Docker.[1]
CMD и ENTRYPOINT
CMD задаёт команду, которую легко заменить: docker run myapp bash запустит оболочку вместо приложения. ENTRYPOINT фиксирует исполняемый файл, а CMD превращается в его аргументы по умолчанию — так делают образы-утилиты. Обе инструкции лучше писать в exec-форме — JSON-массивом ["uvicorn", "main:app"]. В shell-форме (CMD uvicorn main:app) процесс запускается через /bin/sh -c, оболочка становится PID 1 и не передаёт приложению сигнал SIGTERM, из-за чего контейнер останавливается только по тайм-ауту.
Слои и кэш
Каждая инструкция RUN, COPY и ADD создаёт новый слой образа. Сборщик кэширует слои: если инструкция и её входные данные не изменились, слой берётся из кэша. Как только один слой меняется, все последующие пересобираются.[4] Отсюда главное правило порядка инструкций — от редко меняющихся к часто меняющимся: сначала системные пакеты, затем файл зависимостей и их установка, и только в конце — код приложения. Иначе любая правка кода заново скачивает все библиотеки.
Файл .dockerignore исключает из контекста сборки лишнее — .git, node_modules, .venv, локальные секреты, — ускоряя сборку и не давая этим файлам попасть в образ.
Многоэтапная сборка
Компиляторы и инструменты сборки не нужны в итоговом образе. Многоэтапная сборка позволяет собрать приложение в одном образе, а скопировать результат в другой, минимальный:[3]
FROM golang:1.25 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/server ./cmd/server
FROM gcr.io/distroless/static-debian12
COPY --from=build /out/server /server
USER nonroot
ENTRYPOINT ["/server"]
Итоговый образ содержит только бинарник и занимает единицы мегабайт вместо сотен.
BuildKit
С Docker Engine 23.0 (2023) сборку по умолчанию выполняет BuildKit. Он параллельно собирает независимые этапы, пропускает неиспользуемые и добавляет возможности: кэш-монтирования для менеджеров пакетов (RUN --mount=type=cache), безопасную передачу секретов при сборке (RUN --mount=type=secret), сборку под несколько архитектур (docker buildx build --platform linux/amd64,linux/arm64).[5] Первая строка # syntax=docker/dockerfile:1 подключает актуальную версию синтаксиса.
Хорошие практики
- Используйте небольшие официальные базовые образы (
-slim,alpine, distroless) и фиксируйте их версии, а для полной воспроизводимости — дайджест@sha256:.... - Объединяйте установку и очистку кэша пакетного менеджера в одной инструкции
RUN: удаление файлов в следующем слое не уменьшает размер образа. - Не запускайте процесс от root — добавьте
USER. - Не записывайте секреты в
ENV,ARGили файлы образа: они остаются в слоях и видны любому, у кого есть образ. - Проверяйте Dockerfile линтером hadolint[6] и сканируйте образ на уязвимости.
- Готовый образ публикуйте в реестр с осмысленными тегами — версией и хешем коммита, а не только
latest.[2]
Формат хранения готового образа описан в статье Образ контейнера и OCI, а запуск нескольких контейнеров вместе — в статье Docker Compose.