Что делает docker build и когда её использовать
Команда docker build собирает образ из двух частей: файла Dockerfile с инструкциями и контекста сборки, то есть набора файлов, доступных сборщику. Она читает Dockerfile сверху вниз, выполняет каждую инструкцию в отдельном слое и сохраняет результат в локальном хранилище образов. В типовой установке Docker по умолчанию работает сборщик BuildKit: он параллелит независимые шаги, аккуратнее использует кэш и печатает компактный лог.
Запуск выглядит так:
docker build -t myapp:latest .
Точка в конце - это контекст сборки, текущая директория. Всё, что лежит в ней, отправляется на демон Docker и может попасть в образ через COPY или ADD. Имя и тег задаёт флаг -t. После успешной сборки образ появляется в списке docker images под этим тегом.
Минимальный Dockerfile и первая сборка
Самый короткий рабочий пример из трёх инструкций:
FROM ubuntu:22.04 RUN apt-get update && apt-get install -y curl CMD ["bash"]
Разбор по строкам:
- FROM ubuntu:22.04 задаёт базовый образ и наследует его файлы и настройки. Без FROM не обходится ни один Dockerfile - как Docker описывает наследование базового образа.
- RUN выполняет команду в слое поверх базового образа. Здесь мы обновляем индексы пакетов и ставим curl.
- CMD задаёт команду по умолчанию, которая запускается при старте контейнера.
Сохраните файл как Dockerfile в текущей директории и соберите образ:
docker build -t myapp:latest .
После завершения проверьте результат командой docker images. В списке появится строка с репозиторием myapp, тегом latest, идентификатором IMAGE ID, временем создания и размером.
Контекст сборки: что попадает в образ
docker build отправляет демону весь контекст, а не только Dockerfile. Если в директории лежат node_modules, .git или .env с секретами, они уйдут в сборку, и COPY сможет их прочитать. Отсекает их не .gitignore, а .dockerignore.
Создайте .dockerignore рядом с Dockerfile:
.git node_modules .env *.log
Каждая строка - шаблон для исключения. Файлы и папки из .dockerignore не отправляются в контекст, поэтому COPY и ADD их просто не видят. Так вы одновременно ускоряете сборку (контекст меньше) и снижаете риск утечки секретов в слой. Правило простое: если файл не нужен внутри образа, добавьте его в .dockerignore.
Ключевые параметры docker build: -t, -f, --build-arg, --no-cache
Четыре флага закрывают большинство ежедневных сценариев: имя образа, выбор Dockerfile, передачу переменных и управление кэшем. Разберём каждый с примером.
Как задать имя и тег через -t
Флаг -t (длинная форма --tag) присваивает образу имя в формате name:tag. Без явного тега Docker подставит latest.
docker build -t registry.example.com/team/myapp:1.0.0 .
Здесь registry.example.com/team/myapp - имя репозитория, 1.0.0 - тег. Тег участвует в docker run и docker push, поэтому задавайте осмысленные версии, а не складывайте всё в latest. Один образ можно тегировать несколько раз, повторно запуская сборку с другим -t.
Указание альтернативного Dockerfile через -f
Если в проекте несколько Dockerfile, например для dev и prod, выберите нужный флагом -f:
docker build -f docker/Dockerfile.prod -t myapp:prod .
Путь к Dockerfile указывается относительно контекста, а сам контекст передаётся целиком. Поэтому .dockerignore нужен и при -f: без него весь проект уйдёт на демон, даже когда собирается один prod-файл.
Передача переменных через --build-arg
Переменные, объявленные в Dockerfile через ARG, принимают значения из командной строки. В Dockerfile:
FROM ubuntu:22.04 ARG VERSION=dev RUN echo "build $VERSION"
Команда сборки:
docker build --build-arg VERSION=1.2 -t myapp .
ARG действует только на этапе сборки и не сохраняется в окружении контейнера. ENV, наоборот, попадает в образ и доступна процессу во время работы. Разница принципиальна при работе с секретами.
Не передавайте пароли, токены и ключи через --build-arg. Значение сохраняется в истории образа и видно в docker history, поэтому любой, у кого есть образ, прочитает секрет. Для чувствительных данных используйте BuildKit secrets: они монтируются на время шага и не оседают в слоях.
Отключение кэша через --no-cache
Docker кэширует каждый слой и при повторной сборке переиспользует их, если инструкция и входные файлы не изменились. Иногда кэш мешает получить свежий результат, и сборку запускают заново:
docker build --no-cache -t myapp .
Флаг отключает кэш для всех шагов, поэтому сборка идёт дольше. Точечно сбросить кэш можно, изменив саму инструкцию или обновив базовый образ флагом --pull. Применяйте --no-cache осознанно: когда подозреваете, что слой устарел, или собираете релиз с нуля.
Как читать вывод сборщика и находить ошибки
Лог сборки читается сверху вниз и заканчивается либо идентификатором нового образа, либо сообщением об ошибке. Научитесь различать эти два финала, и диагностика займёт минуты.
Структура вывода BuildKit: шаги, слои, кэш
Каждый шаг соответствует одной инструкции Dockerfile. BuildKit помечает статусы:
- CACHED - слой взят из предыдущей сборки, шаг не выполнялся.
- DONE - шаг выполнен, слой создан.
- ERROR - шаг завершился с ошибкой, сборка остановлена.
Пример фрагмента лога:
=> [2/3] RUN apt-get update && apt-get install -y curl => CACHED [3/3] CMD ["bash"] => exporting to image => => naming to docker.io/library/myapp:latest
CACHED на шаге RUN означает, что команда не запускалась: BuildKit нашёл готовый слой. Чтобы принудительно пересобрать, используйте --no-cache. Независимые шаги BuildKit выполняет параллельно, поэтому строки в логе могут идти не в том порядке, что инструкции в Dockerfile.
Типичные ошибки и как их исправлять
COPY failed: file not found. Путь в COPY не совпадает с расположением файла в контексте либо файл исключён через .dockerignore. Проверьте относительный путь и список исключений.
The command '/bin/sh -c ...' returned a non-zero code. Ошибка внутри RUN. Запустите команду вручную в контейнере на базовом образе, чтобы увидеть полный вывод:
docker run --rm -it ubuntu:22.04 bash
failed to solve: process "/bin/sh -c ..." did not complete successfully. Общая формулировка BuildKit, причина всегда в строках выше. Смотрите на последний ERROR и вывод команды под ним.
Ошибка при --build-arg. ARG в Dockerfile не объявлена либо в имени аргумента опечатка. Docker молча пропустит неизвестный аргумент, и значение не подставится.
Алгоритм: найдите шаг со статусом ERROR, откройте соответствующую инструкцию Dockerfile, воспроизведите команду вручную в контейнере базового образа. Так вы отделяете ошибку в самой команде от проблемы с контекстом или правами.
Проверка результата: docker images и запуск контейнера
Сборка без ошибок ещё не доказывает, что образ корректен. Проверьте наличие, тег, а затем запустите контейнер в изолированной среде.
Как проверить образ через docker images
Команда выводит таблицу с колонками REPOSITORY, TAG, IMAGE ID, CREATED, SIZE. Формат вывода:
| REPOSITORY | TAG | IMAGE ID | CREATED | SIZE |
|---|---|---|---|---|
| myapp | latest | a1b2c3d4e5f6 | 2 minutes ago | 142MB |
| ubuntu | 22.04 | f1e2d3c4b5a6 | 3 weeks ago | 77.9MB |
Сверьте тег и размер. Большой размер часто указывает на лишние слои или неочищенный кэш пакетов. Детали по образу покажет docker image inspect: конфигурацию, переменные окружения, точку входа и слои. Разбор слоёв и поиск «лишнего веса» с dive и inspect мы описали в отдельном руководстве. Если образа в списке нет, проверьте, не перезаписал ли тег другой запуск сборки.
Безопасный запуск контейнера из собранного образа
Быстрая проверка в интерактивном режиме:
docker run --rm -it myapp:latest bash
Флаг --rm удаляет контейнер после выхода, -it даёт интерактивный терминал. Такой запуск не публикует порты и не трогает файловую систему хоста, поэтому прод не пострадает.
Для веб-приложения опубликуйте порт и проверьте ответ:
docker run -d -p 8080:80 myapp:latest curl -I http://localhost:8080
Осторожнее с флагом -v: смонтированный с хоста каталог контейнер может изменить или удалить. Публикация портов открывает сервис наружу, поэтому не выставляйте тестовые контейнеры в интернет. В продакшене запускайте процесс от непривилегированного пользователя, а не от root.
Практические рекомендации и чек-лист перед сборкой
Проходите по короткому списку перед каждой сборкой. Это дешевле, чем разбираться с последствиями на проде.
Безопасность сборки: секреты и непривилегированный пользователь
Секреты не передавайте через --build-arg: значения остаются в истории образа. Для паролей и токенов используйте BuildKit secrets, они не сохраняются в слоях.
Создайте отдельного пользователя и переключитесь на него перед запуском приложения:
FROM ubuntu:22.04 RUN useradd -m -s /bin/bash agent USER agent CMD ["bash"]
Инструкция USER agent переключает контекст, и процесс контейнера идёт без прав root. Похожий подход применяют в sandbox-образах Docker: они основаны на Ubuntu и работают от непривилегированного пользователя agent с доступом sudo - как это устроено в базовых образах Docker. При установке системных пакетов сначала работают от root, а затем возвращаются к agent, иначе файлы окажутся в /root/, куда пользователь не имеет доступа.
Запуск от root расширяет поверхность атаки: при компрометации контейнера процесс получит больше возможностей внутри него. Готовый чек-лист с командами для защиты контейнеров, включая сканирование образов и запуск без root, собран в материале по безопасности Docker-контейнеров.
Воспроизводимость и кэширование
Фиксируйте версии базовых образов и пакетов. FROM ubuntu:22.04 даёт предсказуемый результат, а FROM ubuntu:latest в один день может принести новую версию и сломать сборку.
Порядок инструкций влияет на кэш. Сначала копируйте файлы зависимостей, ставьте зависимости, и только потом копируйте код:
COPY package.json package-lock.json ./ RUN npm ci COPY . .
При правке кода слой с npm ci берётся из кэша, и сборка остаётся быстрой. Если поменять порядок, каждая правка кода потянет за собой переустановку зависимостей.
Флаг --pull заставляет заново скачать базовый образ, что помогает не пропустить обновление безопасности. Для уменьшения размера используйте многоэтапную сборку: компилятор и исходники остаются в промежуточном этапе, а в финальный образ копируется только результат. Пошаговый разбор многоэтапной сборки и работы с кэшем мы собрали в гайде по сборке Docker-образа.
Чек-лист перед сборкой:
- Создан .dockerignore с .git, .env, node_modules и логами.
- Тег задан через -t, версия осмысленна.
- Для нескольких Dockerfile используется -f.
- Секреты не передаются через --build-arg.
- При подозрении на устаревший слой применяется --no-cache.
- Результат проверен через docker images и docker image inspect.
- Тестовый контейнер запущен с --rm, лишние порты не опубликованы.
После проверки перенесите команды в CI/CD и зафиксируйте версии так, чтобы результат повторялся на любой машине. Переход от ручной сборки к воспроизводимой в dev, test и prod разобран в руководстве по воспроизводимым Docker-образам.