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

Kubernetes: ImagePullBackOff из‑за Docker Hub rate limit — mirror и registry cache на практике

ImagePullBackOff нередко вызван Docker Hub pull rate limit: кластер внезапно перестаёт скачивать образы, особенно при автоскейле и частых деплоях. Разберём диагностику по событиям и с нод, настройку containerd mirrors и вариант с proxy cache registry.
Kubernetes: ImagePullBackOff из‑за Docker Hub rate limit — mirror и registry cache на практике

Когда в Kubernetes внезапно появляются ImagePullBackOff и ErrImagePull, первая мысль — «упал registry» или «сломались креды». Но в реальных кластерах (dev/test, CI, автоскейлинг, много нод) часто причина проще: вы упёрлись в Docker Hub pull rate limit. Итог одинаковый: Pod не стартует, у Deployment растёт число недоступных реплик, а вы теряете время на не те проверки.

Ниже — практичный разбор: как быстро подтвердить именно лимит, чем отличается registry mirror от registry cache, и как настроить зеркала для containerd, либо поднять proxy-cache (Harbor/Quay/Distribution) и сделать его «щитом» для кластера.

Как выглядит ImagePullBackOff при pull rate limit Docker Hub

Снаружи это выглядит как обычный неудачный pull образа: Pod застревает на старте, kubelet ретраит загрузку, а статус меняется на ImagePullBackOff.

Быстрая проверка через kubectl describe

Начните с событий Pod:

kubectl -n default describe pod my-app-6d7b9c7d5f-abcde

Внизу, в блоке Events, при лимите Docker Hub часто встречаются toomanyrequests или прямое упоминание rate limit. Примеры типовых сообщений (формулировки зависят от runtime и версии):

Failed to pull image "library/nginx:1.25": rpc error: code = Unknown desc = failed to pull and unpack image "docker.io/library/nginx:1.25": failed to resolve reference "docker.io/library/nginx:1.25": too many requests to Docker Hub

Back-off pulling image "docker.io/library/nginx:1.25"

toomanyrequests: You have reached your pull rate limit. You may increase the limit by authenticating ...

Если в событиях видно toomanyrequests / rate limit — это не «битый DNS» и не «не тот тег». Это лимит, и лечится он организацией кэша/зеркала или аутентификацией.

Проверка с ноды: что видит containerd

Если события в Kubernetes «обрезаны» или выглядят неочевидно, подтвердите диагноз с ноды. Для containerd используйте crictl (общение через CRI):

crictl info
crictl pull docker.io/library/nginx:1.25

При rate limit вы увидите похожий toomanyrequests уже на уровне runtime. Это удобная проверка без догадок, особенно если проблема проявляется не на всех нодах.

Почему лимит внезапно «ломает» кластер

Pull rate limit проявляется волнами, поэтому создаёт ощущение «вчера работало, сегодня нет». Типичные причины:

  • Автоскейлинг: новые ноды приходят пустые и тянут все образы заново.

  • Частые деплои: CI выкатывает новые теги, и ноды не успевают «прогреть» локальный кэш.

  • Популярные базовые образы во многих подах (alpine/ubuntu/nginx): суммарно это десятки и сотни pull.

  • Один внешний IP (NAT): много нод «выглядят» для Docker Hub как один клиент, лимит общий.

  • Пересоздание нод (immutable-инфраструктура, автообновления): кэш на диске пропадает.

Если у вас registry cache будет жить рядом с кластером, удобно разместить его на отдельной машине/ВМ с быстрым диском и предсказуемой сетью — под такую роль обычно выбирают VDS с нормальным NVMe и возможностью расширять хранилище.

Схема: ноды Kubernetes тянут образы через локальный proxy cache вместо прямого Docker Hub

Что делать: три рабочие стратегии

На практике чаще всего комбинируют два подхода: быстро уменьшают число pull и параллельно внедряют кэш/зеркало.

  1. Снизить количество pull (политики, предзагрузка, стабильные теги, контроль пересозданий).

  2. Настроить зеркала (registry mirror) на уровне runtime: часть запросов уйдёт в альтернативный источник.

  3. Поднять proxy cache registry (registry cache): один раз скачали с Docker Hub — дальше раздаём из локальной инфраструктуры.

