Docker build: сборка образа Dockerfile
Dockerfile — документ с инструкциями по созданию Docker-образов. Фактически это рецепт управляемых контейнеров, которые могут воспроизводиться в любой рабочей среде. Нужно уметь с ним обращаться, чтобы избежать ошибок при сборке и проблем в продакшене. Разберем структуру Dockerfile, команду docker build и практики работы с образами, которые пригодятся в реальных проектах.

- Что такое Dockerfile и зачем он нужен
- Основные инструкции Dockerfile с примерами
- Процесс сборки: команда docker build
- Оптимизация Docker-образов: уменьшаем размер, повышаем безопасность
- Практические примеры: собираем образ веб-приложения
- Продвинутые техники и лучшие практики
- Отладка сборки и решение проблем
- Следующие шаги: что делать с собранным образом
Что такое Dockerfile и зачем он нужен
Dockerfile — это текстовый файл, который содержит инструкции для автоматической сборки образа. Описываются все шаги: выбор «базы», копирование файлов, установка зависимости, команда для запуска и другие. Окружение представлено в виде кода, который можно воспроизвести в любой системе, где работает Docker.
Преимущества Dockerfile:
Повторяемость — все шаги явно описаны в инструкциях, поэтому Dockerfile гарантирует, что результат сборки образа не поменяется независимо от операционной системы, «железа» и команды разработки.
Версионность — текстовый файл можно хранить в любой системе контроля версий и отслеживать изменения. При необходимости легко откатиться к прежнему состоянию инструкций.
Автоматизация и CI/CD — Dockerfile без проблем интегрируется в пайплайны сборки и доставки приложений. Системы могут автоматически собрать образ, тестировать и публиковать без участия человека.
Dockerfile позволяет автоматизировать сборку образов и сделать ее предсказуемой. Не нужно каждый раз вручную по-разному настраивать контейнеры — все шаги каждый раз будут выполняться одинаково по инструкциям.
Dockerfile
Docker читает Dockerfile сверху вниз и послойно создает образ. Инструкции, изменяющие файловую систему (RUN, COPY, ADD), создают отдельные слои. Инструкции-метаданные (ENV, LABEL, EXPOSE, CMD, ENTRYPOINT) не создают постоянных слоев, а добавляют информацию к образу.
Базовый синтаксис и структура файла
В текстовом файле Dockerfile стандартный формат каждой строки выглядит так:
Инструкции — это ключевые слова Docker, например, FROM, RUN, COPY.
Аргументы — условия, которые нужно соблюсти для выполнения инструкции.
Инструкции по стандарту прописываются заглавными буквами для улучшения читаемости, но к регистру они нечувствительны.
За порядком в инструкции нужно следить, поскольку Docker послойно создает образ. Если в Dockerfile появились изменения, нужно пересобрать слой, который они затронули и все последующие слои. Чтобы делать это реже, Dockerfile необходимо структурировать логически, от базового образа переходя к настройке окружения, затем к запуску приложения. Типичная структура обычно выглядит так:
FROM — базовый образ;
ENV/ARG — переменные окружения и аргументы сборки;
RUN — установка пакетов и зависимостей;
COPY/ADD — добавление файлов в образ;
WORKDIR — рабочая директория;
CMD/ENTRYPOINT — запуск контейнера.
Длинные инструкции могут делиться на несколько строк с помощью символа \. Пример такого описания:
Для пояснения шагов сборки используются комментарии, которые в Dockerfile начинаются с символа #. Пример:
Основные инструкции Dockerfile с примерами
Недостаточно просто знать инструкции — нужно уметь ими пользоваться с учетом нюансов. Примеры и советы:
Инструкция | Назначение | Пример применения | Лучшие практики |
FROM | Задает родительский образ для нового Docker-образа | FROM ubuntu:22.04FROM python:3.11-slimFROM alpine:latest | Указывайте конкретные теги (например, python:3.11-slim). Alpine (~5 MB) минимален, но несовместим с некоторыми glibc-бинарными файлами. Slim-версии (~30-70 MB) — баланс для продакшен. Для Python с native-расширениями используйте многоэтапную сборку. |
WORKDIR | Устанавливает рабочую директорию внутри контейнера | WORKDIR /app | Если директории нет, Docker автоматически ее создаст. Все последующие команды (RUN, COPY, CMD) будут выполняться с привязкой к этой директории |
COPY | Копирует файлы и директории из контекста сборки в контейнер | COPY src/ /app/src/ | Простая и предсказуемая инструкция, которая позволяет получить ожидаемый результат |
ADD | Расширенная версия инструкции COPY | ADD app.tar.gz /app/ | Помимо копирования, инструкция обеспечивает распаковку архивов и загрузку файлов по URL. Для простого копирования лучше применять COPY |
RUN | Запускает команды внутри контейнера и создает новые слои образа | RUN apt-get update && apt-get install -y python3 | Каждая инструкция RUN создает новый слой. Чтобы уменьшить размер итогового образа, объединяйте несколько команд в одну строку |
CMD | Задает команду с аргументами, которая будет выполняться при запуске контейнера | CMD ["python", "app.py"] | Можно переопределить эту инструкцию при запуске контейнера |
ENTRYPOINT | Задает неизменяемую команду, которая будет всегда выполняться при запуске контейнера | ENTRYPOINT ["python"] | Часто применяется вместе с CMD — инструкцией, которая передает аргументы |
ENV | Задает переменные окружения внутри контейнера | ENV NODE_ENV=production | Заданные инструкцией переменные будут доступны в процессе выполнения контейнера. Это нужно для конфигурации приложения без изменения образа |
ARG | Определяет переменные, которые будут передаваться в процессе сборки образа | ARG VERSION=latest | Значения передаются при сборке и недоступны во время работы контейнера |
Процесс сборки: команда docker build
Для сборки Docker-образа по инструкциям из Dockerfile выполняется с помощью команды docker build. Рассказываем, как происходит процесс.
Синтаксис и ключевые флаги команды
Базовый синтаксис выглядит так:
Благодаря этой команде Docker начинает собирать образ с использованием инструкций из Dockerfile и контекста, который описан в текущей директории. Для пояснения процесса используются флаги:
-t (tag) — позволяет в формате name:tag задать имя и тег образа. Это нужно для идентификации образа, чтобы ссылаться на него при запуске контейнеров или публикации.
-f (file) — указывает путь к Dockerfile, если он имеет другое название или находится не в той директории, которая рекомендована по умолчанию. Контекст сборки нужно указать отдельно.
--no-cache — отменяет использование кэша при сборке образа. Docker будет заново выполнять все инструкции, даже если раньше создавал такие же слои. Флаг применяется, когда нужна «чистая» сборка.
--target — позволяет на определенном этапе останавливать сборку. Применяется в многоэтапных процессах создания образов, чтобы было удобнее осуществлять отладку.
Флаги добавляются в базовый синтаксис команды. Пример:
Здесь указывается путь к Dockerfile, задаются имя и тег образа.
Создание контейнераКонтекст сборки — что такое точка (.)
В команде docker build (.) означает контекст сборки — директорию, содержимое которой получает Docker-демон. Учитывайте, что будет передан весь контекст, а не только файлы, явно указанные в Dockerfile.
Чтобы исключить лишнее, используйте .dockerignore. Например, можно сделать так, что не будут передаваться временные файлы, node_modules, каталоги сборки и .git. Исключения уменьшат размер контекста, снизят нагрузку на систему и ускорят сборку.
Кэширование слоев: как ускорить повторную сборку
Напоминаем, что каждая инструкция Dockerfile создает отдельный слой, который можно повторно использовать при сборке новых образов. В этом поможет механизм кеширования. Его можно применить, если инструкции, входные данные и предыдущие слои не менялись.
Если Docker в процессе видит изменение, кеш для этого слоя и всех последующих сбрасывается. В этом случае слои пересобираются заново.
Оптимизация Docker-образов: уменьшаем размер, повышаем безопасность
Когда процесс сборки образов отлажен, необходимо перейти к оптимизации. Она помогает уменьшить размер образа для быстрой загрузки и запуска, убрать ненужные вводные. Разбираемся, какие приемы можно применять.
Использование многоэтапной сборки (multi-stage build)
Многоэтапная сборка позволяет объединить несколько этапов в одном Dockerfile. Идея в том, чтобы разделить этапы сборки с полным SDK и запуск приложения с минимальным runtime.
Пример для Go:
Go-приложение компилируется в статический бинарный файл, который может работать даже на scratch (пустом образе).
Пример для Java:
Для Java на этапе сборки используется JDK (Java Development Kit), на этапе запуска — JRE (Java Runtime Environment). JRE все еще необходим, но исключение инструментов компиляции уменьшает размер образа и поверхность атак.
Получается, что в финальном варианте не будет исходного кода, компиляторов и инструментов сборки. Это снижает количество уязвимостей безопасности, уменьшает размер образа и упрощает поддержку.
Multi-stage buildВыбор минимального базового образа
Состав и размер образа будут влиять на параметры и поведение контейнера. Например, образ ubuntu — около 70 MB, alpine — около 5 MB.
К преимуществам минимального базового образа Alpine можно отнести небольшое количество установленных пакетов, малую поверхность для потенциальных атак, быструю загрузку и распространение. Однако есть и минусы. Это возможная несовместимость с некоторыми бинарными файлами, недоступность части пакетов и инструментов «из коробки» и сложная отладка из-за минималистичного окружения.
Минимальный базовый образ стоит выбирать, если в приоритете безопасность и быстрое распространение.
Очистка кеша менеджеров пакетов в одном RUN
Очистку временных файлов и установка пакетов иногда объединяют в одну инструкцию RUN. Каждый RUN создает новый слой, поэтому если очистка выполняется отдельно, кэш остается в предыдущем слое. Логично объединить операции, в чем поможет такой шаблон:
Объединение позволяет уменьшить размер образа, избавиться от кеш менеджера пакетов в слоях и вести сборку в соответствии с лучшими практиками Docker.
Файл .dockerignore: исключение ненужных файлов из контекста
Мы уже упоминали файл .dockerignore, который позволяет исключать все лишнее из контекста сборки. Пример типичного .dockerignore:
Почему .dockerignore критически важен:
Безопасность: исключает случайную передачу секретов (.env, ключи API, сертификаты) в образ.
Производительность: уменьшает размер контекста сборки, ускоряет передачу Docker-демону.
Предсказуемость: предотвращает попадание в образ файлов, которые могут конфликтовать с процессом сборки.
Изоляция: гарантирует, что образ содержит только необходимые файлы, не включая артефакты разработки.
Файл .dockerignore работает на уровне клиента Docker, исключая файлы до отправки демону. Это не защита от утечки секретов в самом образе — проверяйте, что секреты не копируются через COPY/ADD.
Файл .dockerignoreПрактические примеры: собираем образ веб-приложения
Чтобы разобраться в применении Dockerfile, можно изучить примеры файлов для Python и Node.js. Предлагаем варианты, которые полностью иллюстрирует реальную структуру продакшен-образов.
Пример Dockerfile для Python/Django приложения
Dockerfile для таких приложений обычно включает выбор базового образа, копирование кода, установку зависимостей и команду запуска. Как может выглядеть структура:
За основу взят официальный минималистичный образ python. Отключается создание .pyc файлов и буферизация вывода. Для оптимизации кэширования сначала копируется requirements.txt и устанавливаются зависимости, после чего копируется исходный код. Команду запуска Django-сервера задает CMD.
Пример Dockerfile для Node.js приложения
Для Node.js рекомендовано отдельно устанавливать в зависимости и применять многоэтапную воспроизводимую сборку с помощью команды npm ci. Как может выглядеть структура Dockerfile:
Используется полный базовый образ Node.js. для уменьшения размера, финальный основан на node:alpine. Установка зависимости выполняется с помощью npm ci.
Сборка и проверка образа, запуск контейнера
Согласно готовому Dockerfile, команды выполняются в стандартной последовательности. Сначала идет сборка образа:
Docker создает образ с тегом, используя в качестве контекста сборки текущую директорию. Затем следует проверка:
После выполнения команды выводится список локальных образов с именами, тегами, размерами и уникальными идентификаторами. Если все в порядке, контейнер запускается:
Docker на основе образа запускает контейнер и пробрасывает на хост-систему порт приложения.
Готовые образы можно хранить в сервисе Evolution Artifact Registry от Cloud.ru. При загрузке они проверяются на наличие уязвимостей. Для локальной проверки образов на уязвимости используйте Docker Scout:
Продвинутые техники и лучшие практики
У Dockerfile и инструментов сборки есть продвинутые возможности, которые позволяют повысить безопасность и управляемость контейнеров в продакшен-среде. Делимся популярными методиками.
Использование HEALTHCHECK
Механизм HEALTHCHECK позволяет Docker определять работоспособность контейнера — выявлять состояния healthy или unhealthy.
Как это работает:
Важные особенности:
Docker только маркирует контейнер как healthy/unhealthy, но не перезапускает его автоматически.
В Docker Swarm HEALTHCHECK используется при развертывании и обновлении сервисов: unhealthy задачи могут быть заменены новыми в процессе rollout. Для автоматического восстановления отдельных контейнеров в Swarm используйте --restart-condition any или --restart-condition on-failure при создании сервиса. HEALTHCHECK в Swarm влияет на поведение при обновлениях, но не является единственным механизмом восстановления.
В Kubernetes HEALTHCHECK не используется (начиная с версии 1.8 он отключен). Для Kubernetes используйте livenessProbe и readinessProbe:
Где применять:
standalone Docker-контейнеры с ручным управлением;
Docker Swarm для автоматического восстановления;
локальная разработка и отладка.
Где НЕ применять:
Kubernetes (используйте встроенные probes);
если нужен только мониторинг без автоматического восстановления.
Если вы используете Kubernetes, HEALTHCHECK в Dockerfile игнорируется (начиная с версии 1.8 и во всех последующих версиях, включая актуальные). Вместо этого используйте встроенные механизмы Kubernetes:
HEALTHCHECK остается актуальным для standalone контейнеров, Docker Compose (особенно с флагом --wait) и Docker Swarm. В Docker Compose HEALTHCHECK позволяет сервисам ожидать готовности зависимостей перед запуском.
Пример использования в Docker Compose:
Настройка пользователя (USER)
По умолчанию процессы в контейнере запускаются от имени пользователя root, что создает риски безопасности. Рекомендуется запускать процессы от имени непривилегированного пользователя.
Вариант 1 (рекомендуемый): создать выделенного пользователя в Dockerfile:
Вариант 2: использовать системного пользователя nobody:
Однако учтите ограничения: nobody не имеет домашней директории и может не иметь прав на запись в некоторые директории. Это подходит только для приложений, которым не требуется запись в файловую систему.
Никогда не запускайте продакшен-контейнеры от root. Использование непривилегированного пользователя — обязательная практика безопасности.
Версионирование образов и теги
Чтобы идентифицировать разные версии одного образа, Docker используют теги. Распространенные схемы тегирования:
myapp:latest — обозначает последнюю сборку, категорически не рекомендуется для продакшен;
myapp:1.2.3 — семантическое версионирование, обеспечивает детерминированные деплои;
myapp:sha-abc123 — тегирование по хешу коммита, позволяет точно соотнести образ с версией кода;
myapp:1.2.3-abc123 — комбинация версии и хеша для максимальной воспроизводимости.
Почему latest опасен для продакшен:
невозможно гарантировать, что на staging и production развернут один и тот же образ;
откат на предыдущую версию становится недетерминированным;
нарушается принцип воспроизводимости инфраструктуры как кода (IaC).
Всегда используйте фиксированные теги в продакшен. latest допустим только для разработки и CI-сборок.
Многие используют первый вариант, но для продакшена он не рекомендуется. Не обязательно зацикливаться на myapp:1.2.3, но это схема предпочтительнее.
Сборка для разных архитектур (buildx)
Современные приложения в основном разворачиваются на разных архитектурах процессоров — amd64 и arm64. Для этих случаев в Docker есть docker buildx — расширение docker build.
Инструмент поддерживает мультиплатформенную сборку. Это значит, что с его помощью можно собрать один образ для нескольких архитектур и опубликовать его как multi-arch. При загрузке Docker автоматически определит подходящую архитектуру. Эта функция актуальна для современных CI/CD пайплайнов и облачных платформ.
Для мультиплатформенной сборки (особенно arm64 на amd64 или наоборот) требуется предварительно зарегистрировать обработчики QEMU:
Без этого сборка для других архитектур будет недоступна или приведет к ошибкам. Также необходимо создать и использовать билдер, поддерживающий мультиплатформенность:
Пример сборки multi-arch образа:
Отладка сборки и решение проблем
При работе с образами даже у опытных пользователей возникают неполадки и ошибки. Главное — быстро определить проблему и найти решение в соответствии с рекомендациями разработчиков Docker.
Пошаговая отладка сборки
Чтобы избежать ошибок на старте, можно проанализировать весь процесс сборки образа. В этом поможет команда:
Флаг --progress=plain позволяет в текстовом формате подробно вывести процесс сборки и проверить выполнение инструкций Dockerfile без агрегирования логов.
Чтобы поэтапно восстановить процесс сборки, можно применять временное комментирование инструкций в Dockerfile. Комментарии нужно оставлять с символом #. Можно помечать проблемные шаги или вносить уточнения по конкретным этапам работы с образом.
Анализ размера образа
Важный этап отладки — понять, какие инструкции и слои влияют на размер образа. Можно воспользоваться командой:
Чтобы ее выполнить, укажите имя или идентификатор образа, например:
Команда выдаст все слои образа, их размеры и инструкции для создания. Можно увидеть, на каком шаге получаются «тяжелые» слои.
Для детального анализа образов и оптимизации часто применяется инструмент dive. Он позволяет посмотреть содержимое каждого слоя, оценить инструкции Dockerfile и увидеть все примененные изменения.
Частые ошибки новичков
У начинающих пользователей в основном возникают одни и те же проблемы и ошибки. Вот самые распространенные:
Проблемы с путями. Если неправильно указать относительные и абсолютные пути в инструкциях COPY и ADD, в сборке будет ошибка. Во избежание укажите пути с привязкой контексту сборки, а не местоположению Dockerfile.
Неправильное использование CMD. Если в одном Dockerfile несколько инструкций CMD, выполняться будет только последняя. Прописывайте только одну.
Отсутствие .dockerignore. Если проигнорировать или неправильно настроить файл для исключения, в контекст сборки будут переданы лишние элементы. Пропишите исключения сразу.
Чтобы работать без ошибок, изучите документацию Docker и используйте рекомендуемые подходы.
Следующие шаги: что делать с собранным образом
После сборки Docker-образа начинается работа с контейнером. Подсказываем основные этапы.
Запуск контейнера: docker run с параметрами
Для запуска контейнера на основе собранного образа применяется команда docker run. Она включает несколько параметров:
назначение имени контейнера;
передачу переменных окружения;
проброс портов;
запуск приложения в фоновом режиме.
Примеры применения команды:
docker run myapp:1.0 — запуск с настройками по умолчанию;
docker run -p 8000:8000 myapp:1.0 — запуск с пробросом портов;
docker run -d -p 8000:8000 myapp:1.0 — запуск приложения в фоновом режиме;
docker run -d --name myapp_container -p 8000:8000 myapp:1.0 — запуск с назначением имени контейнера.
Параметры в команде позволяют управлять контейнером без пересоздания образа. Готовые образы можно использовать в разных средах.
Публикация в реестр: Docker Hub, GitLab Registry, AWS ECR
Чтобы использовать одни и те же образы на разных машинах, их нужно опубликовать в публичном или приватном реестре контейнеров.
Сначала нужно выполнить тегирование с помощью команды docker tag. На этом этапе образа появится имя, которое отвечает формату реестра. Для публикации применяется команда docker push.
Этот способ подходит для Docker Hub, GitLab Container Registry и AWS Elastic Container Registry.
Интеграция в CI/CD пайплайн
Один из сценариев использования контейнеров Docker — интеграция в системе непрерывной интеграции и доставки кода. Типичная схема CI/CD пайплайна выглядит так:
сборка образа на основе Dockerfile;
тестирование приложения внутри контейнера;
публикация образа в реестр;
деплой в целевую среду (сервер, кластер, оркестратор).
С помощью такого подхода обеспечивается единое окружение для тестирования и продакшен-среды.
Для управления контейнерными приложениями в продакшен собранные образы удобно разворачивать в управляемом Kubernetes-кластере. Evolution Managed Kubernetes предоставляет готовую инфраструктуру для оркестрации контейнеров: автоматическое масштабирование, маркетплейс плагинов для мониторинга и безопасности, а также гибкую настройку мастер- и рабочих узлов. Это позволяет сосредоточиться на разработке, а не на управлении контрольной плоскостью кластера.
Заключение
Dockerfile напрямую влияет не только на удобство и предсказуемость сборки, но и на безопасность контейнерного приложения. Для снижения рисков откажитесь от лишних зависимостей, минимизируйте размер образов, запускайте процессы от имени обычных системных пользователей вместо привилегированных. Стоит разобраться в структуре образов и слоев, чтобы на раннем этапе выявить уязвимые места. Такой подход используется при проектировании корпоративных систем и в продакшен-средах.

