Обновляем кластеры до Kubernetes 1.26: миграция deprecated API и проверка Helm-чартов
Kubernetes 1.26 GA - удалены in-tree облачные провайдеры, containerd как основная CRI. Проходим полный цикл обновления продуктивных кластеров с разбором граблей.
Kubernetes 1.26 GA - удалены устаревшие in-tree облачные провайдеры, containerd стал основной CRI-реализацией, удалены FlowSchema v1beta1, HPA v2beta2 и ряд других API
Kubernetes 1.26 вышел в GA 9 декабря - раньше, чем мы рассчитывали. Хорошая новость: благодаря тому, что в ноябре прогнали pluto по всем кластерам ещё на RC-этапе, список сюрпризов оказался управляемым. Плохая новость: managed-кластеры всё равно преподнесли пару неочевидных моментов, о которых в ноябрьском посте мы ещё не знали.
Что убрали в 1.26 окончательно
Для тех, кто не следил за RC: список удалённых API в 1.26 включает autoscaling/v2beta2 (HPA), flowcontrol.apiserver.k8s.io/v1beta1 (FlowSchema и PriorityLevelConfiguration), CSIStorageCapacity v1beta1. Всё это были deprecated-позиции с объявленным дедлайном - неожиданностью стать не должны были.
Отдельная история - in-tree облачные провайдеры. AWS, GCP, Azure, OpenStack и часть других убраны из ядра. Если кластер использовал in-tree интеграцию (через параметр --cloud-provider в kubelet и controller-manager), теперь нужны внешние cloud-controller-manager. Нас это напрямую не касается - у клиентов либо bare-metal, либо уже давно на external CCM. Но если у вас self-hosted кластер поверх облака и вы ставили его пару лет назад без оглядки на этот момент - проверьте.
Сам процесс обновления
Обновляли несколько кластеров последовательно: сначала dev/staging, потом прод. Стандартный порядок: control plane узлы один за другим, потом worker-ноды с drain/upgrade/undrain.
Перед обновлением каждого кластера - финальная проверка:
pluto detect-helm --target-versions k8s=v1.26.0- ещё раз по установленным Helm-релизам. После ноябрьского прогона часть чартов уже обновили, но не все.kubectl get --raw /metrics | grep apiserver_requested_deprecated_apis- смотрим, что реально вызывается через deprecated API прямо сейчас. pluto ловит статические манифесты, а этот счётчик показывает live-трафик. Нашли один неожиданный момент - собственный скрипт клиента для бэкапа etcd дёргал какой-то deprecated endpoint черезkubectlстарой версии. Скрипт не трогали года два.- Версии containerd на всех узлах -
kubectl get nodes -o wideплюсcrictl versionна каждом воркере. На двух кластерах всё ещё оставался containerd 1.5.x - обновляли до 1.6 заранее, не дожидаясь основного обновления k8s.
Грабли с Helm-чартами
После ноябрьского аудита казалось, что с чартами всё под контролем. Оказалось - не совсем.
Первое - чарт, который обновился в апстриме, но не у клиента. Мы в ноябре зафиксировали что надо обновить до свежей версии чарта, но в декабре обнаружили что в клиентском GitOps-репозитории обновление было закоммичено, но не применено - Flux отложил синк из-за другой ошибки в репозитории, которую никто не заметил. Перед обновлением k8s пришлось разбираться со статусом Flux сначала.
Второе - Helm-релиз, собранный с --set флагами без values-файла. pluto detect-helm смотрит в секрет с кешированным манифестом релиза - там был корректный apiVersion. Но у клиента оказался CI-скрипт, который переустанавливал этот релиз с явным apiVersion: autoscaling/v2beta2 в --set. Плюс нашли это только потому, что деплой упал уже после обновления кластера с сообщением об удалённом API. Урок: проверяй не только манифесты в git, но и скрипты деплоя.
Третье - CRD от стороннего оператора. Один клиент использует оператор для управления сертификатами, который хранит своё состояние в CRD. Сам оператор поддерживает 1.26, его новая версия вышла ещё в октябре. Но при обновлении оператора обнаружилось что у него migration job - и эта job надо запустить до обновления k8s, а не после. В документации оператора это написано, но не очевидно. Обошлось, но потребовало дополнительного maintenance-window.
containerd 1.6: что по факту
На кластерах где containerd уже был 1.6 - обновление k8s прошло без каких-либо инцидентов со стороны CRI. На двух кластерах где обновляли containerd непосредственно перед k8s - тоже без проблем. Процедура: drain ноды, systemctl stop containerd, apt upgrade containerd.io (или через dnf, зависит от дистрибутива), systemctl start containerd, проверка crictl info, undrain.
Единственный момент: на одном из кластеров на RедОС после обновления пакета containerd изменился путь к конфигу - /etc/containerd/config.toml был кастомный (прописаны зеркала registry), и дефолтный пакет перезаписал его. Настройки пришлось восстанавливать. Теперь проверяем конфиги containerd через etckeeper или просто держим их в git.
Текущий статус
Из четырёх кластеров три уже на 1.26, один - клиент перенёс обновление на январь по внутренним причинам. На обновлённых кластерах пока стабильно, никаких регрессий в workload не замечено.
Следующий вопрос - 1.27, но это уже следующий год. Главный вывод из 1.26: аудит deprecated API на этапе RC реально помог, но не заменяет проверку скриптов CI/CD и процедуры операторов. Managed-сопровождение как раз про это - не только обновить, но и не пропустить то, что находится рядом с манифестами.