Задача
kubectl get pods отрабатывает по несколько секунд. Создание объектов иногда падает по таймауту. При этом ноды почти не загружены, а рабочие нагрузки продолжают работать.
В логах API server — сообщения о медленных запросах к etcd.
Вопрос: что могло привести к такому состоянию и почему рабочие нагрузки при этом живы?
Что кажется очевидным
«Не хватает ресурсов control plane, надо добавить CPU мастерам».
Иногда помогает, но чаще причина в другом — в том, что и как пишут в etcd.
Как это работает на самом деле
etcd — распределённое key-value хранилище, единственный источник правды для всего кластера. В нём лежит каждый объект: поды, ConfigMap’ы, секреты, события, CRD.
Почему рабочие нагрузки живы: уже запущенные контейнеры работают под управлением kubelet и не нуждаются в etcd. Ломается управление кластером, а не выполнение нагрузок. Но новые поды не создаются, а восстановление после сбоя становится невозможным.
Ключевые ограничения, о которые бьются:
1. Размер одного объекта — около 1.5 МиБ. Это лимит на запись в etcd. Попытка создать ConfigMap или Secret крупнее упрётся в ошибку. Соблазн «положить датасет/сертификаты/бинарник в ConfigMap» разбивается об это ограничение — и правильно.
2. Размер базы — по умолчанию 2 ГиБ (--quota-backend-bytes, максимум рекомендуемый — 8 ГиБ). При достижении квоты etcd переходит в режим только для чтения и весь кластер перестаёт принимать изменения. Это самый неприятный сценарий.
3. etcd хранит историю ревизий. Каждое изменение объекта создаёт новую ревизию, старые остаются до компакции. Объект, который обновляется в цикле, генерирует тысячи ревизий и раздувает базу — даже если сам объект маленький и его текущий размер не меняется.
4. Watch-и рассылают изменения. На каждое изменение объекта API server рассылает событие всем подписчикам (kubelet, контроллеры, операторы). Горячо обновляемый объект создаёт нагрузку не только на etcd, но и на всех наблюдателей.
5. etcd чувствителен к диску. Он делает fsync на каждую запись — медленный или «плавающий» по латентности диск напрямую превращается в тормоза всего кластера.
Типичные виновники
- Контроллер/оператор с багом, обновляющий статус объекта в бесконечном цикле — классика. Каждая итерация = ревизия + рассылка watch.
- Гора событий (
Events). Они хранятся в etcd; при массовых перезапусках их становятся сотни тысяч. - Огромные ConfigMap/Secret, читаемые многими подами.
- CRD с толстыми объектами, которые часто пишутся.
- Забытые namespace’ы с тысячами объектов.
Ответ
Скорее всего, что-то интенсивно пишет в etcd: контроллер в цикле обновления, лавина событий или крупные часто обновляемые объекты. База растёт, история ревизий не успевает компактиться, латентность записи растёт — и все операции API замедляются. Нагрузки живы, потому что зависят от kubelet, а не от etcd.
Что с этим делать
Диагностика
# Размер базы и состояние узлов
etcdctl endpoint status --write-out=table --cluster
# Сколько объектов каждого типа (частый источник сюрпризов)
kubectl get events -A --no-headers | wc -l
kubectl get secrets -A --no-headers | wc -l
# Метрики API server: что пишется чаще всего
# etcd_object_counts / apiserver_storage_objects
kubectl get --raw /metrics | grep apiserver_storage_objects | sort -t' ' -k2 -rn | head -20Последняя команда обычно и указывает на виновника: если объектов одного типа десятки тысяч, вопрос закрыт.
Лечение и профилактика
- Компакция и дефрагментация. Kubernetes компактит историю автоматически (
--etcd-compaction-interval), но освобождённое место возвращается файловой системе только после дефрагментации:Выполняйте по одному узлу за раз — во время дефрагментации узел не обслуживает запросы.BASHetcdctl defrag --cluster - Ограничьте события. У них есть TTL (
--event-ttl, по умолчанию час). Если событий всё равно много — сократите TTL или вынесите их в отдельный etcd (--etcd-servers-overrides). - Быстрый диск под etcd. Отдельный SSD/NVMe с низкой латентностью, не разделяемый с другими нагрузками. Это самая частая инфраструктурная причина тормозов.
- Не храните в ConfigMap/Secret большие данные. Для файлов — объектное хранилище или образ; в кластер класть только ссылки и конфигурацию.
- Ревизуйте свои операторы. Обновление статуса должно происходить только при фактическом изменении, иначе получится цикл. Это самый частый баг самописных контроллеров.
- Мониторьте: размер базы относительно квоты,
etcd_disk_wal_fsync_duration_seconds(латентность fsync), число объектов по типам, частоту записей. Алерт на приближение к квоте обязателен — переход в read-only останавливает весь кластер. - Резервные копии. Снапшот etcd — это и есть бэкап control plane:И, как всегда, проверьте восстановление заранее.BASH
etcdctl snapshot save /backup/etcd-$(date +%F).db
Дальше: Задача 10. HPA: почему автомасштабирование не срабатывает.