etcd-operator присоединяется к Cozystack с новым API v1alpha2

Проект etcd-operator, который разрабатывает оператор для развёртывания и обслуживания кластеров etcd в Kubernetes, был передан проекту Cozystack. Вместе с передачей была опубликована написанная с нуля реализация оператора под новой версией API — etcd-operator.cozystack.io/v1alpha2, которая заменяет прежнюю etcd.aenix.io/v1alpha1. Вместо управления членами через StatefulSet новая реализация напрямую использует нативный Membership API etcd (операции MemberAdd, MemberPromote и MemberRemove), что даёт оператору полный контроль над составом кластера. Новую реализацию написал Timofei Larkin, один из сопровождающих прежней кодовой базы, которая сохранена в ветке v1alpha1. Проект написан на Go и распространяется под лицензией Apache 2.0.

Проект был начат компанией Ænix, которая собрала инициативную группу из сообщества Kubernetes для его создания. После завершения базовой реализации была предпринята попытка передать проект в CNCF. Под влиянием этой инициативы проект etcd пришёл к выводу, что нужен официальный оператор, и сформировал собственную рабочую группу, которая, оценив существующие реализации, решила разрабатывать кодовую базу с нуля — так появился etcd-io/etcd-operator. По функциональности официальный оператор пока не догнал aenix etcd-operator, который уже используется в промышленной эксплуатации сообществом и такими проектами, как Cozystack и Kamaji, поэтому проект продолжил собственную независимую линию развития (сравнение с официальным оператором приведено в конце этой статьи).

Оператор управляет кластерами etcd через два ресурса: EtcdCluster описывает желаемое состояние кластера (количество реплик, версия etcd, параметры хранилища, TLS, аутентификация, тюнинг etcd), а EtcdMember создаётся самим оператором для каждого члена кластера и владеет его Pod и PVC. В отличие от типичных решений, оператор не использует StatefulSet — Pod и PVC каждого члена согласуются независимо, а изменения состава кластера проходят через Membership API etcd: новые члены присоединяются в роли learner (MemberAdd) и позже повышаются до голосующих членов (MemberPromote), удаление выполняется с корректным выходом из кворума (MemberRemove), а приостановка кластера сохраняет идентичность членов. Обоснование этой архитектуры описано в concepts.md.

Ключевые возможности

  • первичная инициализация кластера и масштабирование в обе стороны по одному члену за раз: присоединение в режиме learner, корректное удаление с выходом из кворума;
  • приостановка кластера без потери данных (spec.replicas: 0) и его возобновление с теми же идентификаторами кластера и членов;
  • хранение данных в PVC (по умолчанию) или в tmpfs — для данных, которые можно восстановить; члены, работающие в памяти, автоматически пересоздаются при потере их Pod;
  • независимая настройка TLS для клиентских и peer-соединений: используйте свои Secret или позвольте оператору выпускать и автоматически обновлять сертификаты через cert-manager;
  • аутентификация с единственным пользователем root, учётные данные которого передаются через Secret;
  • снимки (snapshot) в S3 или PVC через ресурс EtcdSnapshot и восстановление кластера из снимка при первичной инициализации;
  • автоматически создаваемый PodDisruptionBudget, который не даёт операциям drain нарушить кворум;
  • валидация spec на стороне apiserver (выражения CEL в CRD) без webhook и зависимости от cert-manager;
  • субресурс /scale, благодаря которому работают kubectl scale и VerticalPodAutoscaler, порт метрик 2381, проброс affinity и topologySpreadConstraints;
  • плагин kubectl-etcd для операций day-2, выполняемых после развёртывания кластера.

Что изменилось по сравнению с v1alpha1

По сравнению со старой реализацией etcd.aenix.io/v1alpha1 были внесены следующие изменения:

  • группа API изменилась с etcd.aenix.io на etcd-operator.cozystack.io;
  • вместо StatefulSet используются отдельные ресурсы EtcdMember для каждого члена;
  • произвольная карта spec.options заменена типизированным набором параметров (quota-backend-bytes, режим авто-компакции и срок хранения, snapshot-count) — произвольная карта позволяла передавать флаги, конфликтовавшие с логикой оператора;
  • ресурс EtcdBackup переименован в EtcdSnapshot с сохранением его семантики;
  • валидация перенесена с webhook на правила CEL в CRD;
  • Service кластера переведён в headless-режим, который необходим для стабильных DNS-имён отдельных членов.

Миграция выполняется на месте с помощью инструмента etcd-migrate: работающий кластер старого оператора перенимается без переноса данных, перезапуска Pod и потери кворума — изменяются только владение объектами, метки и аннотации, после чего управление берёт на себя новый оператор. Клиенты, которые обращаются к кластеру по DNS-имени, продолжают работать без изменений. Процедура описана в migration.md.

Сравнение с официальным оператором

Реализация покрывает большинство пунктов дорожной карты официального etcd-operator, разрабатываемого проектом etcd. Статус по пунктам дорожной карты:

  1. Создание нового кластера etcd, например, из 3 или 5 членов заданной версии etcd — реализовано.
  2. Определение состояния кластера — реализовано.
  3. Включение TLS-соединений, включая обновление сертификатов — реализовано.
  4. Обновление в пределах патчей или на одну минорную версию — частично реализовано: spec.version применяется только к вновь создаваемым членам.
  5. Масштабирование вниз и вверх, например, 1 -> 3 -> 5 членов и обратно — реализовано.
  6. Поддержка настройки параметров etcd (через флаги или переменные окружения) — реализовано, в виде типизированного закрытого набора параметров.
  7. Восстановление одного отказавшего члена кластера (при сохранении кворума) — частично реализовано: члены с повреждённым PVC пока не заменяются автоматически.
  8. Восстановление после отказа нескольких членов кластера (потеря кворума) — не реализовано, работа запланирована.
  9. Создание резервной копии кластера по требованию — реализовано.
  10. Создание периодических резервных копий кластера — сознательно вне области охвата: регулярные снимки предполагается выполнять с помощью стандартного CronJob.

Помимо этой дорожной карты, v1alpha2 также предоставляет возможности, не перечисленные в официальном плане, продиктованные мультиарендным сценарием использования Cozystack и Kamaji:

  • масштабирование до нуля (приостановка/возобновление) с сохранением идентичности кластера и членов;
  • хранилище в памяти (tmpfs) с заменой членов силами оператора;
  • валидация CEL на стороне apiserver — без webhook и без зависимости от сертификатов;
  • автоматически создаваемый PodDisruptionBudget, ограниченный голосующими членами;
  • субресурс /scale с заполненным status.selector, благодаря чему kubectl scale и VerticalPodAutoscaler.targetRef работают напрямую;
  • проброс параметров планирования (affinity, topologySpreadConstraints) и объединённые additionalMetadata для всех принадлежащих объектов;
  • инструмент миграции на месте со старого оператора;
  • плагин kubectl-etcd для операций day-2.