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

Kubernetes Operators: полугодовой итог - Zalando Postgres Operator в production

Подводим итог работы с Kubernetes Operators за полугодие: Zalando Postgres Operator упрощает управление кластерами СУБД, но требует понимания CRD и lifecycle-хуков.

Контекст момента

Зрелость паттерна Kubernetes Operators - Etcd, PostgreSQL, Prometheus операторы распространяются в production-кластерах

В марте мы разбирали паттерн Kubernetes Operators как концепцию, в июне раскатали Prometheus Operator на production. Сейчас конец июня, и если оглянуться на последние полгода - операторы из «интересного эксперимента CoreOS» превратились в реальный инструмент с которым мы работаем каждую неделю. Время сделать честный срез: что прижилось, что требует осторожности, и почему Zalando Postgres Operator оказался и неожиданно удобным, и неожиданно требовательным.

Три оператора, три разных опыта

Etcd Operator у нас так и живёт в тестовом кластере. Не потому что плохой - он работает корректно, правильно обрабатывает quorum при выводе члена из строя, делает rolling upgrade без потери данных. Проблема в другом: production-etcd это сам etcd Kubernetes, и запускать ещё один etcd-кластер для приложений через оператор на тех же нодах - это двойная ответственность за один и тот же тип данных в разных контекстах. Пока не нашли клиентский сценарий где это необходимо именно так.

Prometheus Operator уже три недели на production-кластере, и это самый чистый опыт из трёх. ServiceMonitor CRD прижился, команды разработки создают мониторинг самостоятельно, тикетов «добавьте наш сервис в Prometheus» не было ни одного за этот период. Паттерн простой и понятный: инфраструктура управляет самим Prometheus, разработчики управляют тем что мониторить. Работает.

Zalando Postgres Operator - это отдельная история, и ради неё написан этот пост.

Zalando Postgres Operator: откуда он взялся

Задача появилась месяц назад: один из клиентов хочет несколько PostgreSQL-кластеров в Kubernetes с failover, point-in-time recovery и возможностью масштабировать реплики без ручного вмешательства. StatefulSet с PostgreSQL в одном Pod они уже пробовали - при падении пода потеряли несколько минут транзакций и долго разбирались почему. Логично было посмотреть на операторы.

Вариантов на рынке сейчас несколько: Crunchy Data PostgreSQL Operator, KubeDB, и Zalando Postgres Operator. Мы выбрали Zalando по нескольким причинам: они используют его в собственной production-инфраструктуре, код открытый на GitHub, и он построен поверх Patroni - инструмента для HA PostgreSQL с которым мы уже работали не в контексте Kubernetes.

CRD оператора называется postgresql (в API группе acid.zalan.do). Манифест выглядит примерно так:

apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
  name: acid-postgres-cluster
  namespace: default
spec:
  teamId: "acid"
  volume:
    size: 10Gi
  numberOfInstances: 3
  users:
    appuser:
    - superuser
    - createdb
  databases:
    myapp: appuser
  postgresql:
    version: "10"

Создаёшь этот объект - оператор поднимает кластер из трёх нод, настраивает Patroni для управления leader election, создаёт пользователей и базы. Если нода падает - Patroni переключает лидера, оператор следит за тем чтобы общее количество нод восстановилось.

Где оказалось нетривиально

Первый сюрприз пришёл со стороны lifecycle-хуков. Postgres Operator не просто запускает Pod и ждёт Ready - он управляет жизненным циклом через preStop-хуки и собственные health-проверки, которые нужно понять прежде чем трогать что-то в манифестах Pod-шаблона. Мы один раз поменяли resource limits напрямую в StatefulSet который создал оператор - при следующей синхронизации оператор вернул их в исходное состояние. Это правильное поведение - оператор владеет этими объектами, а не мы. Но пока не понимаешь что управление идёт через CRD, а не через прямую правку ресурсов - теряешь время.

Второй момент: хранилище credentials. Оператор создаёт Kubernetes Secret для каждого пользователя автоматически. Неплохо. Но схема именования секретов фиксирована ({username}.{cluster-name}.credentials.postgresql.acid.zalan.do), и если у вас уже есть соглашение по именованию секретов - придётся либо адаптироваться, либо настраивать. Для клиентских managed-кластеров мы написали небольшую документацию по этому поводу, потому что иначе разработчики не могут найти свои credentials.

Третий момент - backup. Оператор поддерживает интеграцию с WAL-G и Spilo (Docker-образ от Zalando) для WAL-архивации и PITR. Это хорошо. Но настройка требует S3-совместимого хранилища для WAL-сегментов и понимания как Spilo управляет базовыми бэкапами. Мы потратили примерно день на то чтобы отладить цепочку: Spilo пишет WAL в MinIO, проверить что восстановление с конкретной точки действительно работает, убедиться что оператор корректно передаёт параметры подключения к хранилищу через переменные окружения.

Что с CRD и API-стабильностью

Это общая проблема всех операторов сейчас. Zalando Postgres Operator использует v1 в имени API-группы, но это не означает что API стабильный в смысле «никогда не меняется». Это просто имя группы. Мы нарвались на это при обновлении оператора - несколько полей в spec переименовались, существующий объект postgresql не удалился и не обновился автоматически, пришлось мигрировать руками.

Это не катастрофа, но требует того чего у многих нет в начале: нормального процесса обновления оператора с проверкой совместимости CRD.

Etcd Operator, к слову, в этом же месте тоже вызывал вопросы - там API v1beta2 что хотя бы честно говорит «мы ещё итерируем».

Где мы сейчас

Postgres Operator работает на одном production-кластере третью неделю. Под ним кластер из трёх нод PostgreSQL 10, WAL-архивация настроена, одно переключение лидера мы намеренно проверили в тестовом окне - Patroni переключился за несколько секунд, приложение пережило это с минимальным количеством ошибок. Клиент доволен.

Паттерн Operators в целом за полгода перешёл для нас из категории «надо разобраться» в категорию «используем на реальных кластерах». Prometheus Operator - рабочий инструмент. Postgres Operator - тоже рабочий, но требует больше вложений на старте: нужно понять Patroni, lifecycle-хуки, схему именования объектов и выстроить процесс обновления самого оператора.

Если кто-то ждёт «просто поставь и забудь» - пока так не работает. Оператор убирает рутину, но не убирает необходимость понимать что происходит внутри.

Контакт

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

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