Устранение ошибок сборки 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, который сможет понять следующий человек.