Устранение ошибок сборки Docker: подробное руководство по поиску и исправлению неисправностей

Отладка ошибок сборки Docker, вызванных неправильными путями, отсутствующими пакетами, неожиданностями кэша, сетевыми проблемами, правами доступа или нехваткой дискового пространства.

Устранение ошибок сборки Docker: подробное руководство по поиску и исправлению неисправностей

Ошибки сборки Docker легче исправлять, если рассматривать вывод сборки как стенограмму. Docker сообщает, на каком шаге произошла ошибка, какая команда выполнялась и что она вывела. Полезная работа заключается в поиске первой настоящей ошибки, а не последней строки, сообщающей о сбое сборки.

Запускайте сборку с обычным прогрессом, когда стандартный вывод скрывает слишком много:

docker build --progress=plain -t my-app:debug .

Ищите номер шага, на котором произошла ошибка, например #8, и инструкцию рядом с ним. Если ошибочная инструкция — COPY, вероятно, у вас проблема с контекстом сборки или путём. Если это RUN apt-get install, у вас проблема с пакетом, сетью, репозиторием или архитектурой. Если это RUN npm ci или pip install, прочитайте ошибку менеджера пакетов, прежде чем изменять настройки Docker.

COPY failed: файл отсутствует в контексте сборки

Одна из самых распространённых ошибок сборки также является одной из самых простых:

COPY failed: file not found in build context or excluded by .dockerignore

Docker может копировать только файлы, находящиеся внутри контекста сборки, который обычно является последним аргументом docker build:

docker build -t my-app .

Здесь . — это контекст. Dockerfile, находящийся в подкаталоге, не может скопировать ../secret.txt извне этого контекста. Docker намеренно блокирует это, поскольку сборки должны быть воспроизводимы из своего контекста.

Проверьте три вещи:

pwd
ls -la
docker build --progress=plain -f path/to/Dockerfile .

Если ваш Dockerfile находится в docker/Dockerfile, а приложение — в корне репозитория, выполняйте сборку из корня и указывайте на Dockerfile с помощью -f:

docker build -f docker/Dockerfile -t my-app .

Также проверьте .dockerignore. Он может исключать файл, который вы пытаетесь скопировать. Это часто происходит с dist, target, .env или сгенерированными файлами. Если Dockerfile ожидает файл, не игнорируйте его, если только файл не создаётся внутри сборки.

Ошибки установки пакетов

Ошибки менеджера пакетов обычно делятся на несколько категорий: устаревшие индексы пакетов, неправильные имена пакетов, отсутствующие репозитории, сетевые проблемы или использование команд из неправильного дистрибутива Linux.

Это не сработает на Alpine, потому что Alpine не использует apt-get:

FROM alpine:3.20
RUN apt-get update && apt-get install -y curl

Используйте менеджер пакетов для базового образа:

FROM alpine:3.20
RUN apk add --no-cache curl

Для образов Debian или Ubuntu объединяйте обновление и установку в одном слое:

