ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

Kubernetes: ErrImagePull и 401 Unauthorized — настройка imagePullSecrets для private registry

Если Pod застрял в ErrImagePull/ImagePullBackOff и в Events виден 401 Unauthorized, почти всегда виноваты imagePullSecrets: секрет не того типа, не в том namespace или не привязан к ServiceAccount. Разберём диагностику и рабочие команды.
Kubernetes: ErrImagePull и 401 Unauthorized — настройка imagePullSecrets для private registry

Симптомы: ErrImagePull, ImagePullBackOff и 401 Unauthorized

Типичная картина: деплой применился, Pod создан, но контейнер не стартует. В статусе появляется ErrImagePull, позже — ImagePullBackOff, а в событиях (Events) видно 401 Unauthorized. Это означает, что kubelet на ноде не смог аутентифицироваться в реестре образов при попытке скачать image из private registry.

Важно различать: ErrImagePull — первая неудачная попытка скачать образ, ImagePullBackOff — повторные попытки с увеличивающейся задержкой. В обоих случаях первопричина одна: образ недоступен, имя/тег неверные, либо реестр требует авторизацию или валидный TLS.

Дальше фокус только на ситуации с 401, когда реестр доступен, но Kubernetes не применяет нужные учётные данные: imagePullSecrets, секрет типа kubernetes.io/dockerconfigjson, привязка к ServiceAccount и типовые нюансы Harbor/GitLab/registry v2.

Шаг 1. Быстро подтвердить, что это именно auth (kubectl describe pod)

Начните с диагностики «в лоб». Нас интересуют две вещи: текст ошибки в Events и то, видит ли Pod какие-то imagePullSecrets.

kubectl describe pod -n NAMESPACE POD_NAME

Внизу часто будет сообщение вида:

Failed to pull image "registry.example.com/team/app:1.2.3": rpc error: code = Unknown desc = failed to pull and unpack image ...: failed to resolve reference ...: unexpected status from HEAD request ...: 401 Unauthorized

Ориентиры для быстрой развилки:

  • 401 Unauthorized — kubelet не применил корректные credentials (или применил не к тому host).

  • 403 Forbidden — аутентификация прошла, но нет прав на репозиторий/проект.

  • 404 Not Found — чаще неверный repo/tag или вы указываете путь, которого нет.

В том же describe посмотрите строку:

Image Pull Secrets:  my-regcred

Если там пусто, Kubernetes даже не пытается использовать секрет при pull.

Шаг 2. Откуда Kubernetes берёт credentials: Pod vs ServiceAccount

Есть два нормальных способа передать учётные данные для private registry:

  • Явно в манифесте Deployment/Pod через spec.template.spec.imagePullSecrets.

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

Если вы ожидаете, что секрет «подхватится сам», но он просто создан в namespace и ни к чему не привязан, будет ErrImagePull и 401 Unauthorized.

Проверьте, какой ServiceAccount использует Pod:

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

Если вывод пустой, используется default. Посмотрите его:

kubectl get sa -n NAMESPACE default -o yaml

Ищите секцию imagePullSecrets. Если её нет, credentials через SA не подтягиваются.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Если вы держите кластер на отдельных виртуальных машинах, удобно выносить рабочие ноды в VDS: так проще контролировать сеть до реестра, TLS и доступы, а диагностика pull-ошибок обычно быстрее и прозрачнее.

События Pod с ошибкой ImagePullBackOff и 401 Unauthorized

Шаг 3. Проверить секрет: тип, namespace, корректность dockerconfigjson

Самые частые причины 401 в практике: секрет не того типа, секрет создан не в том namespace, неправильный ключ в data, или в нём указан не тот сервер в auths.

Проверьте секрет:

kubectl get secret -n NAMESPACE SECRET_NAME -o yaml

Для секрета реестра ожидаем:

  • type: kubernetes.io/dockerconfigjson

  • в data есть ключ .dockerconfigjson

Если secret создан как Opaque или вы положили ключ config.json вместо .dockerconfigjson, kubelet не сможет использовать его как registry credentials.

Чтобы убедиться, что внутри валидный JSON, можно декодировать (делайте это только в защищённой среде, потому что это чувствительные данные):

