ImagePullBackOff
Почему Pod не может скачать образ, где в describe лежит настоящий текст ошибки и что с этим делают.
Самая частая поломка в Kubernetes выглядит скучно: Pod создан, но
контейнер не стартует, а в статусе висит ImagePullBackOff.
NAME READY STATUS RESTARTS AGE
web 0/1 ImagePullBackOff 0 90s
Kubernetes тут ни в чём не виноват. Он честно попросил образ и не получил его. Разбор ниже: что происходит под капотом, где искать текст ошибки и почему в девяти случаях из десяти виновата опечатка.
Как kubelet тянет образ
kubelet - агент Kubernetes на каждой ноде, который запускает контейнеры. Получив spec Pod с конкретным image, он делает несколько вещей по порядку:
- Проверяет, есть ли уже образ локально на ноде.
- Если нет, дёргает container runtime (containerd, CRI-O) с запросом на pull.
- Runtime идёт в registry: парсит имя, резолвит host, шлёт HTTP запросы на manifest и слои.
- Если получил всё, монтирует и запускает контейнер.
На любом шаге может сломаться. Каждая ошибка даёт свой статус.
Семейство Image-ошибок
| Статус | Что значит | Типичная причина |
|---|---|---|
ErrImagePull | первая попытка pull провалилась | опечатка, нет авторизации, нет интернета, образ снят с registry |
ImagePullBackOff | kubelet делает паузы между повторными попытками | то же самое, что выше, просто прошло несколько секунд |
InvalidImageName | имя образа не парсится | пробелы, спецсимволы, тег без двоеточия |
RegistryUnavailable | registry не отвечает | сетевая проблема, DNS, registry упал |
Между ErrImagePull и ImagePullBackOff концептуальной разницы нет.
Это одна и та же ошибка, просто kubelet перешёл в режим
экспоненциальной задержки: 10s, 20s, 40s, 80s, до 5 минут между
попытками. Поэтому Pod, простоявший в ImagePullBackOff десять минут,
не «завис» - он просто ждёт следующей попытки.
Что показывает диагностика
kubectl describe pod web
В выводе важны три места.
Containers → Image: то, что реально просили затянуть.
Containers:
app:
Image: nginxinc/nginx-unpriviledged:1.25
State: Waiting
Reason: ImagePullBackOff
Events: что говорит runtime. Тут прямой текст ошибки.
Events:
Type Reason Message
Normal Pulling Pulling image "nginxinc/nginx-unpriviledged:1.25"
Warning Failed Failed to pull image "...": rpc error: not found
Warning Failed Error: ErrImagePull
Normal BackOff Back-off pulling image "..."
Warning Failed Error: ImagePullBackOff
Дальше текст ошибки читается почти как справочник:
not foundилиmanifest unknown- почти всегда опечатка в имени или теге.unauthorized- нет доступа, нужен imagePullSecret.no such host- DNS или интернет на ноде.
Для приватного registry нужен imagePullSecret, привязанный к Pod или к
ServiceAccount. Без него ошибка та же самая, отличается только текст:
unauthorized.
Если нужен только хвост событий без полного describe, помогает выборка по объекту:
kubectl get events --field-selector involvedObject.name=web --sort-by='.lastTimestamp'
Типичные опечатки
- Лишняя или пропущенная буква:
nginx-unpriviledgedвместоnginx-unprivileged,quay.oiвместоquay.io. - Неверный namespace registry:
nginx/nginx-unprivilegedвместоnginxinc/nginx-unprivileged. - Опечатка в теге:
:1.25вместо:1.25.0,:latestsвместо:latest. - Кириллическая буква вместо латинской. Чаще всего попадаются
сиc,аиa.
Проверить гипотезу быстро: попробовать тот же образ вручную через
docker pull или crane manifest. Если runtime отвечает not found,
имя действительно неверное, и дело не в кластере.
imagePullPolicy и почему он влияет на дебаг
У контейнера есть поле imagePullPolicy. По умолчанию:
Always, если тег:latestили тега нет вовсе.IfNotPresent, если тег конкретный (например:1.25).Neverничего не тянет, использует локальный кэш.
Always означает, что kubelet будет тянуть образ при каждом старте
контейнера. Если registry падает, Pod ломается, даже когда образ на
ноде уже лежит.
IfNotPresent тянет, только если на ноде нет такого тега. Это
безопаснее, но при первом запуске всё равно идёт в registry.
Never означает «считай, что образ уже есть локально». Если его нет,
статус будет ErrImageNeverPull. Такой режим используют в локальных
кластерах (kind, minikube), куда образ загружают отдельной командой.
Как чинят
Поле spec.containers[*].image у Pod изменяемое, так что имя можно
поправить на месте:
kubectl set image pod/web app=nginxinc/nginx-unprivileged:1.25
Или руками через редактор:
kubectl edit pod web
После изменения kubelet сразу дёргает pull заново, и через 5-10 секунд Pod становится Running.
У Deployment правят шаблон, а не Pod: kubectl set image deploy/...
создаёт новый ReplicaSet, и Pod пересоздаются с исправленным образом.
Когда Pod уже давно висит в ImagePullBackOff с длинным таймером
backoff, часто быстрее снести его и применить исправленный манифест:
kubectl delete pod web --grace-period=0 --force
kubectl apply -f web.yaml # тот же манифест, но с правильным image
Так старт гарантированно идёт с чистого состояния, без зависших таймеров от прошлых попыток.
Как не допустить
Имя образа удобно проверять до применения манифеста: docker pull или
crane manifest дают ответ за секунду. В CI тот же эффект даёт шаг,
который пуллит образ перед деплоем.
Плавающие теги вроде :latest вместе с imagePullPolicy: Always
делают деплой зависимым от доступности registry в момент каждого
рестарта. Конкретный тег и IfNotPresent убирают эту зависимость.
Домашка
Теорию закрепляет задание на своём кластере: 1 шаг с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.
./run.sh start debug-image # развернуть стартовое состояние
./run.sh verify debug-image # проверить текущий шаг
Коротко
ErrImagePullиImagePullBackOff- одна ошибка, разница только в таймере повторов.- Настоящий текст ошибки лежит в Events у
kubectl describe pod. not found- опечатка,unauthorized- нет доступа,no such host- DNS.imagePullPolicyопределяет, пойдёт ли kubelet в registry вообще.imageу Pod меняется на лету черезkubectl set image, у Deployment правят шаблон.
Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс