Что означают ImagePullBackOff и ErrImagePull
Статусы ErrImagePull и ImagePullBackOff почти всегда про одно и то же: kubelet на ноде попытался скачать образ из container registry, получил ошибку, а затем перешёл в режим backoff — повторные попытки будут происходить реже, с увеличивающейся задержкой.
ErrImagePull — первая «жёсткая» ошибка при попытке pull (не авторизовался, не резолвится домен registry, не проходит TLS, сеть не ходит через proxy, сработал лимит запросов и т. п.). ImagePullBackOff — следствие: Kubernetes перестаёт дёргать registry слишком часто и ждёт.
Диагностика начинается не со статуса Pod, а с событий и конкретного текста ошибки от runtime.
Быстрый чек-лист: где искать причину
Чтобы не бегать по кругу, двигайтесь сверху вниз:
- Уточните, какой registry и какой image reference пытается подтянуть Pod (включая tag или digest).
- Снимите Events и найдите код/текст ошибки (401/403/429, TLS/x509, DNS, timeout, proxy).
- Проверьте imagePullSecrets и Secret типа
kubernetes.io/dockerconfigjsonв нужном namespace. - Проверьте сеть ноды: DNS, egress, proxy, MTU, доступ до registry.
- Если проблема только на части нод — сравните настройки runtime (
containerd), CA/сертификаты, proxy и DNS на этих нодах.

С чего начать: 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"}'
Типовые причины и как их отличить по тексту ошибки
1) Неверное имя образа, tag или приватный репозиторий
Самое банальное: опечатка, несуществующий tag, репозиторий переименовали, а в манифестах осталась старая ссылка.
Частые сообщения:
manifest unknownnot foundfailed 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 (если он локальный).

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.
Частые «подводные камни» в 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.


