Kubernetes
Kubernetes в продакшене: апгрейды, операторы, StatefulSet, пробы и отказы, которые проявляются только на живых кластерах.
36 статей
- Calico в Ready и без ошибок, а каждый новый под полминуты недоступенПосле апгрейда Calico свежие поды 15–42 секунды сидели в чёрной дыре. Felix крутился в цикле XDP-resync, который видно только тестом «новый под ↔ новый под между нодами».
- Kubernetes с 1.32 до 1.37 за день, пять хопов, без kubespray, которым кластер собиралиBare-metal кластер на EOL-версии, потерянный инвентарь kubespray и бинарники вне пакетного менеджера. Как прошли пять хопов kubeadm за один день и грабли, которых не было ни в одном плане.
- Минуту работало, потом таймауты: MetalLB L2 с kube-proxy ipvs без strictARPПеревод хостов на новый LoadBalancer IP работал недолго, потом внешний балансировщик увидел таймауты на L4. На ARP за один и тот же IP отвечали все ноды.
- Сертификаты истекли по расписанию. Алерт неделю кричал — в пустотуkubeadm-кластер потерял control plane ровно через год после установки. Алерт об истечении горел семь дней, но у Alertmanager был только receiver null.
- Kubernetes 1.33 → 1.36 и Calico → Cilium за один деньРепетиция на домашней лаборатории: три хопа Kubernetes, замена CNI на Cilium без kube-proxy и отказ от ретированного ingress-nginx в пользу Gateway API — почему контролируемый big-bang оказался безопаснее живой миграции и какие грабли стоит знать заранее.
- multipathd захватил том Longhorn: «mount point busy», хотя ничего не смонтированоПод пять дней висел в Init, 2413 попыток монтирования, полный ребут не помог. multipathd построил карту поверх iSCSI-устройства Longhorn и держал его открытым.
- Argo CD показывал Synced, а API-сервер молча выкинул мой фиксМесяцы без метрик, фикс уже в git и зелёный Synced. Поля не было в схеме CRD, API-сервер его отрезал, и никто не пожаловался.
- Liveness-проба, привязанная к чужой сети: 330 рестартов, которые ничего не чинилиДве одинаковые реплики cloudflared — 330 и 13 рестартов. Liveness проверяла туннель до edge, поэтому каждая икота аплинка превращалась в SIGKILL.
- Kafka на 94 % диска, 17 часов в запасе и retention, который хранил вдвое большеТрафик вырос в 150 раз, а тома никто не пересматривал. Хуже того, при дефолтном роллинге сегментов retention в 7 дней на деле означал 7–14. Как мы выиграли время и устранили причину.
- Basic auth пропал на 80-м порту после переезда с Ingress на Gateway APIВсе статусы зелёные, мой curl возвращал 401. А вход в WordPress был открыт всем: Gateway API привязывает политику к одному listener'у, а аннотации Ingress покрывали все порты.
- NotReady шесть часов, а нода всё это время была живаВсе сигналы говорили, что нода мертва. На деле она задыхалась от нехватки памяти, а очевидный фикс — удалить ноду и принудительно отцепить том — был в одной команде от порчи базы.
- Звёздочка в середине пути Vault — не wildcardПротухший токен уронил оператор секретов. Его перевыпуск вскрыл более старый баг: 27 политик Vault со * в середине пути молча запрещали то, что объявляли, и прятали избыточные права.
- Реплика Postgres 23 часа крутилась в цикле pg_rewind: пересоздавать, а не чинитьПосле failover реплика CloudNativePG не смогла сделать rewind: её WAL-сегменты были нулевого размера, а каталог данных — в десять раз меньше, чем у primary. 23 часа никто не заметил, потому что метрики оператора не собирались.
- Профилируем утечку памяти в Python на проде с memray — и авария, которую мы устроили по дорогеБэкенд падал по OOM каждые 10–15 часов. Почасовые снимки memray на живом поде нашли утечку в библиотечных middleware, а не в коде приложения — после того как первая попытка положила прод на семь минут.
- Переезд продового кластера с Longhorn на LINSTOR — и ожидание, которое не сбылосьЗачем мы заменили Longhorn на LINSTOR/DRBD на self-managed кластере, как каждая нагрузка переезжала своим способом и что пошло не так по дороге.
- С ingress-nginx на Envoy Gateway без простоя: 27 Ingress, 39 HTTPRouteingress-nginx ретирован и блокирует апгрейды Kubernetes. Как мы перевели 16 публичных хостов на Envoy Gateway: подняли новый вход рядом со старым и в самом конце поменяли только адрес.
- Перевод CloudNativePG на плагин Barman Cloud без разрыва архиваCNPG убирает встроенную поддержку barmanObjectStore. У переключения нет валидного промежуточного состояния, у базы не было ни одного базового бэкапа, а один путь решает, работает ли ещё point-in-time recovery.
- Четыре проверки, которые врали, пока система была здороваЗа один день миграций платформы четыре проверки сообщали об ошибке, хотя всё было в порядке. Когда один сигнал спорит со всеми остальными, первым подозревайте сам сигнал.
- Алерты, которые не могут сработать: слепой ServiceMonitor и мёртвый канал доставкиПир Vault 32 часа был sealed, а секреты неделю не синхронизировались — при алерте VaultSealed в репозитории и здоровом Alertmanager. Инцидент спрятали два независимых сбоя.
- «Дрейф ядер» оказался дрейфом аптайма: патчи ставились, ребутов не былоТри разных ядра на семи нодах выглядели как незавершённые волны патчей и увели расследование по ложному следу. Патчи на всех нодах были одинаковые, различался только аптайм.
- kubectl patch --type merge стёр CoreDNS, а все проверки остались зелёнымиОднострочное обновление образа заменило всю спецификацию контейнера. CoreDNS потерял конфиг, тихо откатился на плагин whoami и отвечал на всё NOERROR без ответа.
- Замена MetalLB на Cilium LB IPAM без единого потерянного запросаВ кластере был ровно один LoadBalancer IP, и от него зависели все маршруты. Как я передал его от MetalLB к Cilium без единой ошибки — дав обоим контроллерам поработать одновременно.
- Перевод Vault с Shamir на transit auto-unseal и четыре ловушки по дорогеКаждый рестарт пода Vault заканчивался запечатанным хранилищем и замороженной синхронизацией секретов. Как мы добавили transit-провайдер, мигрировали raft-кластер из трёх нод и на чём споткнулись.
- Апгрейд GitLab и два сигнала, которые вралиРядовой минорный апгрейд self-hosted GitLab в Docker. Предварительная проверка насчитала 32 незавершённые миграции, а контейнер стал healthy, ничего не обслуживая.
- Как обновить ОС на семи нодах Kubernetes так, чтобы никто не заметилПоочерёдное обновление Ubuntu в кластере, который хранит всё своё состояние сам. Предварительные проверки превратили страшную работу в скучную, а темп задавал Longhorn.
- Одна забытая DHCP-опция положила весь Kubernetes-кластерПосле ребута хоста около пятидесяти подов ушли в крашлуп, хотя все ноды были Ready. Причина — забытая DHCP-опция classless routes, которая молча убрала шлюз по умолчанию.
- ONLINE_UNCERTAIN: когда MySQL-оператор и база не согласныRouter флапает, оператор два дня висит в блокировке, CRD застыл на 2/3 — а все три члена InnoDB Cluster здоровы. Лечится одним rescan метаданных.
- Vault Secrets Operator замолчал после seal — и рестарт его не разбудил38 из 43 VaultStaticSecret два дня висели с замёрзшей ошибкой. Unseal Vault и рестарт оператора ничего не дали; помогло изменение одного поля в spec.
- Петля логина в WordPress, которая случалась только иногдаwp-admin выкидывал обратно на страницу логина с reauth=1, но не всегда. Причина — случайные auth-соли, сгенерированные в каждом поде за балансировщиком без sticky-сессий.
- Три инцидента, которые были одним: IP базы, зашитый в конфиг500 на логине, бэкенды в CrashLoopBackOff и зависший деплой выглядели как три проблемы. Все три оказались одним захардкоженным IP базы в конфиге pgbouncer.
- «Ошибки диска» оказались OOM: как тома Longhorn потеряли бэкендEIO, ext4 в read-only, повреждённый datadir MySQL и запечатанный Vault выглядели как умирающий диск. dmesg хоста показал OOM на всю ноду, который первым убил instance-manager Longhorn.
- «Vault сам себя запечатывает» — нет, он просто никогда не распечатываетсяПод Vault днями висел в Ready 0/1, пока кворум raft тихо держался на двух пирах. Никто его не запечатывал: Shamir без auto-unseal оставляет запечатанным каждый рестарт.
- Том Longhorn, который не хотел расти: attachment ticket, прибитый к плохой нодеPVC бесконечно висел в Resizing, а Longhorn каждые пять секунд повторял расширение без единой ошибки. Устаревший attachment ticket держал том на одной нездоровой ноде.
- Меняем неизменяемое поле StatefulSet, не перезапустив ни одного подаВ Git — 20 GiB, в живом StatefulSet — шаблоны на 6 GiB, а Argo CD показывает Synced. Как удаление с orphan и OnDelete пересобрали шаблон без простоя.
- Пять дней на 1/3 при зелёных дашбордах: когда статус CRD врётMySQL InnoDB Cluster пять дней работал без HA, а статус ресурса, фаза подов и readiness gate показывали здоровье. Все три сигнала шли от одного зависшего оператора.
- Helm пишет «another operation is in progress», а ничего не запущеноОтменённая CI-джоба оставила два Helm-релиза в pending-upgrade и заблокировала все деплои. Как найти lock, безопасно его снять и проверить весь namespace разом.