kubectl get secret -n NAMESPACE SECRET_NAME -o jsonpath='{.data..dockerconfigjson}' | base64 -d; echo

Внутри должен быть объект формата Docker config, минимально:

{
  "auths": {
    "registry.example.com": {
      "username": "user",
      "password": "pass",
      "auth": "dXNlcjpwYXNz"
    }
  }
}

Критично, чтобы ключ в auths совпадал с тем, как Kubernetes обращается к реестру в имени образа. Если image указан как registry.example.com/team/app:tag, то credentials должны быть для registry.example.com. Если вы положили https://registry.example.com или добавили путь registry.example.com/team, kubelet не сопоставит и пойдёт без авторизации, получив 401 Unauthorized.

Шаг 4. Правильное создание imagePullSecrets (рекомендуемый способ)

Самый надёжный способ — создать secret командой kubectl create secret docker-registry: она гарантирует правильный тип kubernetes.io/dockerconfigjson и корректную структуру.

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

Если реестр — Harbor, часто удобнее использовать robot account и токен, а не пароль пользователя. Тогда --docker-username будет именем robot’а (например, robot$ci), а --docker-password — его secret.

Если вы хотите глубже закрыть тему TLS и ошибок, которые маскируются под «проблемы с реестром», полезно держать под рукой отдельную шпаргалку: TLS и авторизация для private registry: типовые ошибки и диагностика.

Шаг 5. Привязка секрета: в Deployment и/или в ServiceAccount

Вариант A: указать imagePullSecrets в Deployment

Самый предсказуемый вариант — прописать секрет прямо в манифесте. Пример фрагмента:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  namespace: NAMESPACE
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      imagePullSecrets:
        - name: regcred
      containers:
        - name: app
          image: registry.example.com/team/app:1.2.3

Плюс этого подхода: секрет явно «едет» вместе с конкретным workload, меньше сюрпризов при переносе манифестов между namespace.

Вариант B: добавить imagePullSecrets в ServiceAccount

Если в namespace много деплоев и все тянут из одного private registry, удобнее настроить ServiceAccount. Для default это можно сделать так:

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

После этого новые Pod’ы с ServiceAccount default будут тянуть образы с этим секретом. Застрявшие Pod’ы проще пересоздать (например, через kubectl rollout restart у Deployment).

Шаг 6. Harbor auth: типовые грабли

Для Harbor чаще всего «стреляют» такие моменты:

  • Неправильный endpoint в секрете. Нужен домен реестра (например, harbor.example.com), без протокола и без пути.

  • Robot account без pull-доступа к проекту/репозиторию (тут чаще будет 403, но при особенностях прокси/настроек может выглядеть и как 401).

  • SSO/LDAP пользователь с протухшим паролем/токеном. Для CI/K8s стабильнее robot account.

  • Проект приватный: на вашем ноутбуке pull «проходит», потому что Docker уже залогинен; на ноде Kubernetes этих credentials нет без secret.

Полезная проверка: теми же учётными данными попробуйте вручную сделать pull на любой машине с Docker/nerdctl/containerd. Если вручную не тянется, Kubernetes тоже не вытянет.

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

Если реестр доступен по HTTPS и у вас собственный CA или нестандартная цепочка, проверьте сертификаты заранее. С корректно выпущенным сертификатом (например, через SSL-сертификаты) меньше шансов упереться в ошибки вида x509, которые часто путают с авторизацией.

Привязка imagePullSecrets к ServiceAccount и проверка dockerconfigjson

Шаг 7. «Секрет есть, но всё равно 401»: короткий чек-лист

1) Secret в другом namespace

Secrets — строго namespace-scoped. Если Deployment в prod, а secret создан в default, kubelet его не увидит. Проверка:

kubectl get secret -n NAMESPACE

2) Pod использует не тот ServiceAccount

Вы могли пропатчить default, но Pod использует, например, app-sa. Проверьте:

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

3) Неверный ключ в auths (server mismatch)

Сравните, что указано в image и что лежит в .dockerconfigjson:

  • image: registry.example.com/team/app:tag

  • auths: должен быть ключ registry.example.com

Любые расхождения по поддомену, порту и протоколу ломают сопоставление.

4) Порт указан в одном месте, а в другом нет

Если тянете как registry.example.com:5443/team/app:tag, то и в auths ключ должен быть registry.example.com:5443.

