Выберите продукт

Kubernetes ImagePullBackOff и ErrImagePull: как быстро найти причину и починить pull из registry

ImagePullBackOff и ErrImagePull возникают, когда нода не может скачать образ из registry. В статье — пошаговая диагностика через kubectl Events, проверка imagePullSecrets и прав (401/403), работа с rate limit (429), DNS/TLS/x509, proxy и инструменты containerd/crictl.
Kubernetes ImagePullBackOff и ErrImagePull: как быстро найти причину и починить pull из registry

Что означают ImagePullBackOff и ErrImagePull

Статусы ErrImagePull и ImagePullBackOff почти всегда про одно и то же: kubelet на ноде попытался скачать образ из container registry, получил ошибку, а затем перешёл в режим backoff — повторные попытки будут происходить реже, с увеличивающейся задержкой.

ErrImagePull — первая «жёсткая» ошибка при попытке pull (не авторизовался, не резолвится домен registry, не проходит TLS, сеть не ходит через proxy, сработал лимит запросов и т. п.). ImagePullBackOff — следствие: Kubernetes перестаёт дёргать registry слишком часто и ждёт.

Диагностика начинается не со статуса Pod, а с событий и конкретного текста ошибки от runtime.

Быстрый чек-лист: где искать причину

Чтобы не бегать по кругу, двигайтесь сверху вниз:

  1. Уточните, какой registry и какой image reference пытается подтянуть Pod (включая tag или digest).
  2. Снимите Events и найдите код/текст ошибки (401/403/429, TLS/x509, DNS, timeout, proxy).
  3. Проверьте imagePullSecrets и Secret типа kubernetes.io/dockerconfigjson в нужном namespace.
  4. Проверьте сеть ноды: DNS, egress, proxy, MTU, доступ до registry.
  5. Если проблема только на части нод — сравните настройки runtime (containerd), CA/сертификаты, proxy и DNS на этих нодах.

Пример вывода Kubernetes Events с ошибкой Failed to pull image

С чего начать: kubectl describe pod и события

Первый инструмент — kubectl describe pod. Он показывает Events, где обычно уже написано, что именно пошло не так.

kubectl -n NAMESPACE describe pod POD_NAME

Ищите блок Events и строки вида Failed to pull image, Error: ErrImagePull или rpc error: code = Unknown desc = ....

Если событий слишком много, удобно отфильтровать только по Pod:

kubectl -n NAMESPACE get events --field-selector involvedObject.name=POD_NAME --sort-by=.lastTimestamp

И отдельно посмотреть, какой образ и какая политика pull указаны в спецификации:

kubectl -n NAMESPACE get pod POD_NAME -o jsonpath='{.spec.containers[*].image}{"\n"}{.spec.containers[*].imagePullPolicy}{"\n"}'
FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Типовые причины и как их отличить по тексту ошибки

1) Неверное имя образа, tag или приватный репозиторий

Самое банальное: опечатка, несуществующий tag, репозиторий переименовали, а в манифестах осталась старая ссылка.

Частые сообщения:

  • manifest unknown
  • not found
  • failed to resolve reference

Проверьте, что образ существует, и что вы указываете правильный путь (например, registry.example.com/team/app:1.2.3 vs registry.example.com/app:1.2.3).

2) 401 Unauthorized: нет (или не применились) imagePullSecrets

401 Unauthorized означает: registry требует авторизацию, но kubelet не смог предоставить валидные креды.

Secret должен быть в том же namespace, где создаётся Pod, и должен быть привязан через imagePullSecrets в Pod (или через ServiceAccount).

Проверьте, какие imagePullSecrets указаны в Pod:

kubectl -n NAMESPACE get pod POD_NAME -o jsonpath='{.spec.imagePullSecrets[*].name}{"\n"}'

Если пусто — Kubernetes не знает, какие креды использовать. Исправления два: добавить imagePullSecrets в Pod/Deployment или привязать Secret к ServiceAccount.

Проверьте, что Secret существует и правильного типа:

kubectl -n NAMESPACE get secret SECRET_NAME -o yaml

Нужен тип kubernetes.io/dockerconfigjson (обычно создаётся командой kubectl create secret docker-registry).

Создание Secret (типичный вариант):

kubectl -n NAMESPACE create secret docker-registry regcred --docker-server=REGISTRY_HOST --docker-username=USER --docker-password=PASS --docker-email=mail@example.com

Пример фрагмента манифеста (как текст, в вашем YAML подставьте реальные значения):

apiVersion: v1
kind: Pod
metadata:
  name: demo
spec:
  imagePullSecrets:
  - name: regcred
  containers:
  - name: app
    image: REGISTRY_HOST/team/app:1.2.3

Альтернатива (часто удобнее): привязать Secret к ServiceAccount по умолчанию в namespace:

