Короткий ответ
docker pull выполняет dockerd на хосте, не сеть контейнера web. На host.example проверьте: резолв registry.example, маршрут и прокси (HTTP_PROXY в systemd drop-in docker), TLS, затем docker login registry.example:5000. Ошибка x509 / 401 на самом registry — private registry недоступен. Не включайте insecure-registries «чтобы качалось» на проде без тикета. DNS контейнера 127.0.0.11 тут ни при чём.
Симптомы и как отличить
Error response from daemon: Get "https://registry.example:5000/v2/": dial tcp: lookup registry.example;unauthorized: authentication required;- висит на
Pulling fs layer; - с ноутбука curl к registry ок, с сервера нет (прокси/ACL);
compose pullпадает, локальный образ ещё есть — это не dangling-чистка.
| Сообщение | Слой |
|---|---|
| lookup / no such host | DNS хоста |
| i/o timeout / connection refused | сеть/firewall/порт 5000 |
| x509 | TLS/PKI |
| 401/403 / unauthorized | auth |
| 407 | proxy |
Возможные причины
- Нет DNS/search для
registry.exampleна хосте. - Прокси для интернета, но
NO_PROXYбезregistry.example. - Прокси нужен, в unit docker его нет.
- Просроченный
~/.docker/config.jsonили login от другого пользователя (rootvsadmin). - Rate limit Docker Hub, если на самом деле тянете не private (путаница зеркала).
- ACL периметра на
:5000. - Редко: HTTP/2 баг через ломаный MITM.
Диагностика
Образ-пример: registry.example:5000/app/web:1.2.3.
1. DNS и TCP с хоста
getent ahostsv4 registry.example
resolvectl query registry.example
curl -svS --max-time 10 https://registry.example:5000/v2/ -o /dev/null-k только чтобы отличить x509 от сети, не как норма.
2. Что видит dockerd
systemctl show docker -p Environment --no-pager
sudo grep -r proxy /etc/systemd/system/docker.service.d/ /etc/docker/ 2>/dev/null
docker info --format '{{.HTTPProxy}} {{.HTTPSProxy}} {{.NoProxy}}'3. Auth
sudo ls -l /root/.docker/config.json ~/.docker/config.json
sudo docker login registry.example:5000Логин от admin не равен логину демона, если команды через sudo без -H пишут в /root.
4. Журнал pull
journalctl -u docker --since '10 min ago' --no-pager | tail -n 50
docker pull registry.example:5000/app/web:1.2.3Решение
Сценарий A. DNS
Исправьте netplan/resolved или /etc/hosts хоста временно для split-horizon:
getent hosts registry.exampleПостоянно — зона DNS, не hosts. Проверка: docker pull снова, потому что dockerd кэширует мало, но systemd-resolved кэширует.
Сценарий B. Нужен корпоративный прокси
Drop-in:
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf >/dev/null <<'EOF'
[Service]
Environment="HTTP_PROXY=http://proxy.example:3128"
Environment="HTTPS_PROXY=http://proxy.example:3128"
Environment="NO_PROXY=localhost,127.0.0.1,registry.example,10.0.0.0/8"
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
docker info --format '{{.HTTPSProxy}} {{.NoProxy}}'registry.example в NO_PROXY, если registry во внутренней сети. Иначе pull пойдёт в прокси и получит 407/timeout.
Окно: restart docker рвёт контейнеры, если нет live-restore.
Сценарий C. Прокси не нужен, но выставлен в сессии
Уберите переменные из unit и из /etc/environment, если они ломают внутренний registry.
Сценарий D. Auth
sudo docker logout registry.example:5000
sudo docker login registry.example:5000
sudo docker pull registry.example:5000/app/web:1.2.3Robot-аккаунт с правом pull, не личный admin. config.json права 600, не в git.
Сценарий E. Путаница Hub vs private
В compose.yaml должно быть image: registry.example:5000/app/web:1.2.3, не app/web:1.2.3 (это Docker Hub).
containerd в 27.x ходит за слоями сам; ошибка в journal может быть у dockerd или у containerd. Смотрите оба unit. Таймаут на большом слое при живом curl /v2/ часто MTU: VPN/GRE на host.example с MTU 1400 и внешний путь 1500. Симптом: мелкий конфиг-слой скачался, слой 80 MiB висит. Проверка: ip link, уменьшение MTU docker-сети — уже другая статья, но не лечите это insecure-registries.
Зеркало Hub (registry-mirrors в daemon.json) не обслуживает registry.example:5000. Настройка зеркала «чтобы быстрее» не чинит private pull и может направить запросы на не тот registry, если в compose забыли префикс хоста. Всегда полный путь образа.
~/.docker/config.json с "auths" и "credsStore": если включён helper, а бинаря нет, login «как будто есть», pull 401. На сервере проще файл 600 с auth, чем desktop-store. Права: не 644 в /root/.docker. Sudo без -H пишет auth в /root, команда без sudo читает /home/admin/.docker — расхождение «у меня login есть».
Прокси с NTLM/Kerberos для dockerd — боль: unit Environment обычно Basic. Если корпоративный прокси требует NTLM, вынесите pull на hop с нормальным HTTP-proxy или разрешите NO_PROXY к registry напрямую. Не кладите пароль прокси в compose проекта app.
Как проверить, что проблема устранена
docker pull registry.example:5000/app/web:1.2.3
cd /opt/app && docker compose pull
docker compose up -d
docker compose imagesDigest совпал с ожидаемым. Стек Running. Повторный pull идемпотентен (already exists).
Если не помогло
- curl 200 на
/v2/, pull 401 — auth/scope токена, не DNS. - curl x509 — сертификаты registry, следующая статья.
- Pull слоя timeout — MTU/прокси buffering, не DNS. Попробуйте с hop в той же сети.
- IPv6 AAAA висит:
curl -4, правка DNS. daemon.jsonregistry-mirrors на Hub не зеркалит вашregistry.example.
Профилактика
- Образ всегда с FQDN registry в compose.
- NO_PROXY документирован.
- Отдельная учётка pull на хостах.
- Мониторинг
docker pullв CI с хоста, идентичного проду. - Не хранить login в скриптах plaintext.
IPv6: getent ahostsv6 registry.example даёт AAAA, dockerd идёт туда, путь чёрный, IPv4 жив. Для проверки curl -4 и pull после временного sysctl net.ipv6.conf.all.disable_ipv6 не используйте как лечение. Почините DNS/маршрут v6 или не публикуйте AAAA, пока путь не готов.
FAQ
Почему exec curl в web работает, а pull нет?
Разные netns и разный DNS. Pull = хост.
docker compose pull vs docker pull
Compose берёт теги из YAML. Опечатка сервиса image: — типичный «не качается».
insecure-registries для :5000?
Только HTTP registry в лабе. Прод — TLS. Иначе MITM на слоях.
Нужен ли --privileged для pull?
Нет.
После login от admin sudo pull всё ещё 401
config в home admin, dockerd читает не его. sudo docker login или sudo -H.