Реестр образов перестал принимать push или pull. Ошибка 401 Unauthorized, connection refused или manifest unknown блокирует пайплайн сборки. Это руководство дает проверенный алгоритм для каждой типовой неисправности. Вы получите конкретные команды для диагностики и пошаговый чек-лист восстановления, который вернет реестр в строй за минимальное время.
Мы разберем три главных класса проблем: сбои аутентификации, сетевые ошибки и конфликты тегов. Для каждого случая указаны первопричина, способ проверки и команда для исправления. Финальный раздел содержит готовую последовательность действий при полной недоступности реестра. Материал написан для Docker Registry и Harbor, но принципы применимы к любому OCI-совместимому решению.
Типичные ошибки аутентификации и способы их исправления
Ошибки аутентификации - самая частая причина отказа при push и pull. Симптом всегда один: клиент получает HTTP 401 Unauthorized. Корень проблемы - отсутствие, истечение или повреждение токена доступа. Решение зависит от того, где именно произошел сбой: на стороне клиента Docker, в credential store или в конфигурации самого реестра.
Перед глубокой диагностикой выполните быструю проверку: откройте браузер или curl на эндпоинт /v2/ реестра. Если запрос возвращает 401 с заголовком Www-Authenticate, сервер жив и ждет учетные данные. Если ответ 200 - аутентификация не настроена, и проблема на стороне клиента.
Ошибка «unauthorized: authentication required» при push/pull
Эта ошибка почти всегда связана с отсутствием или истечением токена. Docker хранит токен в памяти демона после успешного docker login. Токен имеет ограниченный срок жизни, заданный в конфигурации реестра параметром token_expiration. После истечения срока любая операция возвращает 401.
Алгоритм исправления состоит из трех шагов.
- Принудительный выход:
docker logout registry.example.com. Команда удаляет сохраненный токен из памяти демона. - Повторный вход с явной передачей пароля через stdin:
echo "$REGISTRY_PASSWORD" | docker login registry.example.com --username myuser --password-stdin. Использование--password-stdinисключает попадание пароля в историю команд и логи процессов. - Проверка срока действия токена в конфигурации реестра. Для Docker Registry откройте файл
/etc/docker/registry/config.ymlи найдите блокauth.token. Значениеexpirationзадает время жизни в секундах. Для production-среды рекомендуется 3600 (1 час) - это баланс между безопасностью и удобством.
Если ошибка сохраняется после повторного логина, проверьте конфигурацию аутентификации на стороне сервера. Для htpasswd-аутентификации убедитесь, что файл паролей существует и доступен процессу реестра: ls -la /auth/htpasswd. Хеш пароля должен начинаться с префикса $2y$ (bcrypt).
Проблемы с сохраненными учетными данными в Docker credential store
Docker хранит учетные данные в файле ~/.docker/config.json. После смены пароля на стороне реестра старые сохраненные креды продолжают использоваться и вызывают 401. Ситуация усугубляется, когда включен внешний credential store - например, osxkeychain на macOS или secretservice на Linux.
Проверьте текущую конфигурацию: cat ~/.docker/config.json. Поле credsStore указывает на активный помощник. Поле auths содержит base64-закодированные пары логин:пароль для каждого реестра. Наличие устаревшей записи для проблемного реестра - прямое указание на причину сбоя.
Для принудительного сброса выполните два действия.
- Удалите секцию проблемного реестра из
authsв~/.docker/config.json. Можно отредактировать файл вручную или удалить конкретную запись командой:docker logout registry.example.com. - Если используется внешний credential store, очистите его. Для osxkeychain откройте «Связка ключей» и удалите запись реестра. Для secretservice выполните
secret-tool clear service docker-credential-secretservice.
После очистки выполните docker login заново. Docker запишет новые учетные данные в выбранный store.
Сетевые сбои при подключении к реестру: диагностика и решение
Сетевые ошибки - второй по частоте класс проблем. Симптомы разнообразны: dial tcp: connection refused, no such host, таймауты TLS-рукопожатия, ошибки проверки сертификата. Системный подход к диагностике описан в нашем руководстве по диагностике сетевых проблем в Docker и Kubernetes. Здесь мы сфокусируемся на специфике реестра.
Первое правило: всегда проверяйте связность на уровне TCP до того, как винить Docker. Клиент Docker может давать смазанные сообщения об ошибках, которые не указывают на истинную причину.
Ошибка «dial tcp: connection refused» или «no such host»
Ошибка connection refused означает, что порт реестра не принимает соединения. Либо сервис не запущен, либо порт блокирован файрволом. Ошибка no such host указывает на проблему DNS-резолвинга - клиент не может преобразовать имя реестра в IP-адрес.
Алгоритм изоляции проблемы.
- Проверьте DNS:
nslookup registry.example.com. Если ответа нет, добавьте запись в/etc/hostsдля теста:echo "192.168.1.100 registry.example.com" | sudo tee -a /etc/hosts. После теста удалите запись, чтобы не создавать скрытую зависимость. - Проверьте доступность порта:
telnet registry.example.com 443илиnc -zv registry.example.com 443. Если соединение не устанавливается, проблема на сетевом уровне. - Проверьте файрвол на стороне клиента:
sudo iptables -L -n | grep 443. Наличие правила DROP или REJECT для порта 443 требует добавления разрешающего правила. - Проверьте, что демон Docker использует правильный DNS. Контейнеры могут использовать внутренний резолвер, отличный от хостового. Проверьте
/etc/docker/daemon.jsonна наличие параметраdns.
Для production-сред критически важно настроить мониторинг доступности. Наше руководство по Docker в production содержит раздел о настройке health-check эндпоинтов и алертов в Prometheus.
Таймауты TLS-рукопожатия и ошибки сертификатов
Реестр работает, порт открыт, но Docker отказывается соединяться из-за проблем с сертификатом. Типичные сообщения: x509: certificate signed by unknown authority, x509: certificate has expired or is not yet valid. Docker требует полного доверия к сертификату реестра - самоподписанные сертификаты по умолчанию отклоняются.
Проверьте цепочку сертификатов: openssl s_client -connect registry.example.com:443 -showcerts. Вывод покажет всю цепочку от корневого до конечного сертификата. Обратите внимание на срок действия - даты notBefore и notAfter.
Для добавления доверия к самоподписанному или корпоративному CA выполните:
- Создайте директорию для сертификатов реестра:
sudo mkdir -p /etc/docker/certs.d/registry.example.com/. Имя директории должно точно совпадать с именем хоста реестра, включая порт, если он нестандартный. - Скопируйте корневой сертификат CA в эту директорию с именем
ca.crt:sudo cp root-ca.crt /etc/docker/certs.d/registry.example.com/ca.crt. - Перезапустите демон Docker:
sudo systemctl restart docker.
Параметр insecure-registries в daemon.json отключает проверку TLS полностью. Используйте его только для локальных тестов. В production это открывает вектор атаки man-in-the-middle.
Для HTTP-прокси проверьте переменные окружения демона Docker. Если реестр находится в локальной сети, он должен быть в списке NO_PROXY. Настройка выполняется через systemd unit-файл docker.service: добавьте Environment="NO_PROXY=localhost,127.0.0.1,registry.example.com" в секцию [Service].
Конфликты тегов и ошибки манифестов
Ошибки манифестов возникают, когда Docker не может найти образ по указанному тегу. Это случается при удалении тега, перезаписи манифеста или неверном указании имени. В production-средах такие ошибки часто вызваны отсутствием политики иммутабельности тегов.
Docker разрешает тег в манифест в момент pull. Если манифест удален или заменен, клиент получает ошибку. Тег latest - это просто соглашение, а не технический механизм. Полагаться на него в production нельзя.
Ошибка «manifest for registry.example.com/image:tag not found»
Тег либо не существует, либо был удален. Docker не различает эти два случая и выдает одинаковую ошибку. Для точной диагностики используйте REST API реестра напрямую.
Проверьте список образов: curl -s https://registry.example.com/v2/_catalog. Реестр вернет JSON со списком всех репозиториев. Затем запросите теги конкретного образа: curl -s https://registry.example.com/v2/myimage/tags/list. Если образ или тег отсутствует в выводе, он действительно удален.
Важное различие: удаление тега через API (DELETE /v2/myimage/manifests/mytag) удаляет только ссылку на манифест. Сам манифест и связанные с ним блобы остаются в хранилище до запуска garbage collection. Если манифест еще не удален физически, тег можно восстановить повторной отправкой того же манифеста: docker push registry.example.com/myimage:recovered-tag.
Если образ удален полностью, восстановление возможно только из резервной копии. Стратегия резервного копирования реестра должна быть частью плана аварийного восстановления.
Предотвращение перезаписи тегов: политики и best practices
Случайная перезапись тега - частая причина сбоев в production. Разработчик пушит новую версию с тем же тегом, и все pod'ы Kubernetes, использующие этот тег, получают обновление при следующем перезапуске. Результат - неожиданное поведение и трудноотлаживаемые инциденты.
Решение - включение иммутабельности тегов. В Harbor это настраивается на уровне проекта: установите флаг «Immutable tags» и задайте правило (например, теги, соответствующие паттерну v*.*.*, иммутабельны). В Docker Registry иммутабельность включается в конфигурации хранилища: параметр storage.maintenance.uploadpurging.enabled: true и storage.delete.enabled: false предотвращают удаление манифестов.
Для тегирования используйте семантическое версионирование (v1.2.3) или хеш коммита (sha-abc123). Тег latest допустим только для локальной разработки. В CI/CD пайплайне всегда указывайте точный тег. Это устраняет неопределенность и делает развертывание воспроизводимым.
Безопасность образов - смежная тема. После устранения конфликтов тегов убедитесь, что образы прошли сканирование уязвимостей. В руководстве по безопасности реестра образов описана интеграция Trivy и Clair в пайплайн.
Чек-лист быстрого восстановления работоспособности реестра
Реестр полностью недоступен. Пользователи не могут ни push, ни pull. Время простоя растет. Этот чек-лист - готовая последовательность действий для возврата сервиса в строй. Выполняйте шаги по порядку, от простого к сложному.
Шаг 1: Проверка состояния сервиса и потребления ресурсов
Начните с базового: жив ли процесс реестра и хватает ли ему ресурсов. Для контейнеризированного реестра выполните docker ps | grep registry. Отсутствие вывода означает, что контейнер упал. Проверьте причину: docker logs registry --tail 50.
Для systemd-сервиса: systemctl status docker-registry. Состояние active (running) - сервис жив. Состояние failed - смотрите логи: journalctl -u docker-registry -n 50.
Проверьте ресурсы хоста: top -bn1 | head -5 для CPU, free -h для памяти. Критически важно проверить свободное место в хранилище образов: df -h /var/lib/registry. Заполнение диска на 100% - частая причина падения реестра. Если место заканчивается, запустите garbage collection (шаг 3) или расширьте раздел.
Шаг 2: Анализ логов реестра и типовые ошибки
Включите debug-логирование для получения детальной информации. Для Docker Registry добавьте в конфигурацию:
log:
level: debug
formatter: json
Перезапустите реестр и воспроизведите проблему. В логах ищите ключевые паттерны:
token auth attempt- проблема аутентификации, проверьте связь с провайдером токенов.error allocating- нехватка памяти или дискового пространства.s3 timeout- проблема с бэкенд-хранилищем, проверьте доступность S3-эндпоинта.blob upload unknown- прерванная загрузка блоба, требуется очистка мусора.
Для Harbor логи находятся в директории /var/log/harbor/. Каждый компонент (core, registry, jobservice) пишет в отдельный файл. Начните с registry.log.
Шаг 3: Очистка мусора и восстановление целостности данных
Осиротевшие блобы - результат прерванных push-операций или удаления манифестов без очистки. Они занимают место и могут вызывать ошибки целостности. Garbage collection удаляет блобы, на которые не ссылается ни один манифест.
Для Docker Registry запустите команду внутри контейнера: docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml. Реестр должен быть в режиме read-only во время очистки, иначе возможна потеря данных. В production выполняйте garbage collection в окно обслуживания.
После очистки проверьте целостность файловой системы хранилища: sudo fsck -f /dev/sdb1 (замените на ваше устройство). Поврежденные блобы восстановлению не подлежат, их нужно удалить вручную из директории /var/lib/registry/docker/registry/v2/blobs/.
Если реестр использует S3-бэкенд, garbage collection настраивается через политику жизненного цикла бакета. Неполные multipart-загрузки удаляются автоматически по истечении заданного срока.
Профилактика: настройка мониторинга и безопасной конфигурации
Реактивное восстановление - вынужденная мера. Проактивный подход снижает количество инцидентов и время их обнаружения. Два ключевых элемента профилактики: мониторинг метрик и резервное копирование.
Мониторинг доступности и метрик реестра
Docker Registry отдает метрики в формате Prometheus на эндпоинте /metrics. Для включения добавьте в конфигурацию:
http:
addr: :5000
debug:
prometheus:
enabled: true
path: /metrics
Настройте сбор метрик в Prometheus и создайте алерты на критические события:
- Рост количества 5xx ошибок:
rate(registry_http_requests_total{code=~"5.."}[5m]) > 0.1. - Рост латенси запросов:
histogram_quantile(0.95, rate(registry_http_request_duration_seconds_bucket[5m])) > 1. - Падение health-check:
up{job="registry"} == 0.
Health-check эндпоинт /v2/ должен проверяться отдельно. Он возвращает 200, только если реестр полностью функционален и подключен к бэкенд-хранилищу. Настройте проверку с интервалом 15 секунд.
Для управления запущенными контейнерами и быстрой отладки используйте приемы из шпаргалки по управлению контейнерами Docker.
Резервное копирование и стратегия восстановления
Минимальный набор для восстановления реестра с нуля: директория хранения образов и конфигурационные файлы. Для Docker Registry это /var/lib/registry/docker/registry/v2/ и /etc/docker/registry/config.yml. Для Harbor - директория /data/ и файл harbor.yml.
Стратегия резервного копирования зависит от бэкенда хранилища. Для файлового бэкенда используйте rsync с сохранением прав и временных меток: rsync -avz /var/lib/registry/ backup-server:/backups/registry/. Для S3-бэкенда настройте репликацию бакета в другом регионе - это дает защиту от потери всего дата-центра.
Задокументируйте процедуру восстановления и проверяйте ее раз в квартал. Сухая теория без практической проверки не работает. Восстановите реестр из резервной копии на тестовом стенде и убедитесь, что все образы доступны для pull. Время восстановления должно быть измерено и зафиксировано в SLA.
Для комплексной защиты контейнерной инфраструктуры используйте полный чек-лист безопасности Docker-контейнеров.