5) Нода ходит в реестр через прокси/зеркало/редирект

Иногда сеть подменяет endpoint или включён reverse proxy, который меняет host. В итоге kubelet обращается к одному хосту, а secret содержит другой.

6) Нестандартная схема авторизации

Kubernetes ожидает стандартный Docker config и совместимость с Docker Registry API v2 (включая token service). Если у вас нестандартная прослойка, проверьте её совместимость, иначе kubelet может уходить в pull без валидных credentials.

Шаг 8. Минимальный набор команд «починить сейчас»

В большинстве случаев достаточно такой последовательности: создать или обновить secret, привязать к ServiceAccount, затем пересоздать Pod’ы.

kubectl create secret docker-registry regcred -n NAMESPACE --docker-server=registry.example.com --docker-username=USER --docker-password=PASS --docker-email=devops@example.com
kubectl patch serviceaccount -n NAMESPACE default -p '{"imagePullSecrets": [{"name": "regcred"}]}'
kubectl rollout restart deployment -n NAMESPACE DEPLOYMENT_NAME

Шаг 9. Как отличить auth-проблему от TLS/CA и DNS

Иногда всё пытаются «лечить» imagePullSecrets, хотя ошибка другого класса. Быстрые ориентиры по тексту событий:

  • TLS/CA: фразы про x509, certificate signed by unknown authority, tls: failed to verify certificate.

  • DNS/сеть: no such host, i/o timeout, connection refused.

  • Auth: явный 401 Unauthorized или authentication required.

Шаг 10. Практика: безопасное хранение и ротация credentials

Чтобы ErrImagePull не всплывал «раз в пару месяцев», когда кто-то поменял пароль или протух токен:

  • Для CI/Kubernetes используйте robot/service accounts в реестре, а не персональные логины.

  • Давайте минимальные права: только pull на нужные репозитории.

  • Документируйте соответствие: какой namespace тянет из какого registry и каким secret.

  • Планируйте ротацию: обновили secret, сделали rolling restart нужных workload.

Если namespace’ов много, помогает единый стандарт: отдельный ServiceAccount на приложение, один registry secret на namespace, плюс явное указание imagePullSecrets в чарте/манифесте там, где важна переносимость.

Итоги

ErrImagePull и ImagePullBackOff с 401 Unauthorized почти всегда сводятся к тому, что kubelet не нашёл или не смог применить корректные credentials для private registry. Начните с kubectl describe pod, проверьте ServiceAccount, тип секрета kubernetes.io/dockerconfigjson, ключ .dockerconfigjson и точное совпадение адреса реестра в auths с тем, что указано в image. Затем привяжите secret к Pod или ServiceAccount и пересоздайте Pod’ы.

Если после исправления auth всё равно не тянется, смотрите в сторону TLS (CA) и сетевых проблем: они проявляются другими сообщениями, но часто всплывают «следом».

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

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

Linux watchdog: soft lockup/hard lockup, rcu stall и hung task — диагностика по dmesg и связь с I/O OpenAI Статья написана AI (GPT 5)

Linux watchdog: soft lockup/hard lockup, rcu stall и hung task — диагностика по dmesg и связь с I/O

Если сервер подтормаживает или «замирает», а в логе ядра появляются soft lockup/hard lockup, rcu stall или hung task — это не всег ...
Kubernetes NodePort и ExternalTrafficPolicy: как сохранить IP клиента и не сломать балансировку OpenAI Статья написана AI (GPT 5)

Kubernetes NodePort и ExternalTrafficPolicy: как сохранить IP клиента и не сломать балансировку

NodePort и LoadBalancer нередко «съедают» реальный IP клиента из‑за SNAT в kube-proxy. Разберём режимы externalTrafficPolicy (Clus ...
Apache 503 Service Unavailable: уперлись в MaxRequestWorkers, KeepAlive и scoreboard — как найти и исправить OpenAI Статья написана AI (GPT 5)

Apache 503 Service Unavailable: уперлись в MaxRequestWorkers, KeepAlive и scoreboard — как найти и исправить

503 в Apache часто означает не «упал сайт», а «закончились воркеры». Разберём, как по AH-ошибкам и scoreboard понять причину, учес ...