Стратегия 1: уменьшить число pull

  • Не используйте :latest в продакшне: он провоцирует лишние обновления и непредсказуемые pull.

  • Проверьте imagePullPolicy. Для закреплённых версий чаще нужен IfNotPresent, чтобы нода не тянула образ без необходимости.

  • Если нод много — рассмотрите pre-pull через DaemonSet (например, перед релизом). Это ускоряет старт и сглаживает пики, но не решает проблему системно при частом пересоздании нод.

Важно: даже при IfNotPresent новые ноды всё равно будут тянуть образы. Поэтому для кластеров с автоскейлом почти всегда нужен второй слой защиты: mirror или cache.

Стратегия 2: registry mirror vs registry cache

Термины путают, из-за чего делают «зеркало», ожидая поведения «кэша».

  • Registry mirror — альтернативная точка входа для скачивания образов. Может быть внешним зеркалом или корпоративным registry с синхронизацией. Плюс: проще маршрутизировать pull. Минус: зеркала не всегда гарантируют, что у вас будет закэширован именно нужный набор репозиториев/тегов.

  • Registry cache (proxy cache) — прокси, который при первом запросе тянет образ из upstream (Docker Hub) и сохраняет локально, а дальше отдаёт локально. Плюс: радикально снижает количество pull к upstream. Минус: нужны диски, мониторинг и базовая эксплуатация.

Если задача звучит как «пережить Docker Hub rate limit», чаще всего нужен именно registry cache.

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

Настройка containerd registry mirrors в Kubernetes

В современных кластерах часто используется containerd. Зеркала настраиваются на уровне containerd: вы описываете endpoints для хоста docker.io. Точные пути зависят от дистрибутива (kubeadm/RKE2/K3s), но логика одинаковая.

Вариант A: правка config.toml (универсально)

Посмотрите версию и текущую конфигурацию:

containerd --version
containerd config dump

Дальше добавьте зеркало для docker.io. Пример фрагмента TOML:

