Kubernetes 1.15: CRD versioning помог мигрировать схему операторов без downtime
Обновили кластеры до Kubernetes 1.15: versioning в CRD позволил плавно мигрировать схему операторов, а kubectl diff наконец показывает что изменится до apply.
Kubernetes 1.15 - улучшенная расширяемость API, CRD versioning, обновления kubectl
Kubernetes 1.15 вышел в конце июня. Мы обновили несколько managed-кластеров и хотим зафиксировать одно наблюдение, которое пришло не из release notes, а из живого опыта за неделю после обновления.
Что в 1.15 нас интересовало
Релиз получился не таким «громким», как 1.14 с Windows GA, - нет одного большого анонса, который перекрывает всё остальное. Зато здесь прибрали накопившийся долг в нескольких местах сразу.
Нас в первую очередь интересовали три вещи:
- CRD versioning - возможность держать несколько версий API в одном CRD одновременно, с преобразованием между ними.
- kubectl diff - из экспериментального флага вырос в полноценный подкоманду.
- Улучшения в Admission Webhook - в частности,
defaultingчерез MutatingAdmissionWebhook стал предсказуемее в плане порядка вызовов.
Мигрируем CRD: откуда боль
У нас есть несколько операторов собственной разработки - один из них описан в посте про паттерн операторов. Все они зарегистрированы с версией v1alpha1, потому что это честный способ сказать «схема может меняться». И она менялась - по мелочи, но менялась.
Проблема в том, что до 1.15 в CRD была ровно одна version. Как только меняешь поле в схеме - клиенты на старой версии перестают работать. Если у тебя несколько кластеров, несколько операторов и несколько команд, которые пишут CR-манифесты, это превращается в синхронизационный цирк: «все обновляемся одновременно, иначе всё ломается».
Мы обходили это тем, что делали поля с opaque object без строгой валидации, оставляли устаревшие поля в схеме навсегда и документировали что «это поле игнорируется». Технический долг в чистом виде.
CRD versioning в 1.15
В 1.15 в apiextensions.k8s.io/v1beta1 появился раздел versions вместо единственного version. Теперь CRD описывается так:
spec:
group: adg.io
names:
kind: PostgreSQLCluster
plural: postgresqlclusters
scope: Namespaced
versions:
- name: v1alpha1
served: true
storage: false
- name: v1alpha2
served: true
storage: true
storage: true - это версия в которой объект хранится в etcd. served: true - значит API-сервер принимает запросы с этой версией. Можно держать обе версии активными одновременно: старые клиенты шлют v1alpha1, новые - v1alpha2, API-сервер конвертирует на лету через webhook конвертации.
Мы попробовали на одном из операторов. В v1alpha1 у нас поле называлось primarySelector - Map с лейблами. В v1alpha2 мы переименовали его в leaderSelector и добавили поле healthCheckInterval. Написали конвертационный webhook (простой Go-сервис с двумя хендлерами - туда и обратно), зарегистрировали его в CRD.
Миграция прошла так:
- Задеплоили новую версию CRD с обеими версиями и webhook-ом конвертации.
- Обновили оператор чтобы он читал
v1alpha2. - Подождали несколько дней пока команда обновила свои манифесты.
- Выключили
served: trueдляv1alpha1.
Downtime: ноль. Всё время пока шла миграция - оба формата работали. Это качественно отличается от того что было раньше.
kubectl diff: наконец-то нормально
Раньше kubectl diff существовал как kubectl alpha diff и работал по принципу «иногда работает, иногда нет, читай issue». Теперь это просто kubectl diff -f deployment.yaml.
Показывает unified diff между тем что в кластере и тем что в файле, прямо как git diff. С учётом defaulting со стороны API-сервера - то есть видишь реальные изменения, а не шум из полей которые сервер добавляет сам.
Мы сразу включили это в наш workflow перед каждым kubectl apply на production. Отдельно полезно для CRD-изменений - видишь что именно поменяется в объектах до того как применил. Казалось бы очевидная штука, но именно сейчас она стала достаточно надёжной чтобы на неё положиться.
Что ещё заметили
Startup probe - в 1.16 обещают, в 1.15 уже есть как alpha за флагом. Мы не включали, но отметили - это ответ на давний паттерн «liveness probe убивает контейнер во время инициализации», который мы обходим через увеличенный initialDelaySeconds. Подождём до GA.
Производительность API-сервера - по ощущениям время отклика на больших кластерах стало немного лучше. Но это субъективно и без бенчмарков утверждать не будем.
Scale subresource для CRD теперь работает в v1beta1 без дополнительных флагов. Пригодится если хотите kubectl scale для кастомных ресурсов.
Итого
Обновление прошло штатно на всех кластерах. Никаких сюрпризов с deprecated API - мы заранее прошлись по манифестам. CRD versioning - это та фича, которую мы ждали конкретно под нашу задачу, и она работает именно так как нужно. Webhook конвертации добавляет один дополнительный компонент в инфраструктуру, который надо мониторить и поддерживать, - это реальная цена, но она оправдана.
kubectl diff теперь в стандартном пайплайне перед apply. Маленькое изменение, которое убирает неприятный вопрос «а что именно я применю прямо сейчас?».