RUN apt-get update &&     apt-get install -y --no-install-recommends curl ca-certificates &&     rm -rf /var/lib/apt/lists/*

Если apt-get install сообщает, что не может найти пакет, проверьте имя пакета для этой версии дистрибутива. Имена пакетов различаются между Debian, Ubuntu, Alpine, Fedora и языковыми образами. В минимальных образах также могут отсутствовать инструменты, которые вы считаете присутствующими, такие как bash, curl, git, tar или ca-certificates.

Если загрузка по HTTPS завершается ошибками сертификата, установите сертификаты CA перед использованием curl, wget, git по HTTPS, npm, pip или языковых менеджеров пакетов:

RUN apt-get update &&     apt-get install -y --no-install-recommends ca-certificates &&     rm -rf /var/lib/apt/lists/*

Неожиданности кэша

Кэш Docker обычно полезен, но он может сделать сломанную сборку непоследовательной. Если вы подозреваете устаревший кэш, выполните:

docker build --no-cache --progress=plain -t my-app:debug .

Если сборка не удаётся только без кэша, возможно, вы полагались на старые слои. Если она не удаётся только с кэшем, проверьте, не изменился ли сгенерированный файл или файл блокировки зависимостей таким образом, что Docker не видит этого там, где вы ожидаете.

Для установки зависимостей копируйте файлы блокировки перед полным деревом исходного кода:

WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .

Для Python:

WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

Этот шаблон не только быстрее. Он делает ошибки сборки более понятными, поскольку установка зависимостей зависит от файлов зависимостей, а не от каждого изменения исходного кода.

Сетевые ошибки и ошибки реестра

Сборка может завершиться ошибкой до выполнения вашего Dockerfile, если Docker не может извлечь базовый образ:

docker pull python:3.12-slim

Если это не удаётся, сначала исправьте доступ к реестру. Проверьте аутентификацию для частных реестров, настройки корпоративного прокси, DNS и правила брандмауэра. За прокси-сервером демону Docker требуется конфигурация прокси; установка HTTP_PROXY только в вашей интерактивной оболочке может быть недостаточной.

Для загрузок внутри шагов RUN проверьте URL с хоста, а затем из временного контейнера на том же сетевом пути:

curl -I https://example.com/file.tar.gz
docker run --rm curlimages/curl -I https://example.com/file.tar.gz

По возможности не полагайтесь на непривязанные удалённые скрипты. Dockerfile, который выполняет curl install.sh из движущейся ветки, может сломаться, потому что удалённый скрипт изменился. Предпочитайте версионные загрузки и проверку контрольных сумм для бинарных файлов:

RUN curl -fsSLo tool.tar.gz https://example.com/tool-1.2.3-linux-amd64.tar.gz &&     echo '<sha256>  tool.tar.gz' | sha256sum -c - &&     tar -xzf tool.tar.gz -C /usr/local/bin &&     rm tool.tar.gz

Замените <sha256> на реальную контрольную сумму со страницы релиза проекта.

Несоответствие архитектур

На Apple Silicon или в смешанных парках CI ошибки сборки могут быть вызваны архитектурой. Образ или загруженный бинарный файл может быть amd64, в то время как сборщик — arm64, или наоборот. Симптомы включают exec format error, отсутствие пакетов для архитектуры или бинарные файлы, которые выдают ошибку во время сборки.

Проверьте ваш хост и цель:

docker version
docker buildx ls

При необходимости выполняйте сборку для конкретной платформы:

docker buildx build --platform linux/amd64 -t my-app:amd64 .

Будьте осторожны: кроссплатформенные сборки могут быть медленнее при использовании эмуляции. Для CI собственные сборщики для каждой платформы часто быстрее и менее неожиданны.

Ошибки прав доступа во время сборки

Права доступа в сборках выходят из строя, когда файлы копируются с неожиданным владельцем, скрипты не являются исполняемыми или Dockerfile переключается на пользователя без полномочий root до завершения настройки.

Если скрипт завершается ошибкой permission denied, проверьте его, прежде чем копировать предположения в Dockerfile:

ls -l scripts/start.sh

Затем исправьте это либо в git, либо в образе:

COPY scripts/start.sh /usr/local/bin/start.sh
RUN chmod +x /usr/local/bin/start.sh

Если вы используете пользователя без полномочий root для выполнения, создайте каталоги и установите владельца перед переключением пользователей:

RUN useradd -r -u 10001 appuser && mkdir -p /app/data && chown -R appuser:appuser /app
USER appuser

COPY --chown=appuser:appuser . . часто чище, чем копирование от root и последующее выполнение широкого рекурсивного chown.

Дисковое пространство и очистка кэша сборки

Большие сборки могут завершиться ошибкой из-за нехватки дискового пространства на хосте Docker. Проверьте использование Docker:

docker system df

При необходимости удалите неиспользуемый кэш сборки:

docker builder prune

Будьте осторожнее с широкими командами очистки. docker system prune -a удаляет неиспользуемые образы, что может привести к большим повторным загрузкам или нарушить рабочие процессы, полагающиеся на локальные образы. Используйте её, когда понимаете последствия.

Если сборки регулярно заполняют диск, лучшим исправлением обычно являются меньшие контексты сборки, многоэтапные сборки и отказ от огромных временных файлов в слоях. Очищайте временные артефакты в той же инструкции RUN, которая их создаёт.

Отладка шага RUN в интерактивном режиме

Когда длинная строка RUN завершается ошибкой, временно разделите её:

RUN apt-get update
RUN apt-get install -y --no-install-recommends packageA packageB
RUN some-command-that-fails

Как только вы найдёте ошибочную команду, вы можете снова объединить связанные команды для более чистого образа.

Ещё один полезный трюк — остановиться на известном рабочем этапе. Если ваш Dockerfile имеет этапы, соберите одну цель:

docker build --target builder -t my-app-builder .
docker run --rm -it my-app-builder sh

Оттуда вы можете проверить файлы, выполнить ошибочную команду вручную, проверить переменные окружения и увидеть, что на самом деле содержит файловая система.

Для образов без оболочки добавьте временный этап отладки, а не загрязняйте рабочий образ:

FROM builder AS debug
RUN apt-get update && apt-get install -y --no-install-recommends bash curl

Соберите --target debug, проведите расследование, затем удалите или проигнорируйте цель отладки, когда закончите.

Делайте сборки предсказуемыми

Надёжная сборка Docker скучна в лучшем смысле. Используйте версионные базовые образы. Храните файлы блокировки зависимостей в системе контроля версий. Избегайте latest в рабочих Dockerfile, если только ваш процесс намеренно не пересобирает и не тестирует с движущимися тегами. Держите .dockerignore плотным. Делайте сетевые загрузки версионными и проверенными. Размещайте часто изменяющийся исходный код после установки зависимостей.

Когда сборка не удаётся, не переписывайте весь Dockerfile сразу. Определите ошибочную инструкцию, воспроизведите с обычными журналами, изолируйте команду и исправьте наименьшую реальную причину. Этот подход быстрее, и он оставляет вам Dockerfile, который сможет понять следующий человек.