ADG Оставить заявку
Блог DevOps 4 мин чтения

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.

Миграция прошла так:

  1. Задеплоили новую версию CRD с обеими версиями и webhook-ом конвертации.
  2. Обновили оператор чтобы он читал v1alpha2.
  3. Подождали несколько дней пока команда обновила свои манифесты.
  4. Выключили 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. Маленькое изменение, которое убирает неприятный вопрос «а что именно я применю прямо сейчас?».

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.