company_banner

Обзор операторов PostgreSQL для Kubernetes. Часть 2: дополнения и итоговое сравнение



    На прошлую статью, где мы рассмотрели три оператора 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) != nil\nTemplate.Spec.Volumes[3].VolumeSource.ConfigMap.DefaultMode: &int32(420) != nil\nTemplate.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) != nil\n" 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.


    Читайте также в нашем блоге:

    Флант
    DevOps-as-a-Service, Kubernetes, обслуживание 24×7

    Комментарии 5

      +4
      n_bogdanov Коля Привет. Хотелось бы немного дополнить твою статью личным опытом использования KubeDB. Про кастомизацию подов в KubeDB — отчасти можно.Указав в crd к примеру:
      Код
      podTemplate:
          spec: 
            imagePullSecrets:
            - name: registry-secret
            resources:
              requests:
                memory: "512Mi"
                cpu: "500m"
              limits:
                memory: "4096Mi"
                cpu: "2"


      По поводу Лицензии и бекапа через Appscode Stash — на эту бизнес модель они перешли в новой версии, что на мое мнение большой шаг назад для них. В версии 0.13rc-1 по прежнему можно бэкапить без stash и там не используется лицензия. Так же мне понравилось что там без танцев с бубном можно бэкапить в minio, в zolando у меня к сожалению не получилось это завести, не все живут в облаках. Так же хотелось обратить внимание что KubeDB использует отключаемую функцию Google Analytics.

      P.S: Спасибо за обзор stackgres, в связи с переходом KubeDB на новую модель лицензирования и бекапов, посмотрю в его сторону. В связи с небольшим количеством времени на доскональное разбирательство и отвратительную документацию Zolando нам не зашел.
        +1
        Спасибо
          0
          Стоит заметить, что под кастопизацией пода в первую очередь подразумевается подключение дополнительных контейнеров.
          +1
          Очень бы хотелось увидеть аналогичную статью по Mysql операторам, на основе вашего опыта.
            +2

            Thank you for including StackGres in your review, and my apologies for leaving a comment in English.


            FYI, some of the missing items that you mentioned are being worked on, and can be tracked on our public issue tracker. You are more than welcome to leave your comments there and in our Community Slack (https://slack.stackgres.io):


            • Node Selectors are implemented in the evaluated version (so this is not 100% accurate in the comparison table).
            • Node Tolerations are being implemented for 1.0: https://gitlab.com/ongresinc/stackgres/-/issues/538
            • Node Affinity is also coming in 1.0: https://gitlab.com/ongresinc/stackgres/-/issues/598
            • Custom sidecars is also planned, possibly 1.1: https://gitlab.com/ongresinc/stackgres/-/issues/599 Feedback is specially appreciated here. Unless I got lost in translation, I didn't quite get why you need your SFTP as a sidecar. You can easily import CSV files via psql connection, which can obviously operate over network connections. Putting the DBA hat on, sidecars may be a threat to database stability, we want to be wary of what sidecars we run alongside with the database. We should cherry-pick them carefully, and I'd only consider those that really need to have direct access to either the database files ($PGDATA) or logs or similar use cases, but I'd not consider it for data-related operations which you can comfortably run over the network. What's your use case here if you can elaborate?

            As for the items you consider missing, and which are not currently planned to be developed, I have some comments too:


            • kubectl plugin: I don't see it as an advantage to build plugins. I'd love to understand why you think it is. StackGres is designed to be fully operated and managed via the CRDs, which have well-defined specs and convey back well-defined information on the status fields. This is truly Kubernetes native, and allows any Kubernetes user well versed into Kubernetes administration to easily operate it. However, when you require users to download external cli tools (and plugins are in reality nothing much different from that) you force the user to acquire additional, and different knowledge (than the regular K8s administration knowledge). So it adds cognitive burden. It also limits the ability to perform GitOps approach, since now you require those cli tools and/or plugins everywhere, including the CI/CD pipeline. What's your take on this, why would you rather consider a plugin a good thing in this case?
            • Custom postgres images. This is not as easy as it sounds like. There are some coupling among the sidecars and other elements of the infrastructure, that may break if the postgres container is replaced at will. Or worse, compatibility problems with tools compiled with different versions of dependencies (say glibc) may appear. Issues that may even lead to data corruption. One of StackGres goals is to make Postgres simple to operate for anyone, and these kind of problems require a high level of expertise to deal with --or to make sure you will not be bitten. What is your need, why would you want to bring your own Postgres container image?

            Thank you if you can provide your thoughts, feel free to reach us out in Slack, we'd really appreciate the feedback. Спасибо

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое