Короткий ответ

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 hostDNS хоста
i/o timeout / connection refusedсеть/firewall/порт 5000
x509TLS/PKI
401/403 / unauthorizedauth
407proxy

Возможные причины

  1. Нет DNS/search для registry.example на хосте.
  2. Прокси для интернета, но NO_PROXY без registry.example.
  3. Прокси нужен, в unit docker его нет.
  4. Просроченный ~/.docker/config.json или login от другого пользователя (root vs admin).
  5. Rate limit Docker Hub, если на самом деле тянете не private (путаница зеркала).
  6. ACL периметра на :5000.
  7. Редко: 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.3

Robot-аккаунт с правом 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 images

Digest совпал с ожидаемым. Стек 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.json registry-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.