kubectl -n NAMESPACE patch serviceaccount default -p '{"imagePullSecrets": [{"name": "regcred"}]}'

После этого Pod’ы без явного imagePullSecrets смогут подтягивать образы через этот ServiceAccount. Но если в Pod задан serviceAccountName, патчить нужно именно его.

3) 403 Forbidden: креды есть, но нет прав

403 Forbidden — вы авторизовались, но у токена/пользователя нет прав на конкретный репозиторий, путь или действие.

Частая ловушка: доступ есть к проекту/группе, но образ лежит в другом namespace registry. Или токен имеет недостаточные scopes.

Что проверить:

  • Точный путь образа (org/team/repo) и соответствие реальному расположению.
  • Права robot account/token на pull именно этого репозитория.
  • Если используется :latest — что тег реально опубликован и доступен.

4) 429 Too Many Requests: rate limit публичного registry

429 Too Many Requests — вы упёрлись в rate limit (часто при массовом автоскейле/перезапусках). В Events это может быть явный 429 или текст про лимиты.

Практика снижения ударной нагрузки:

  • Не тяните образ «каждый раз»: для неизменяемых тегов (например, 1.2.3) часто подходит IfNotPresent.
  • Закрепляйте образы по digest, чтобы не зависеть от изменяемых тегов.
  • Не чистите кэш образов на нодах агрессивно без причины.
  • Для базовых образов используйте приватный registry/зеркало внутри инфраструктуры.

Если проблема возникает «волнами» после деплоя — это часто комбинация :latest + Always + много нод.

5) DNS: registry не резолвится или резолвится не туда

Если в ошибке есть no such host, NXDOMAIN, temporary failure in name resolution — начните с DNS. Учитывайте два слоя: DNS внутри кластера (CoreDNS) и DNS ноды (что видит runtime при обращении к внешним доменам).

Проверьте резолв с ноды:

getent hosts REGISTRY_HOST
resolvectl query REGISTRY_HOST

Если часть нод резолвит иначе — ищите различия в /etc/resolv.conf, systemd-resolved, локальных DNS-кэшах и egress-настройках.

6) TLS и x509: certificate signed by unknown authority

Ошибки вида x509 certificate signed by unknown authority, certificate verify failed, remote error: tls говорят о проблемах доверия к сертификату registry или о MITM-прокси, который подменяет сертификаты.

Что это обычно означает на практике:

  • Приватный registry использует сертификат от внутреннего CA, а на нодах нет этого CA в trust store.
  • Сертификат просрочен или не соответствует имени (SAN/CN не совпадает).
  • Прокси перехватывает TLS и выдаёт свой сертификат.

Быстрая проверка сертификатной цепочки с ноды:

openssl s_client -connect REGISTRY_HOST:443 -servername REGISTRY_HOST -showcerts

Дальше варианты зависят от runtime. Для containerd обычно нужно добавить доверенный CA в систему и при необходимости настроить registry в конфигурации containerd (например, через hosts.toml). Конкретные пути и формат зависят от дистрибутива Kubernetes и способа установки.

Если ваш registry публичный или доступен извне, проще всего поддерживать корректную TLS-цепочку. Для этого обычно используют нормальный публичный сертификат (в том числе wildcard) и автоматизированное обновление. По теме wildcard и автоматизации DNS-01 может пригодиться материал: Wildcard SSL и автоматизация DNS-01.

7) Proxy: registry доступен только через прокси или прокси ломает pull

С proxy две противоположные проблемы:

  • Без proxy интернет недоступен, и pull падает по timeout.
  • С proxy pull идёт, но TLS ломается (из-за подмены сертификатов) или не настроены исключения (NO_PROXY).

Проверьте переменные окружения на нодах и в сервисах kubelet/containerd: HTTP_PROXY, HTTPS_PROXY, NO_PROXY. В NO_PROXY обычно включают адреса API кластера, сервисные CIDR, Pod CIDR и адрес самого registry (если он локальный).

Диагностика pull образа на ноде через crictl и containerd

Runtime-уровень: containerd, crictl и логи kubelet

Когда по Events непонятно, что происходит (или нужно доказать, что проблема на конкретной ноде), уходите на уровень runtime.

Проверка pull напрямую через CRI

На проблемной ноде выполните pull вручную через crictl:

crictl info
crictl pull REGISTRY_HOST/team/app:1.2.3

Если тут сразу видите 401/403/429 или x509 — это уже не Kubernetes-манифест, а доступ/сеть/сертификаты.

Иногда crictl не настроен на правильный сокет. Тогда явно укажите endpoint (типично для containerd):

crictl --runtime-endpoint unix:///run/containerd/containerd.sock pull REGISTRY_HOST/team/app:1.2.3

Логи kubelet и containerd

Если pull ведёт себя нестабильно, полезно смотреть журналы:

