Kubernetes 1.16 удалил extensions/v1beta1 - и наши Helm-чарты упали
Kubernetes 1.16 убрал устаревшие API extensions/v1beta1 - Deployment, DaemonSet, Ingress сломались. Пришлось срочно переписывать Helm-чарты перед обновлением.
Kubernetes 1.16 вышел 18 сентября 2019: удалены устаревшие API extensions/v1beta1, CRD достигли GA в apiextensions.k8s.io/v1, улучшен kubectl
Kubernetes 1.16 вышел 18 сентября. Мы читали release notes заранее, видели, что там deprecation removal - и всё равно обновление прилетело болезненнее, чем ожидали. Не потому что мы не знали. А потому что оказалось, что «знать» и «проверить все чарты» - это два разных дела.
Расскажем как это было на одном из клиентских кластеров в managed-инфраструктуре.
Что именно убрали
В 1.16 из Kubernetes вырезали группу API, которую помечали deprecated ещё с 1.9. Это не предупреждение - это удаление. Конкретно перестали работать:
extensions/v1beta1- Deployment, DaemonSet, ReplicaSet, NetworkPolicy, PodSecurityPolicy, Ingressapps/v1beta1иapps/v1beta2- Deployment, DaemonSet, ReplicaSet, StatefulSet
Правильные замены существуют давно: apps/v1 для Deployment/DaemonSet/StatefulSet, networking.k8s.io/v1beta1 для Ingress. Но если ваши манифесты или Helm-чарты написаны год-два назад и с тех пор не трогались - там, скорее всего, стоит extensions/v1beta1. Потому что так было в примерах из документации до 1.9.
Параллельно CRD перешли в GA - apiextensions.k8s.io/v1 вместо v1beta1. CRD-манифесты операторов тоже надо обновлять, хотя там пока действует более мягкий режим совместимости.
Как мы в это влетели
Кластер на 1.15, плановое обновление до 1.16 в тестовой среде. После обновления запустили helm upgrade для нескольких чартов - получили пачку ошибок вида:
Error: unable to build kubernetes objects from release manifest:
[unable to recognize "": no matches for kind "Deployment"
in version "extensions/v1beta1"]
Интересный момент: Helm 2 с Tiller берёт манифесты из хранилища релизов и применяет их. Если чарт написан с extensions/v1beta1 - именно это и попадёт в кластер. Когда API-группы нет - всё, upgrade не проходит.
Подсчитали: у нас в production-кластерах несколько десятков Helm-релизов. Не все сломанные, но проверить нужно каждый - потому что заранее не очевидно, в каких именно чартах написан старый apiVersion.
Как проверяли
Первым делом прогнали kubectl api-versions на обновлённом тестовом кластере - убедились, что extensions/v1beta1 действительно исчез. Потом написали простой скрипт, который рендерит каждый чарт через helm template и грепает на extensions/v1beta1|apps/v1beta1|apps/v1beta2:
for release in $(helm list --short); do
helm get manifest $release | grep -l "extensions/v1beta1\|apps/v1beta"
done
Нашли проблемные. Оказалось, что больше всего старых apiVersion - в чартах, которые давно работают и давно не обновлялись. Nginx Ingress Controller, несколько внутренних деплойментов, один DaemonSet с node-экспортером.
Что переписывали
По каждому проблемному чарту замена в целом прямолинейная:
- Deployment, DaemonSet, ReplicaSet, StatefulSet -
extensions/v1beta1меняем наapps/v1. Но здесь есть нюанс: вapps/v1у Deployment и DaemonSet полеselectorобязательное и immutable. Если оно не было прописано в старом манифесте явно - нужно добавить и убедиться что оно совпадает сtemplate.metadata.labels. Иначе Kubernetes откажет с ошибкой при применении. - Ingress - меняем на
networking.k8s.io/v1beta1. Здесь в 1.16 это ещё beta, не GA - но deprecated Ingress из extensions убрали. - CRD -
apiextensions.k8s.io/v1beta1пока работает в 1.16, но поля схемы стали структурированными. Операторские чарты с CRD лучше обновить доv1и добавитьspec.versionsвместоspec.version.
На внешние чарты (от вендоров) смотрели что пришло в обновлённых версиях. nginx-ingress, например, уже выпустил обновлённые версии chart с правильными apiVersion - просто обновили зависимость.
Чего не хватало при диагностике
kubectl apply с новыми ресурсами на 1.16 кластере даёт понятные ошибки. Но helm upgrade - менее информативен: он сначала рендерит манифесты из chart, потом применяет через Tiller, и ошибка иногда теряется в стектрейсе. Пришлось добавить промежуточный шаг: helm template | kubectl apply --dry-run -f -. Dry-run на 1.16 кластере сразу показывает что именно не принимает API.
Ещё момент: kubectl deprecations как отдельной команды нет. Есть kubectl api-resources и kubectl explain чтобы смотреть что актуально. Отдельного инструмента для сканирования манифестов на deprecated API под рукой не было - kubectl api-resources и kubectl explain делают это вручную, но не в пакетном режиме.
Что из этого следует
Обновление прошло, кластер работает на 1.16. Но общий вывод неприятный: мы были в курсе про deprecation removal, но не систематизировали проверку заранее. Посмотрели release notes, отметили что надо, и занялись другими делами.
Правильный ритм выглядит так - при каждом minor-обновлении Kubernetes, за 2-4 недели до применения, прогонять все чарты через helm template и проверять apiVersion на соответствие тому что в новой версии. Не полагаться на то что «эти чарты работают уже год, значит всё в порядке». Год назад apiVersion был нормальный - сейчас его нет.
Команда Kubernetes предупреждала об этих удалениях начиная с 1.9. Семь версий. Тем обиднее было разбираться в авральном режиме - это была чисто наша недоработка по процессу.