[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
    endpoint = ["https://mirror.registry.local", "https://registry-1.docker.io"]

Смысл: сначала containerd попробует mirror.registry.local, и только если не получится — пойдёт в Docker Hub. В роли зеркала может быть ваш proxy cache (Harbor/Quay/Distribution) или корпоративный registry.

Перезапустите containerd:

systemctl restart containerd

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

crictl pull docker.io/library/nginx:1.25

Вариант B: hosts.toml (per-registry конфиги)

Часто удобнее вынести настройки конкретного registry в отдельный файл hosts.toml (каталог certs.d), чтобы не трогать большой config.toml.

Общий план:

  • Создать каталог для хоста docker.io (иногда нужен registry-1.docker.io — зависит от дистрибутива).

  • Добавить hosts.toml с описанием upstream и зеркал.

Пример (пути уточняйте под вашу систему):

mkdir -p /etc/containerd/certs.d/docker.io
cat > /etc/containerd/certs.d/docker.io/hosts.toml << 'EOF'
server = "https://registry-1.docker.io"

[host."https://mirror.registry.local"]
  capabilities = ["pull", "resolve"]

[host."https://registry-1.docker.io"]
  capabilities = ["pull", "resolve"]
EOF

Дальше — рестарт containerd и повторный crictl pull.

Registry cache: Harbor/Quay/Distribution как «щит» от rate limit

Самый практичный способ системно снизить зависимость от Docker Hub — поднять proxy cache registry рядом с кластером (в той же сети/ЦОД) и направить pull через него.

  • Harbor proxy cache: прокси к Docker Hub и другим registry, хранит кэш, даёт UI, проекты, роботов, политики, опционально сканирование.

  • Quay: корпоративный реестр с возможностями зеркалирования/проксирования (в терминах часто говорят «quay mirror»).

  • Distribution (Docker Registry) в режиме pull-through cache: минималистично, но требовательно к аккуратной настройке и пониманию ограничений.

Как правильно подключить cache к Kubernetes

  1. Поднимите registry cache на стабильном адресе (DNS/балансер) и обеспечьте дисковое хранилище.

  2. Настройте в containerd зеркала для docker.io, чтобы сначала ходить в cache.

  3. При необходимости добавьте доверие к вашему TLS (корпоративный CA) на всех нодах, чтобы containerd мог ходить по HTTPS без ошибок.

Критичный момент — поведение при аварии. Обычно выбирают либо fallback в Docker Hub, либо строгий режим «только через cache», если вы хотите полностью контролировать цепочку поставки и исключить прямые pull наружу.

Если поднимаете cache с собственным TLS, заранее продумайте выпуск и продление сертификата: для продакшна проще и надёжнее использовать публично доверенный сертификат (или централизованно раздать корпоративный CA). В типичных инфраструктурах это как раз тот случай, когда удобно иметь управляемые SSL-сертификаты без ручной возни на каждом узле.

Пример настройки containerd hosts.toml для зеркала docker.io и fallback на Docker Hub

Частые ошибки, из-за которых mirror/cache «не работает»

Ошибка 1: зеркало настроили, но образы всё равно тянутся из Docker Hub

Проверьте:

  • Какой хост реально используется при pull: docker.io и registry-1.docker.io могут по-разному матчиться на конфиг.

  • Порядок endpoints: cache должен стоять первым.

  • Перезапуск containerd выполнен на всех нодах.

  • Сетевую доступность cache с нод (DNS, маршрутизация, firewall).

Ошибка 2: вместо ImagePullBackOff появились TLS/сертификаты

Если вы подняли свой registry с нестандартным сертификатом, containerd может ругаться на доверие. Решение: публично доверенный сертификат или установка корпоративного CA в доверенные на каждой ноде, плюс корректные TLS-параметры в настройках containerd для нужного хоста.

Ошибка 3: cache есть, но «не кэширует»

Убедитесь, что включён именно режим proxy cache/pull-through (а не просто «обычный registry»). Для Harbor это отдельная настройка proxy cache проекта/реестра; для Distribution — отдельный режим конфигурации.

Диагностика: чек-лист на 5 минут

  1. События Pod: kubectl describe pod, ищем toomanyrequests и rate limit.

  2. Pull с ноды: crictl pull docker.io/....

  3. Определяем runtime (containerd/CRI-O) и где лежат его конфиги.

  4. Временно уменьшаем число pull и параллельно внедряем cache.

  5. Добавляем mirror/cache в настройки registry для containerd, перезапускаем runtime на всех нодах.

  6. Повторяем crictl pull и перезапускаем проблемный Deployment.

Практика эксплуатации registry cache

Чтобы proxy cache действительно решал проблему и не стал новой точкой отказа:

  • Диск и IOPS важнее CPU: образы — это слои и метаданные, нагрузка часто упирается в хранилище.

  • Мониторинг: свободное место, задержки диска, ошибки 5xx, количество запросов, размер кэша.

  • Политики очистки: кэш не должен расти бесконечно; включите retention/GC и лимиты.

  • План на аварии: заранее решите, допускаете ли fallback в Docker Hub или блокируете pull без cache.

Если хотите глубже разобраться, как именно хранить слои и эффективно отдавать их через Nginx (и не убить диск), пригодятся материалы про кэширование и форматы изображений, например как настроить Nginx map cache для WebP/AVIF.

Итог

ImagePullBackOff из-за Docker Hub pull rate limit — не редкий «сбой», а закономерность для Kubernetes, который масштабируется и постоянно пересоздаёт ноды/поды. Самый надёжный способ снизить зависимость — поставить registry cache (Harbor/Quay/Distribution) и подключить его через containerd registry mirrors, а затем дополнительно уменьшить количество лишних pull настройками деплоя.

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

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

OpenSSH hardening 2026: KEX, hostkey, CA подписи и FIDO2-ключи без боли для легаси OpenAI Статья написана AI (GPT 5)

OpenSSH hardening 2026: KEX, hostkey, CA подписи и FIDO2-ключи без боли для легаси

Пошагово поджимаем OpenSSH в 2026: инвентаризация через sshd -T, выбор современного KEX и hostkey, запрет ssh-rsa без поломки RSA ...
systemd-networkd: policy routing и таблицы маршрутизации (ip rule, multiple gateways, source routing) OpenAI Статья написана AI (GPT 5)

systemd-networkd: policy routing и таблицы маршрутизации (ip rule, multiple gateways, source routing)

Разберём, как настроить policy routing через systemd-networkd: отдельные таблицы маршрутов, правила ip rule по исходному адресу и ...
Kubernetes StorageClass, PV и PVC: почему PVC Pending и как это чинить (provisioner, topology, access modes) OpenAI Статья написана AI (GPT 5)

Kubernetes StorageClass, PV и PVC: почему PVC Pending и как это чинить (provisioner, topology, access modes)

Если PVC в Kubernetes остаётся в Pending, значит подходящий PV не найден или не создан. В статье разбираем цепочку StorageClass → ...