journalctl -u kubelet -n 200 --no-pager
journalctl -u containerd -n 200 --no-pager

Ищите ошибки резолва, TLS, таймауты, сообщения про авторизацию к registry.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Частые «подводные камни» в Kubernetes-манифестах

imagePullSecrets в Deployment есть, но не работает

Классическая причина — Secret создан в другом namespace. В Kubernetes Secret не «общий» между namespace.

Проверьте namespace и имя:

kubectl -n NAMESPACE get deploy DEPLOY_NAME -o jsonpath='{.spec.template.spec.imagePullSecrets[*].name}{"\n"}'
kubectl -n NAMESPACE get secret SECRET_NAME

Secret есть, но dockerconfigjson неправильный

Если Secret создавали вручную, легко ошибиться в формате. Kubernetes ожидает ключ .dockerconfigjson (base64) и JSON со структурой auths. Проще пересоздать Secret командой kubectl create secret docker-registry и заменить в workload.

ServiceAccount переопределён

В Pod может быть указан serviceAccountName, и тогда патч default не поможет. Проверьте:

kubectl -n NAMESPACE get pod POD_NAME -o jsonpath='{.spec.serviceAccountName}{"\n"}'

И патчьте нужный ServiceAccount.

Как ускорить восстановление после исправления

После того как вы поправили Secret/права/DNS/TLS, Pod может ещё некоторое время оставаться в backoff до следующей попытки pull.

Практичный способ — пересоздать Pod (для Deployment/ReplicaSet он поднимется заново):

kubectl -n NAMESPACE delete pod POD_NAME

Или сделать restart для Deployment:

kubectl -n NAMESPACE rollout restart deploy DEPLOY_NAME

Если проблема была в imagePullSecrets и вы обновили Secret, пересоздание Pod почти всегда ускоряет применение.

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

  • Избегайте изменяемых тегов в проде. Лучше versioned tags или digest.
  • Планируйте стратегию для rate limit: кэширование, внутреннее зеркало, предзагрузка образов на ноды.
  • Стандартизируйте выпуск pull secret: единый процесс выдачи robot-account токенов, ротация, минимальные права.
  • Следите за TLS приватного registry: срок, цепочка, корректные SAN, одинаковые CA на всех нодах.
  • Стабилизируйте DNS: одинаковая конфигурация на нодах, понятные forwarders, мониторинг резолва.

Мини-диагностика по симптомам (шпаргалка)

Когда нужно быстро сориентироваться:

  • 401 Unauthorized → почти всегда imagePullSecrets, namespace, ServiceAccount или неверные креды.
  • 403 Forbidden → креды валидны, но нет прав на repo/path/scope.
  • 429 Too Many Requests → rate limit; лечится кэшем/зеркалом/политиками pull.
  • no such host / NXDOMAIN → DNS (CoreDNS, resolv.conf ноды, egress).
  • x509 certificate signed by unknown authority → доверие к CA, MITM proxy, неверный сертификат registry.
  • context deadline exceeded / i/o timeout → сеть, маршруты, firewall, proxy, MTU, проблемы с доступом до registry.

Итог

ImagePullBackOff — это не «ошибка Kubernetes вообще», а конкретная проблема доставки образа на конкретную ноду. Если вы дисциплинированно начинаете с kubectl describe pod, затем проверяете imagePullSecrets и Secret, и только потом уходите на DNS/TLS/proxy и уровень containerd через crictl, то в большинстве случаев причина находится за 10–20 минут.

Дальше всё упирается в системность: единая политика тегов, контроль прав в registry, мониторинг TLS и подготовленная стратегия на случай 429 Too Many Requests.

Поделиться статьей

Вам будет интересно

Linux: /etc/fstab и emergency mode (rescue) — как быстро поднять систему после ошибки монтирования OpenAI Статья написана AI (GPT 5)

Linux: /etc/fstab и emergency mode (rescue) — как быстро поднять систему после ошибки монтирования

Если после перезагрузки Linux падает в emergency mode или rescue из‑за /etc/fstab, чаще всего виноваты неверный UUID, опции монтир ...
Linux: когда зависает PID 1 (systemd) — D-state, stop-jobs и hung_task OpenAI Статья написана AI (GPT 5)

Linux: когда зависает PID 1 (systemd) — D-state, stop-jobs и hung_task

Если растёт load average, сервисы не останавливаются, а перезагрузка висит на stop jobs — часто виноваты D-state и блокировки I/O. ...
systemd hardening: DynamicUser, ProtectSystem и практичный sandboxing для сервисов OpenAI Статья написана AI (GPT 5)

systemd hardening: DynamicUser, ProtectSystem и практичный sandboxing для сервисов

Пошагово усиливаем безопасность systemd-сервисов без контейнеров: включаем DynamicUser, ограничиваем файловую систему через Protec ...