Задача
Кластер обновили на две минорные версии вперёд. Приложения продолжают работать, но CI/CD падает:
error: resource mapping not found for name: "api" namespace: "prod"
from "deployment.yaml": no matches for kind "Deployment" in version "apps/v1beta2"Манифест не меняли годами, и раньше он применялся без проблем.
Вопрос: что произошло и почему работающие приложения не пострадали?
Что кажется очевидным
«Обновление сломало совместимость». Формально да, но важно понять механику — она объясняет, почему предупреждения были, а их не заметили.
Как это работает на самом деле
API в Kubernetes проходят жизненный цикл, и у него две принципиально разные стадии:
Deprecated (устарел). API помечен как устаревший, но продолжает работать. При обращении возвращается предупреждение, которое kubectl печатает в stderr:
Warning: apps/v1beta2 Deployment is deprecated in v1.16+, unavailable in v1.22+;
use apps/v1 DeploymentRemoved (удалён). API перестаёт существовать. Запросы к нему возвращают ошибку — ровно ту, что в задаче.
Между этими стадиями обычно проходит несколько релизов, и всё это время сервер честно предупреждает. Проблема в том, что предупреждения уходят в stderr пайплайна и никто их не читает — пока не станет поздно.
Почему приложения не пострадали
Ключевой момент: API-версия — это способ представления объекта, а не способ его хранения. Объект, созданный как apps/v1beta2, хранится в etcd в единой внутренней форме и после удаления старой версии остаётся доступен через актуальную — apps/v1.
То есть уже созданные Deployment’ы продолжают работать. Ломается только применение манифестов со старым apiVersion. Именно поэтому симптом проявляется в CI, а не в проде.
Правило version skew
Компоненты кластера не обязаны иметь одинаковую версию, но допустимый разброс ограничен:
- kubelet может отставать от API server на несколько минорных версий, но не опережать его;
- kube-controller-manager и kube-scheduler не должны опережать API server;
- kubectl допускает расхождение с сервером примерно на одну минорную версию в обе стороны.
Отсюда главное правило обновления: сначала control plane, потом узлы, и строго по одной минорной версии за раз. Перепрыгивать через версии нельзя — это ломает гарантии совместимости и миграции хранимых объектов.
Ответ
API-группа apps/v1beta2 была помечена устаревшей, а затем удалена. Существующие объекты продолжили работать через актуальную версию apps/v1, но манифесты со старым apiVersion перестали применяться.
Что с этим делать
Найти проблемы заранее
# Что говорит сервер о версиях
kubectl api-resources
kubectl api-versions
# Предупреждения при применении — не игнорируйте их
kubectl apply -f manifests/ --dry-run=server 2>&1 | grep -i warnФлаг --dry-run=server особенно ценен: он прогоняет манифесты через реальный API server со всеми валидациями и предупреждениями, ничего не изменяя. Хорошая практика — держать такую проверку отдельным шагом в CI.
Для планового аудита существуют специализированные инструменты (например, kubent/pluto), которые сканируют манифесты и объекты кластера на использование устаревших API и показывают, в какой версии они исчезнут.
Порядок безопасного обновления
- Прочитать release notes целевой версии — раздел про удалённые API и изменения поведения. Это не формальность: между минорными версиями меняются дефолты.
- Проверить совместимость дополнений: CNI, CSI-драйверы, ingress-контроллер, операторы, service mesh. Часто именно они, а не сам Kubernetes, определяют, когда можно обновляться.
- Обновить манифесты на актуальные API-версии и убедиться, что всё применяется на текущем кластере.
- Прогнать на тестовом кластере той же конфигурации.
- Обновить control plane (по одной минорной версии).
- Обновить узлы — по одному, через
drainс уважением к PDB (см. задачу 8). - Проверить: все поды поднялись, метрики в норме, ключевые сценарии работают.
Managed-кластеры
В облаках обновление control plane обычно автоматизировано, но узлы остаются вашей ответственностью: стратегия обновления пула нод, PDB, окно обслуживания. Отдельная деталь — окна автообновления: провайдер может обновить кластер сам по истечении срока поддержки версии. Если ваши манифесты не готовы, узнать об этом в момент автоматического обновления — плохой сценарий.
Профилактика
- Не игнорируйте warning’и. Настройте CI так, чтобы предупреждения об устаревших API были заметны — вплоть до блокировки сборки.
- Держите манифесты в актуальных версиях и обновляйте их регулярно, не дожидаясь удаления.
- Не отставайте больше, чем на пару версий. Kubernetes выпускает релизы часто, поддержка каждой версии ограничена по времени; догонять с большим отставанием мучительно, потому что обновляться придётся многократно и последовательно.
- Фиксируйте версии инструментов в CI:
kubectl, Helm, плагины. Расхождение версийkubectlи сервера — источник странных ошибок.
Итог серии
Двадцать задач — и почти в каждой ответ сводился к одному: декларативная простота Kubernetes скрывает механику, которая проявляется под нагрузкой и в отказах. Под Running не значит готов; удаление пода не мгновенно; requests важнее, чем кажется; namespace не изолирует ресурсы; балансировка идёт по соединениям, а не по запросам.
Если из всей серии оставить один практический вывод: задавайте requests и readinessProbe. Эти два поля прямо или косвенно участвуют в доброй половине разобранных ситуаций — от endpoints и rolling update до вытеснения и автомасштабирования.