Dockerfile — текстовый файл с инструкциями для сборки Docker образа. Каждая инструкция создаёт слой образа. Разбираем все инструкции с примерами для Python и Node.js.

- Структура Dockerfile
- Таблица инструкций Dockerfile
- Dockerfile для Python приложения
- Dockerfile для Node.js
- Многоэтапная сборка (multi-stage build)
- .dockerignore — исключить из контекста
- CMD vs ENTRYPOINT
- Лучшие практики
- Часто задаваемые вопросы
- CMD vs ENTRYPOINT — в чём разница?
- Почему нельзя запускать контейнер от root?
- Что такое .dockerignore?
- Что читать дальше по Docker
Структура Dockerfile
# Комментарии начинаются с #
# Базовый образ — ОБЯЗАТЕЛЬНО первым
FROM ubuntu:22.04
# Метаданные образа
LABEL maintainer="dev@example.com"
LABEL version="1.0"
# Переменные окружения
ENV APP_PORT=8080
ENV NODE_ENV=production
# Выполнить команду при СБОРКЕ образа
RUN apt update && apt install -y python3
# Рабочая директория (создаётся если нет)
WORKDIR /app
# Скопировать файлы из контекста сборки в образ
COPY requirements.txt .
COPY . .
# Открытый порт (документация — не открывает реально)
EXPOSE 8080
# Команда при ЗАПУСКЕ контейнера
CMD ["python3", "app.py"]
Таблица инструкций Dockerfile
| Инструкция | Описание |
|---|---|
FROM | Базовый образ. Обязательно первым |
RUN | Выполнить команду при сборке образа |
COPY | Скопировать файлы из контекста в образ |
ADD | Как COPY, но умеет URL и распаковывает .tar.gz |
WORKDIR | Установить рабочую директорию |
ENV | Переменная окружения (доступна при сборке и в контейнере) |
ARG | Аргумент сборки — доступен только при build |
EXPOSE | Документирует порт (не открывает реально) |
CMD | Команда по умолчанию при запуске контейнера |
ENTRYPOINT | Точка входа — не перезаписывается при docker run |
VOLUME | Объявить точку монтирования |
USER | Пользователь для последующих команд |
LABEL | Метаданные образа (ключ=значение) |
HEALTHCHECK | Команда проверки здоровья контейнера |
Dockerfile для Python приложения
# Dockerfile
FROM python:3.12-slim
# Не буферизовать вывод Python — логи видны сразу
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
WORKDIR /app
# Сначала копируем зависимости (кэш слоёв!)
# Если requirements.txt не изменился — pip install не запустится
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Потом остальной код
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
# requirements.txt
flask==3.0.0
gunicorn==21.2.0
requests==2.31.0
Dockerfile для Node.js
FROM node:20-alpine
WORKDIR /app
# Сначала package.json (кэш npm install)
COPY package*.json ./
RUN npm ci --only=production
# Потом код
COPY . .
EXPOSE 3000
# Запуск от непривилегированного пользователя (безопасность)
USER node
CMD ["node", "server.js"]
Многоэтапная сборка (multi-stage build)
Позволяет собирать приложение в одном контейнере, а итоговый образ делать маленьким — без инструментов сборки.
# Этап 1: сборка (большой образ с Node.js)
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build # создаёт dist/
# Этап 2: production образ (только Nginx + статика)
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Финальный образ содержит только Nginx + статику, без Node.js и node_modules (~150 МБ вместо ~500 МБ).
.dockerignore — исключить из контекста
# .dockerignore — работает как .gitignore
node_modules/
dist/
.git/
*.log
.env
.env.local
__pycache__/
*.pyc
*.pyo
.pytest_cache/
venv/
.venv/
*.md
Без .dockerignore весь node_modules (100+ МБ) отправляется в Docker daemon при каждой сборке — значительно замедляет docker build.
CMD vs ENTRYPOINT
# CMD — команда по умолчанию, ПЕРЕЗАПИСЫВАЕТСЯ при docker run
CMD ["python", "app.py"]
docker run myimage # запустит: python app.py
docker run myimage python other.py # запустит: python other.py
# ENTRYPOINT — точка входа, НЕ перезаписывается
ENTRYPOINT ["python"]
CMD ["app.py"]
docker run myimage # запустит: python app.py
docker run myimage other.py # запустит: python other.py
docker run --entrypoint bash myimage # перезаписать только через --entrypoint
# Типичный паттерн: ENTRYPOINT + CMD вместе
ENTRYPOINT ["gunicorn"]
CMD ["--bind", "0.0.0.0:5000", "app:app"]
# docker run myimage → gunicorn --bind 0.0.0.0:5000 app:app
# docker run myimage --workers 4 app:app → заменит CMD
Лучшие практики
# ✅ Объединять RUN команды (меньше слоёв, меньше размер)
RUN apt update && \
apt install -y curl vim && \
rm -rf /var/lib/apt/lists/*
# ❌ Много отдельных RUN — много слоёв
RUN apt update
RUN apt install -y curl
RUN apt install -y vim
# ✅ Копировать зависимости отдельно от кода
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . . # изменения кода не сбрасывают кэш pip install
# ✅ Использовать slim/alpine образы
FROM python:3.12-slim # ~130 МБ вместо ~1 ГБ
FROM node:20-alpine # ~40 МБ вместо ~350 МБ
# ✅ Не запускать от root
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# ✅ Фиксировать версии
FROM python:3.12-slim # не FROM python:latest
Часто задаваемые вопросы
CMD vs ENTRYPOINT — в чём разница?
CMD задаёт команду по умолчанию при запуске контейнера, но её можно легко перезаписать через аргументы docker run. ENTRYPOINT задаёт «исполнитель» — его нельзя перезаписать через обычные аргументы (только через --entrypoint). Распространённый паттерн: ENTRYPOINT = исполняемый файл, CMD = аргументы по умолчанию. Например: ENTRYPOINT ["python"] + CMD ["app.py"] — запускает python app.py, но docker run img other.py запустит python other.py.
Почему нельзя запускать контейнер от root?
Если приложение в контейнере скомпрометировано и контейнер запущен от root — атакующий может эксплуатировать уязвимости ядра для получения прав root на хосте. Запуск от непривилегированного пользователя (USER node, USER 1000) — базовый принцип безопасности. Большинство официальных образов (Node.js, Python) уже содержат непривилегированного пользователя.
Что такое .dockerignore?
Файл .dockerignore работает как .gitignore — исключает файлы и папки из контекста сборки (то что отправляется в Docker daemon при docker build). Без него node_modules, .git, логи и другой мусор включаются в контекст — сборка замедляется, размер образа растёт. Файл .dockerignore должен находиться в том же каталоге что и Dockerfile.
Что читать дальше по Docker
Чтобы связать тему с соседними практическими материалами, дальше удобно открыть:
- docker build — собрать образ из Dockerfile.
- docker commit и docker save — сохранить контейнер или образ.
- docker run — запустить собранный образ.



