На прошлую статью, где мы рассмотрели три оператора PostgreSQL для Kubernetes (Stolon, Crunchy Data и Zalando), поделились своим выбором и опытом эксплуатации, — поступила отличная обратная связь от сообщества*.
Продолжая эту тему, мы добавили в обзор два других решения, на которые нам указали в комментариях: StackGres и KubeDB, — и сделали сводную таблицу сравнения. Также за время эксплуатации оператора от Zalando у нас появились новые интересные кейсы — спешим поделиться и ими.
* Первую часть статьи заметили даже разработчики Zalando, благодаря чему(?) они решили активизироваться в приёме некоторых PR. Это не может не радовать!
Итак, сначала дополним обзор ещё двумя решениями…
KubeDB
Оператор KubeDB разрабатывается командой AppsCode c 2017 года и хорошо известен в Kubernetes-сообществе. Он претендует на звание платформы для запуска различных stateful-сервисов в Kubernetes и поддерживает:
- PostgreSQL;
- Elasticsearch;
- MySQL;
- MongoDB;
- Redis;
- Memcache.
Однако в контексте статьи мы рассмотрим только PostgreSQL.
В KubeDB реализован интересный подход с использованием нескольких CRD, которые содержат в себе различные настройки, такие как:
- версия с образом базы и вспомогательными утилитами — за это отвечает ресурс
postgresversions.catalog.kubedb.com
; - параметры восстановления — ресурс бэкапа <codesnapshots.kubedb.com
собственно, корневой ресурс postgreses.kubedb.com
, который собирает в себе все эти ресурсы и запускает кластер PostgreSQL.
Особой кастомизации KubeDB не предоставляет (в отличие от оператора Zalando), но вы всегда можете управлять конфигом PostgreSQL через ConfigMap.
Интересной особенностью является ресурс dormantdatabases.kubedb.com
, который предоставляет «защиту от дурака»: все удалённые базы сначала переводятся в такое архивное состояние и могут быть легко восстановлены в случае необходимости. Жизненный цикл БД в KubeDB описывается следующей схемой:
Что же касается самого технологического стека, то тут используются свои наработки для управления кластерами, а не знакомые всем Patroni или Repmgr. Хотя для полинга соединений используется pgBouncer, который также создается отдельным CRD (pgbouncers.kubedb.com
). Кроме того, разработчики предоставляют плагин для kubectl, который позволяет удобно управлять базами через всем привычную утилиту, и это огромный плюс на фоне Stolon или Crunchy Data.
KubeDB интегрируется с другими решениями AppsCode, чем напоминает Crunchy Data. Если вы везде используете решения этого вендора, то KubeDB, безусловно, отличный выбор.
Наконец, хочется отметить отличную документацию этого оператора, которая находится в отдельном репозитории GitHub. В ней есть развернутые примеры использования, включая полные примеры конфигураций CRD.
Есть минусы у KubeDB. Многие возможности: бэкапы, полинг соединений, снапшоты, dormant-базы — доступны только в enterprise-версии, а для её использования требуется купить подписку у AppsCode. Кроме того, самой старшей поддерживаемой версией PostgreSQL «из коробки» является 11.x. Перечеркивают ли эти моменты изящную архитектуру KubeDB — решать вам.
StackGres
В твиттере и в комментариях к предыдущей статье нам резонно указали на оператор StackGres. Разработка данного оператора началась совсем недавно, в мае 2019 году. В нем используются известные и проверенные технологии: Patroni, PgBouncer, WAL-G и Envoy.
Общая схема оператора выглядит так:
Кроме того, в комплекте с оператором можно установить:
- веб-панель, как в Zalando;
- систему сбора логов;
- систему мониторинга, аналогичную Crunchy Data, о которой мы говорили в первой части;
- систему сбора бэкапов на основе MinIO, хотя можно подключить и внешнее хранилище.
В целом же, данный оператор использует подход, очень схожий с KubeDB: он предоставляет сразу несколько ресурсов для описания компонентов кластера, создания конфигов и задания ресурсов.
Вкратце про все CRD:
-
sgbackupconfigs.stackgres.io
,sgpgconfigs.stackgres.io
,sgpoolconfigs.stackgres.io
— описание кастомных конфигов; -
sginstanceprofiles.stackgres.io
— размер инстанса Postgres, который будет использоваться как limit/request для контейнера с PostgreSQL/Patroni. Для остальных контейнеров лимитов нет; -
sgclusters.stackgres.io
— когда есть конфигурации для базы, пула коннектов и бэкапа, можно создать кластер PostgreSQL, который описывается этим CRD; -
sgbackups.stackgres.io
— ресурс, схожий со snapshot у KubeDB и позволяющий обратиться к конкретному бэкапу из кластера K8s.
Однако оператор не позволяет использовать свои кастомные сборки образов или же несколько вспомогательных sidecar для сервера баз данных. Pod c Postgres содержит 5 контейнеров:
Из них мы можем отключить экспортер метрик, пулер коннектов и контейнер со вспомогательными утилитами администратора (psql
, pg_dump
и так далее). Да, оператор позволяет при инициализации кластера баз данных подключить скрипты с SQL-кодом для инициализации БД или создания пользователей, но не более того. Это сильно ограничивает нас в кастомизации, например, в сравнении с тем же оператором Zalando, где можно добавлять нужные sidecar с Envoy, PgBouncer и любыми другими вспомогательными контейнерами (хороший пример подобной связки будет ниже).
Подводя итог, можно сказать, что этот оператор понравится тем, кто хочет «просто управлять» PostgreSQL в кластере Kubernetes. Отличный выбор для тех, кому нужен инструмент с простой документацией, покрывающей все аспекты работы оператора, и кому не нужно делать сложные связки контейнеров и баз.
Итоговое сравнение
Мы знаем, что наши коллеги нежно любят сводные таблицы, поэтому приводим подобную для Postgres-операторов в Kubernetes.
Забегая вперед: после рассмотрения двух дополнительных кандидатов мы остаёмся при мнении, что решение от Zalando — оптимальный для нашего случая продукт, так как позволяет реализовывать очень гибкие решения, имеет большой простор для кастомизации.
В таблице первая часть посвящена сравнению основных возможностей работы с БД, а вторая — более специфичным особенностям, следуя нашему пониманию об удобстве работы с оператором в Kubernetes.
Не будет лишним также сказать, что при формулировании критериев для итоговой таблицы использовались примеры из документации KubeDB.
Stolon | Crunchy Data | Zalando | KubeDB | StackGres | |
Текущая версия | 0.16.0 | 4.5.0 | 1.5.0 | 0.13 | 0.9.2 |
Версии PostgreSQL | 9.4—9.6, 10, 11, 12 | 9.5, 9.6, 10, 11, 12 | 9.6, 10, 11, 12 | 9.6, 10, 11 | 11, 12 |
Общие возможности | |||||
Кластеры PgSQL | ✓ | ✓ | ✓ | ✓ | ✓ |
Теплый и горячий резерв | ✓ | ✓ | ✓ | ✓ | ✓ |
Синхронная репликация | ✓ | ✓ | ✓ | ✓ | ✓ |
Потоковая репликация | ✓ | ✓ | ✓ | ✓ | ✓ |
Автоматический failover | ✓ | ✓ | ✓ | ✓ | ✓ |
Непрерывное архивирование | ✓ | ✓ | ✓ | ✓ | ✓ |
Инициализация: из WAL-архива | ✓ | ✓ | ✓ | ✓ | ✓ |
Бэкапы: мгновенные, по расписанию | ✗ | ✓ | ✓ | ✓ | ✓ |
Бэкапы: управляемость из кластера | ✗ | ✗ | ✗ | ✓ | ✓ |
Инициализация: из снапшота, со скриптами | ✓ | ✓ | ✓ | ✓ | ✓ |
Специализированные возможности | |||||
Встроенная поддержка Prometheus | ✗ | ✓ | ✗ | ✓ | ✓ |
Кастомная конфигурация | ✓ | ✓ | ✓ | ✓ | ✓ |
Кастомный Docker-образ | ✓ | ✗ | ✓ | ✓ | ✗ |
Внешние CLI-утилиты | ✓ | ✓ | ✗ | ✓ (kubectl-плагин) |
✗ |
Конфигурация через CRD | ✗ | ✓ | ✓ | ✓ | ✓ |
Кастомизация Pod'ов | ✓ | ✗ | ✓ | ✗ | ✗ |
NodeSelector и NodeAffinity | ✓ | ✓ | ✓ (через патчи) |
✓ | ✗ |
Tolerations | ✓ | ✗ | ✓ | ✓ | ✗ |
Pod anti-affinity | ✓ | ✓ | ✓ | ✓ | ✓ |
Бонусы про оператор от Zalando
Ежедневно пользуясь решением от Zalando, мы столкнулись с некоторыми сложностями, и этим опытом хотелось бы поделиться с сообществом.
1. Приватные репозитории
Недавно нам потребовалось запустить через оператор наряду с PostgreSQL контейнер с SFTP, который позволил бы получать выгрузки из базы в формате CSV. Сам SFTP-сервер со всеми нужными параметрами был собран в закрытом registry.
И тут стало ясно, что оператор не умеет работать с registry secrets. К счастью, с такой проблемой мы были не одни с такой проблемой: все легко решилось коллегами на GitHub. Оказалось, что достаточно добавить в описание ServiceAccount имя с доступами в registry:
pod_service_account_definition: '{ "apiVersion": "v1", "kind": "ServiceAccount", "metadata": { "name": "zalando-postgres-operator" }, "imagePullSecrets": [ { "name": "my-fine-secret" } ] }'
2. Дополнительные хранилища и init-контейнеры
Для работы SFTP нам также требовалось корректно выставлять права на директории, чтобы заработал chroot. Возможно, не все знают, но OpenSSH-сервер требует особых привилегий на директории. Например, чтобы пользователь видел только свой /home/user
, необходимо, чтобы /home
принадлежал root с правами 755, а уже /home/user
был доступен пользователю. Соответственно, мы решили использовать init-контейнер, который исправлял бы права на директории.
Но оператор не умеет пробрасывать дополнительные диски в init-контейнеры! Благо, есть подходящий PR, которым мы и дополнили свою сборку оператора.
3. Перезапуск PgSQL при проблемах с control plane
В процессе эксплуатации кластеров на основе Patroni в Kubernetes мы получили от клиента странную проблему: ровно в 4 часа ночи по Москве обрывались все подключения PostgeSQL. Разбираясь в ситуации, мы обнаружили следующее в логах Spilo:
2020-10-21 01:01:10,538 INFO: Lock owner: production-db-0; I am production-db-0
2020-10-21 01:01:14,759 ERROR: failed to update leader lock
2020-10-21 01:01:15,236 INFO: demoted self because failed to update leader lock in DCS
2020-10-21 01:01:15,238 INFO: closed patroni connection to the postgresql cluster
2020-10-21 01:01:15 UTC [578292]: [1-1] 5f8f885b.8d2f4 0 LOG: Auto detecting pg_stat_kcache.linux_hz parameter...
Согласно issue на GitHub, это означает, что Patoni не смог обработать ошибку Kubernetes API и упал с ошибкой. А проблемы с API были связаны с тем, что в 4 часа стартовали сразу 50 CronJob, что приводило к проблемам в etcd:
2020-10-21 01:01:14.589198 W | etcdserver: read-only range request "key:"/registry/deployments/staging/db-worker-db1" " with result "range_response_count:1 size:2598" took too long (3.688609392s) to execute
Ситуация исправлена в версии Patroni 2.0. По этой причине мы собрали версию Spilo из мастера. При сборке стоит учитывать, что требуется взять PR с исправлениями сборки, который на данный момент уже принят в мастер.
4. Пересоздание контейнеров
Напоследок, еще одна интересная проблема. В последнем релизе в оператор была добавлена возможность делать sidecar-контейнеры с их полным описанием в CRD оператора. Разработчики заметили, что базы периодически перезапускаются. В логах оператора было следующее:
time="2020-10-28T20:58:25Z" level=debug msg="spec diff between old and new statefulsets: nTemplate.Spec.Volumes[2].VolumeSource.ConfigMap.DefaultMode: &int32(420) != nilnTemplate.Spec.Volumes[3].VolumeSource.ConfigMap.DefaultMode: &int32(420) != nilnTemplate.Spec.Containers[0].TerminationMessagePath: "/dev/termination-log" != ""nTemplate.Spec.Containers[0].TerminationMessagePolicy: "File" != ""nTemplate.Spec.Containers[1].Ports[0].Protocol: "TCP" != ""nTemplate.Spec.Containers[1].TerminationMessagePath: "/dev/termination-log" != ""nTemplate.Spec.Containers[1].TerminationMessagePolicy: "File" != ""nTemplate.Spec.RestartPolicy: "Always" != ""nTemplate.Spec.DNSPolicy: "ClusterFirst" != ""nTemplate.Spec.DeprecatedServiceAccount: "postgres-pod" != ""nTemplate.Spec.SchedulerName: "default-scheduler" != ""nVolumeClaimTemplates[0].Status.Phase: "Pending" != ""nRevisionHistoryLimit: &int32(10) != niln" cluster-name=test/test-psql pkg=cluster worker=0
Ни для кого не секрет, что при создании контейнера и pod’а добавляются директивы, которые не обязательно описывать. К ним относятся:
-
DNSPolicy
; -
SchedulerName
; -
RestartPolicy
; -
TerminationMessagePolicy
; - …
Логично предположить, чтобы такое поведение учитывалось в операторе, однако, как оказалось, он плохо воспринимает секцию портов:
ports:
- name: sftp
containerPort: 22
При создании pod’а автоматически добавляется протокол TCP, что не учитывается оператором. Итог: чтобы решить проблему, надо или удалить порты, или добавить протокол.
Заключение
Управление PostgreSQL в рамках Kubernetes — нетривиальная задача. Пожалуй, на текущий момент на рынке нет оператора, который покрывал бы все потребности DevOps-инженеров. Однако среди уже существующих инструментов есть продвинутые и довольно зрелые варианты, из которых можно выбрать то, что лучше всего соответствует имеющимся потребностям.
Наш опыт говорит о том, что не стоит ждать стремительного и магического решения всех проблем. Тем не менее, мы в целом довольны имеющимся результатом, пусть он и требует периодического внимания с нашей стороны.
Говоря же об операторах, рассмотренных в этой части статьи (KubeDB и StackGres), стоит отметить, что они оснащены уникальными функциями для управления бэкапами из кластера, что может стать одним из факторов роста их популярности в ближайшем будущем.
P.S.
Читайте также в нашем блоге:
- «Обзор операторов PostgreSQL для Kubernetes. Часть 1: наш выбор и опыт»;
- «Базы данных и Kubernetes (обзор и видео доклада)»;
- «Postgres-вторник №5: PostgreSQL и Kubernetes. CI/CD. Автоматизация тестирования»;
- «Одна история с оператором Redis в K8s и мини-обзор утилит для анализа данных этой БД».
Автор: Николай Богданов