1. Что такое Dockerfile
Dockerfile — это текстовый файл с инструкциями для создания Docker-образа. Он содержит последовательность команд, которые Docker выполняет сверху вниз при сборке образа.
Имя файла — всегда Dockerfile (с заглавной буквы и без расширения). Инструкции пишутся заглавными буквами, после них идут аргументы.
docker commit. Это неудобно, непрозрачно и не воспроизводимо. Dockerfile — это воспроизводимый рецепт: любой разработчик получит одинаковый образ из одного и того же Dockerfile.
Минимальный пример Dockerfile:
# Dockerfile
FROM ubuntu:22.04
COPY . /app
WORKDIR /app
RUN apt-get update && apt-get install -y python3
ENV PYTHONUNBUFFERED=1
CMD ["python3", "app.py"]
2. Слои образа и кеширование
Каждая инструкция в Dockerfile создаёт новый слой — промежуточное состояние файловой системы после выполнения этой инструкции.
Как работает кеш
Docker кешируем слои, чтобы ускорить сборку. При повторной сборке Docker проверяет: изменилась ли инструкция и её контекст? Если нет — берёт готовый слой из кеша. Если да — пересобирает этот слой и все последующие.
FROM scratch
Особый случай: FROM scratch создаёт образ с нуля, без какого-либо базового образа. Используется для создания минимальных образов — только самые необходимые файлы и библиотеки. Типичный сценарий — Go-бинарник, который компилируется статически.
3. Инструкции Dockerfile
FROM — базовый образ
Первая и обязательная инструкция. Задаёт родительский образ, на основе которого будет собран новый.
# Dockerfile
FROM ubuntu:22.04
FROM python:3.12-slim
FROM nginx:1.27-alpine
FROM scratch
LABEL — метаданные
Добавляет метаданные к образу: автор, версия, описание. Не влияет на работу, но полезно для документирования.
# Dockerfile
LABEL maintainer="you@example.com"
LABEL version="1.0"
LABEL description="My web app"
WORKDIR — рабочая директория
Задаёт рабочую директорию для последующих инструкций (RUN, COPY, CMD, ENTRYPOINT). Если директория не существует — создаёт её. Предпочтительнее, чем RUN mkdir && cd.
# Dockerfile
WORKDIR /app
# Теперь все пути относительны /app
COPY — копирование файлов
Копирует файлы и директории из контекста сборки (папки, откуда запускается docker build) в файловую систему образа. Если целевая директория не существует — создаёт её.
# Dockerfile
COPY index.html /usr/share/nginx/html/
COPY . /app
COPY requirements.txt . # в текущий WORKDIR
RUN — команды при сборке
Выполняет команду в оболочке контейнера и фиксирует результат как новый слой. Используется для установки пакетов, настройки окружения, компиляции.
# Dockerfile
# Правильно: объединяем в одну RUN (один слой) + очищаем кеш пакетов
RUN apt-get update && apt-get install -y \
curl \
git \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
&& в один RUN — меньше слоёв, меньше итоговый размер образа.
ENV — переменные окружения
Устанавливает переменные среды, которые доступны и при сборке, и при работе контейнера.
# Dockerfile
ENV APP_ENV=production
ENV PORT=8080
ENV PYTHONUNBUFFERED=1
ARG — аргументы сборки
Переменные, которые передаются при сборке через docker build --build-arg. В отличие от ENV, недоступны в работающем контейнере.
# Dockerfile
ARG APP_VERSION=1.0
RUN echo "Building version $APP_VERSION"
# Терминал
docker build --build-arg APP_VERSION=2.0 .
EXPOSE — документирование порта
Указывает, что контейнер планирует использовать данный порт. Это только документация — реальный проброс порта делается флагом -p при docker run.
# Dockerfile
EXPOSE 80
EXPOSE 5432
CMD — команда запуска (переопределяемая)
Определяет команду по умолчанию при старте контейнера. В файле должна быть только одна инструкция CMD (последняя побеждает). Может быть переопределена аргументами docker run.
# Dockerfile
# Exec-форма (предпочтительная — не запускает shell)
CMD ["nginx", "-g", "daemon off;"]
CMD ["python3", "app.py"]
# Shell-форма (запускает через /bin/sh -c)
CMD python3 app.py
ENTRYPOINT — фиксированная точка входа
Определяет исполняемый файл, который всегда запускается при старте контейнера. Аргументы docker run добавляются к ENTRYPOINT, а не заменяют его. Переопределить можно только флагом --entrypoint.
# Dockerfile
ENTRYPOINT ["python3", "app.py"]
# docker run myimage --port 8080 → python3 app.py --port 8080
# Dockerfile
ENTRYPOINT ["python3"]
CMD ["app.py"]
# docker run myimage other.py → python3 other.py
USER — пользователь выполнения
Устанавливает пользователя для выполнения последующих команд. Best practice: создавать отдельного непривилегированного пользователя вместо root.
# Dockerfile
RUN useradd -m appuser
USER appuser
VOLUME — точка монтирования
Создаёт точку монтирования. Данные в этой директории хранятся вне слоёв образа и сохраняются при удалении контейнера. При запуске без явного указания volume Docker создаёт анонимный volume автоматически.
# Dockerfile
VOLUME ["/data"]
VOLUME /var/lib/mysql
ADD — расширенная версия COPY
Похожа на COPY, но дополнительно умеет: распаковывать локальные .tar-архивы и скачивать файлы по URL. В большинстве случаев рекомендуется использовать COPY — она предсказуемее.
# Dockerfile
# Использовать только когда нужна распаковка tar:
ADD archive.tar.gz /app/
4. Оптимизация Dockerfile
Стратегия порядка слоёв
Кеш аннулируется начиная с изменённой строки. Правило: размещайте то, что меняется редко — выше; то, что меняется часто — ниже.
# Dockerfile
# Правильный порядок для Python-приложения:
FROM python:3.12-slim
WORKDIR /app
# 1. Сначала только requirements — меняется редко → долгий слой кешируется
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 2. Потом код приложения — меняется часто → пересборка только быстрого слоя
COPY . .
CMD ["python3", "app.py"]
Файл .dockerignore
Аналог .gitignore — исключает файлы из контекста сборки. Уменьшает размер контекста (ускоряет docker build) и предотвращает случайное попадание секретов в образ.
# .dockerignore
.git
.env
__pycache__
*.pyc
*.log
node_modules
.DS_Store
Очистка временных файлов
После установки пакетов apt/apk оставляют кеш — он увеличивает размер слоя. Очищайте его в той же инструкции RUN:
# Dockerfile
RUN apt-get update && apt-get install -y \
curl \
git \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
5. Persistence Layer в Docker
Persistence Layer (слой сохранения данных) — механизм долговременного хранения данных в архитектуре приложения.
Проблема: контейнеры по природе своей эфемерны. Любые данные внутри контейнера исчезают при его удалении. Для баз данных, загружаемых файлов, журналов это неприемлемо.
Три сценария, где нужен Persistence Layer
- Хранение данных между перезапусками: БД не должна терять записи при перезапуске контейнера
- Состояние приложения: пользовательские данные, конфиги, загруженные файлы
- Отказоустойчивость: данные вне контейнера можно восстановить при сбое
Тома (Volumes)
Предпочтительный механизм. Управляются Docker-daemon, хранятся в специальной области Docker на хосте (/var/lib/docker/volumes/). Подходят для продакшна.
- Создаются и управляются Docker, изолированы от файловой системы хоста
- Могут быть именованными или анонимными
- Поддерживают удалённые хранилища через драйверы
# Терминал
# Создать именованный volume
docker volume create mydata
# Использовать volume при запуске контейнера
docker run -v mydata:/app/data myimage
# Список volumes
docker volume ls
# Удалить volume
docker volume rm mydata
Bind Mounts (связанные монтирования)
Монтирует конкретную директорию хоста в контейнер. Полезно при разработке — изменения на хосте сразу видны в контейнере. Менее переносимо для продакшна (зависит от структуры файловой системы хоста).
# Терминал
# Монтировать текущую директорию в /app контейнера
docker run -v ./src:/app/src myimage
# Windows PowerShell — абсолютный путь
docker run -v C:\projects\myapp:/app myimage
6. Запуск базы данных в Docker
Официальные образы баз данных используют переменные окружения для конфигурации паролей и имён — это безопаснее, чем хардкодить в Dockerfile.
MySQL в Docker
# Терминал
docker run -d \
--name mysqlDB \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=my-secret-pw \
-e MYSQL_DATABASE=myapp \
-v mysql-data:/var/lib/mysql \
mysql:8.0
Разбор флагов:
-p 3306:3306— проброс порта хост:контейнер-e MYSQL_ROOT_PASSWORD— пароль root (обязателен для mysql-образа)-e MYSQL_DATABASE— автоматически создать базу данных при запуске-v mysql-data:/var/lib/mysql— именованный volume для данных MySQL
PostgreSQL в Docker
# Терминал
docker run -d \
--name postgresDB \
-p 5432:5432 \
-e POSTGRES_PASSWORD=my-secret-pw \
-e POSTGRES_DB=myapp \
-e POSTGRES_USER=myuser \
-v pg-data:/var/lib/postgresql/data \
postgres:16-alpine
docker history и docker inspect. Передавайте секреты только через -e при docker run или через Docker Secrets (для Swarm/Compose).