Всем привет! На связи Олег Сапрыкин, технический директор по инфраструктуре и тимлид DevOps-команд во «Фланте». В марте 2024 года мы с Андреем Радыгиным (ex-главный архитектор по внедрению Deckhouse) выступили на конференции DevOpsConf. Эта статья — текстовый вариант нашего доклада о развитии и нашем опыте использования Stateful в Kubernetes.
![](https://habrastorage.org/getpro/habr/upload_files/232/7d5/7f2/2327d57f253353166e18d7fe4dfb31a2.png)
Мы рассказали, как оценивать Stateful-компонент, прежде чем запускать его в Kubernetes, показали нюансы работы с такими приложениями, а также поделились особенностями конфигурирования и опытом использования некоторых Stateful-операторов — ClickHouse, Redis, Kafka, PostgreSQL и MySQL.
В 2018 году на Highload++ были сделаны два фундаментальных прогноза:
Stateful будет развиваться в Kubernetes.
Stateful перейдёт под автоматическое управление операторами.
С тех пор прошло шесть лет: прогнозы сбылись, а мы получили много релевантного опыта, которым и хотим поделиться с вами. Прежде чем детально погружаться в обзор, кратко расскажем о предыстории Kubernetes и появлении в нём Stateful в контексте опыта «Фланта».
Как индустрия и мы вместе с ней пришли к Kubernetes и Stateful
В середине нулевых начала активно развиваться разработка на микросервисах. Это создало потребность в новой технологической базе, так как запускать микросервисы и управлять ими старыми методами было крайне неудобно. Решение проблемы нашли в контейнерах. Однако они же со временем породили новую: масштабы и число контейнеров росли. Поэтому понадобились новые инструменты для оркестрации. Параллельно с этим Google в 2003 году создали свой оркестратор рабочих нагрузок Borg, который использовал собственные закрытые технологии контейнеров (или чего-то похожего на них). В последующие годы появлялись другие решения по оркестрации контейнеров, например LXC или OpenVZ, однако ни одно из них не стало массовым. Ближе к 2013 году на сцену вышел Docker и представил инновационную экосистему, реализующую контейнеризацию. Google признали перспективность нового решения и на основе Docker-контейнеров создали наследника Borg — Kubernetes.
Появление Kubernetes не вызвало в сообществе особого энтузиазма: он казался чрезмерным, ненужным усложнением, поэтому его не спешили внедрять. Несмотря на это, было очевидно, что микросервисы, Docker-контейнеры, а далее и Kubernetes приносили явную пользу в разработке, несмотря на изначальный скепсис инженеров. Поэтому начиная примерно с 2014 года начался закономерный взлёт популярности контейнеризации и оркестраторов, и уже к 2018 году Kubernetes оформился как стандарт де-факто в индустрии. Согласно опросу CNCF, в 2020 году более 80% enterprise-пользователей активно использовали Kubernetes в своих окружениях.
Мы во «Фланте» были сторонниками Kubernetes с самого начала его существования. Мы поняли, что он «из коробки» способен облегчить нашу инженерную жизнь и дать хорошую инфраструктуру: масштабируемую, отказоустойчивую, с балансировкой нагрузки и мониторингом. Кроме того, мы нашли в Kubernetes избавление от сильнейшей боли всех инженеров — возможность унификации: он даёт единый интерфейс и API.
«Ранний» Kubernetes реализовывал сложные и гибкие механизмы доставки, умел распределять нагрузку и управлять множеством контейнеров, обеспечивал самовосстановление и гибкую утилизацию ресурсов. Однако тогда все эти задачи решались только для Stateless-сервисов.
Со временем у разработчиков появились запросы на динамические окружения, то есть такие, в которых под каждую ветку приложения создано множество копий — обычных dev-стендов, на которых можно тестировать софт. Поскольку у каждого приложения есть базы и очереди, в каждом таком окружении уже нужно было, по сути, реализовывать Stateful.
Параллельно также развивались Cloud Native Stateful-приложения, в которых вопросы отказоустойчивости и масштабирования были актуальны со времён появления самих облаков и стали ещё более важными с появлением трендов на мультизоны и кросс-ЦОДовые инсталляции.
Таким образом, потребность в реализации Stateful в Kubernetes возникла не только у нас. Просьбы сообщества были услышаны, и уже через 1,5–2 года после выхода Kubernetes, в версии 1.3, появился PetSet, который позже переименовали в StatefulSet. В 2019 году появились Custom Resource Definitions (CRD), которые позволяют воплощать разную гибкую логику управления приложениями, более сложную, чем просто управление набором экземпляров в деплойменте.
С историей разобрались — перейдём к более практическим моментам. Про Kubernetes бытует мнение, что достаточно задеплоить рядом со своим приложением YAML-файлы и всё заработает — раздумывать особо не надо. Когда дело касается Stateful, подумать всё-таки нужно, чтобы понять, надо ли вообще запускать этот компонент в Kubernetes.
Как оценить Stateful-компонент перед запуском в Kubernetes
Есть несколько критериев, чтобы оценить компонент, прежде чем запускать его в Kubernetes.
Производительность: хватит ли её, не упрёмся ли мы в сеть или диски.
Сохранность данных: тут речь скорее про персональные данные — сможем ли мы поместить базу данных туда, где находится контур с кластером.
Отказоустойчивость: может ли приложение/компонент/база масштабироваться, находиться в двух репликах и получится ли достичь нужного уровня SLA.
Бэкапы: зачастую в компании бэкапы делаются централизованно, поэтому, если запустить какой-то компонент в Kubernetes, не всегда возможно сделать его бэкап.
Целесообразность: например, если нужно просто задеплоить и использовать какую-то базу данных, необязательно гнаться за трендами — возможно, достаточно скриптов на Ansible. Другое дело, если необходимо воспроизводить это сотни и тысячи раз в каких-то dev-окружениях — тогда имеет смысл разобраться и перенести это в Kubernetes.
Как работает Stateful в Kubernetes
Прежде чем разбирать пример, освежим немного теорию.
Есть какой-то потребитель (по сути, это всегда под), которому нужен Volume
, чтобы хранить данные. Для этого он отправляет PersistentVolumeClaim
(PVC) в StorageClass
(SC) и через этот StorageClass
выписывает себе PersistentVolume
(PV). Дальше Kubernetes просто идёт в Storage, выделяет там раздел и монтирует его к поду. После этого под читает и пишет данные с этого Volume
. При этом, если, например, под перезапустится, данные никуда не денутся.
![](https://habrastorage.org/getpro/habr/upload_files/da4/3d4/000/da43d4000a543aa00a356a65c01c79fb.png)
Теперь представим, что у нас есть PostgreSQL, которую мы хотим разместить в кластере. Монтируем datadir
, который использует PostgreSQL, в манифест StatefulSet.
kubectl get sts psql -o yaml
...
volumeMounts:
- mountPath: /var/lib/postgresql
name: pgdata
...
Воспроизводим это N раз на стендах разработки, дальше — stage, pre-prod и, наконец, деплой в prod. Месяц спустя обнаруживаем, что растёт прайс за облако. Оказывается, копятся диски. Эту ситуацию можно объяснить на примере пакетного менеджера Helm, который управляет релизами. Helm при удалении релиза удаляет только StatefulSet — при этом PVC и PV, которые создаются из него, копятся.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: psql
spec:
...
volumeClaimTemplates:
- metadata:
name: pgdata
spec:
resources:
requests:
storage: 1Gi
Чтобы этого избежать, вместо volumeMount
необходимо использовать Volume
, смонтированный в отдельный PersistentVolumeClaim
, в отдельный манифест. Тогда PVC будет содержаться в релизе и удаляться вместе с ревью-окружением.
apiVersion: apps/v1
kind: StatefulSet
Metadata:
name: psql
spec:
spec:
...
volumes:
- name: pgdata
persistentVolumeClaim:
claimName: pgdata
apiVersion: v1
kind: PersistentVolumeClaim
Metadata:
name: pgdata
spec:
resources:
requests:
storage: 1Gi
helm uninstall --debug psql-test -n test
uninstall.go:95: [debug] uninstall: Deleting psql-test
client.go:310: [debug] Starting delete for "psql-test" Service
client.go:310: [debug] Starting delete for "psql" StatefulSet
client.go:310: [debug] Starting delete for "pgdata" PersistentVolumeClaim
PVC удалилась. А вот PV остались.
kubectl get pv
NAME CAPACITY RECLAIM POLICY STATUS
pvc-15d571eb-415a-4968-afb7-4f15cd0f5bef 2Gi Retain Released
pvc-2660b9bd-65f0-4bf9-9857-4e036f6cb265 1Gi Retain Released
pvc-2e428234-f7f6-448e-8913-cd74f9d3a451 1Gi Retain Released
pvc-334ac272-47f1-4dcf-8fa9-f74b3c848874 2Gi Retain Released
pvc-33c9cf2d-64a9-49ea-babe-1ac9c3045524 1Gi Retain Released
pvc-3a93b9f6-866e-4947-b60f-09e75b1d899f 2Gi Retain Released
Они остались из-за reclaimPolicy
. Это политика, которая наследуется из StorageClass
, через который заказывали PersistentVolume
. По умолчанию она имеет значение Delete
.
kubectl get sc ssd -o yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ssd
...
reclaimPolicy: Delete
В этом случае PV удалится, если PVC освободил его. Однако в результате можно потерять важные данные.
Второе значение reclaimPolicy
— Retain
.
kubectl get sc ssd -o yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ssd
...
reclaimPolicy: Retain
При таком значении PV перейдёт в статус Released
, и если удалить релиз с PVC, PV останутся в статусе Released
. Более того, если затем вручную удалить эти PV из кластера, то в облаке они останутся. Это сделано для того, чтобы не потерять действительно важные данные.
На возможности потери данных остановимся чуть подробнее. Как мы помним, мы запустили Postgres в Kubernetes, и сейчас всё работает в prod’е. Тут оказывается, что разработчики нашли dev-стенд, решили, что им уже давно никто не пользуется и его можно удалить:
kubectl delete ns -n dev --all
Так они удалили не только свой dev-стенд, но и весь кластер — остались только kube-system
и default
. Смешная ситуация, но мы с ней неоднократно сталкивались. Самое время достать бэкапы, о которых говорили выше.
Если PV выпал в статус Released
, его можно переиспользовать. PV помнит PVC, от которого был создан, поэтому нужно зачистить claimRef
.
apiVersion: v1
kind: PersistentVolume
metadata:
name: pvc-480c58d4-d871-4e68-b908-1f475a1b5f9c
spec:
...
claimRef:
apiVersion: v1
kind: PersistentVolumeClaim
name: psql-pgdata-0
namespace: production
persistentVolumeReclaimPolicy: Retain
...
После такого патча PV переходит в статус Available
, и его можно переиспользовать.
kubectl -n test get pv pv-test
NAME CAPACITY RECLAIM POLICY STATUS CLAIM
pv-test 1Gi Retain Released test/pgdata-psql-0
kubectl -n test patch pv pv-test -p
'{"spec":{"claimRef": null}}'
persistentvolume/pv-test patched
kubectl get pv pv-test
NAME CAPACITY RECLAIM POLICY STATUS CLAIM
pv-test 1Gi Retain Available
Если дальше создать PVC, который будет соответствовать по StorageClass
и будет подходящего объёма, PV смонтируется.
kubectl -n test scale psql web --replicas 1
statefulset.apps/psql scaled
kubectl -n test get pv pv-test
NAME CAPACITY RECLAIM POLICY STATUS CLAIM
pv-test 1Gi Retain Bound test/pgdata-psql-0
Если PVC будет меньше по объёму, в целом он будет удовлетворять требованиям и тоже смонтируется.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: psql
spec:
...
volumeClaimTemplates:
- metadata:
name: pgdata
spec:
resources:
requests:
storage: 500Mi
Далее разберёмся с заменой StorageClass
в StatefulSet.
Выше мы говорили про необходимость оценить производительность. К сожалению, не всё можно просчитать с самого начала — мы упираемся в диски. Придётся импровизировать. Переходим на SSD и просим администраторов создать новый StorageClass
— нужно заменить его. Если попробовать изменить StorageClass
прямо в манифесте StatefulSet
, возникнет ошибка:
Error: UPGRADE FAILED: cannot patch "psql" with kind StatefulSet: StatefulSet.apps "psql" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', 'updateStrategy', 'persistentVolumeClaimRetentionPolicy' and 'minReadySeconds' are forbidden
Так происходит потому, что большинство полей в манифесте StatefulSet
иммутабельные.
Чтобы всё-таки изменить StorageClass
, можно удалить StatefulSet
Postgres’а, который запущен в prod’е, с помощью опции --cascade=orphan
.
kubectl -n test delete sts psql --cascade=orphan
statefulset.apps "psql" deleted
При этом поды, которые запущены из этого StatefulSet
, останутся запущенными, с ними ничего не произойдёт.
Теперь нужно просто задеплоить новый StorageClass
в StatefulSet
. Когда StatefulSet
масштабируется до нескольких новых реплик, новые поды начнут использовать новый StorageClass
.
kubectl -n test scale sts psql --replicas 2
statefulset.apps/psql scaled
kubectl -n test get pvc
NAME STATUS VOLUME CAPACITY STORAGECLASS AGE
psql-0 Bound pvc-1 1Gi old 1h
psql-1 Bound pvc-2 1Gi new 5s
Важно понимать, что Kubernetes при этом не проводит никакую автомиграцию — нужно сделать это вручную либо приложение должно уметь это делать.
Отдельно поговорим про режим доступа к PersistentVolume
.
![](https://habrastorage.org/getpro/habr/upload_files/3dd/b18/70c/3ddb1870c06cde73b6f9a949ec3bb3a8.png)
Схема RWO, которую мы и описали выше, — самая обычная: есть потребитель, который читает и пишет в свой Volume. Это возможно в любых облаках, везде, где есть какой-то Storage.
ROX предполагает, что нужно читать из разных подов на разных узлах. Такое достижимо уже не везде.
RWX — самая изощрённая ситуация, когда нужно читать и писать из разных узлов в один Volume.
Матёрые сисадмины в такой ситуации посоветовали бы использовать NFS. Напомним: до этого мы строили отказоустойчивую инфраструктуру, поднимали кластер, возможно, думали про зону. А теперь нам предлагают поместить все persistent-данные в неотказоустойчивые NFS. Кроме того, NFS в этом году исполняется 40 лет — стоит уже отправить его на покой.
В манифесте The Twelve-Factor App от Heroku есть рекомендация использовать S3, если нужно где-то размещать статичные файлы, чтобы читать их и писать в них. Чтобы использовать такой подход, нужно применять какие-то новые библиотеки, переписать legacy-код (может быть, найти разработчиков, которые его писали). Не всегда это достижимо, поэтому приходится прибегать к поделкам вроде S3FS.
S3FS использует «под капотом» всем известный FUSE (Filesystem in Userspace). Соответственно, каждая операция чтения-записи — это три switch-контекста. Нормального IOPS (Input/Output Operations Per Second) здесь не получить: если положить так базу данных, можно упереться практически сразу. Поэтому для статики лучше использовать S3, для баз данных — хотя бы CephFS.
С ростом числа примитивов и опций приходится держать в голове всё больше информации, а значит, растёт риск ошибки. Чтобы его минимизировать, хочется всё автоматизировать. В этой связи рассмотрим ещё один пример. Представим, что нужно запустить кластер ClickHouse из двух реплик и одного шарда в Kubernetes. Необходимо со старта описать два StatefulSet для реплик. Будем кластеризировать по старинке через ZooKeeper. Казалось бы, чтобы всё это описать и связать между собой, потребуется много рутинной работы.
kubectl -n clickhouse-prod get sts
NAME READY
chi-0-0 1/1
chi-0-1 1/1
zookeeper 3/3
kubectl -n clickhouse-prod get po
NAME READY STATUS
chi-0-0-0 1/1 Running
chi-0-1-0 1/1 Running
zookeeper-0 1/1 Running
zookeeper-1 1/1 Running
zookeeper-2 1/1 Running
На самом деле это можно сделать с помощью одного YAML-файла: достаточно указать в нём, сколько реплик и шардов нужно, — дальше Kubernetes «автомагически» всё это задеплоит и развернёт.
apiVersion: clickhouse.altinity.com/v1
kind: ClickHouseInstallation
metadata:
name: clickhouse
namespace: clickhouse-prod
spec:
configuration:
clusters:
- layout:
replicasCount: 2
shardsCount: 1
name: production
...
![](https://habrastorage.org/getpro/habr/upload_files/07f/674/b44/07f674b4400009a0f46c8b38d62d8090.png)
Такое возможно благодаря операторам. Они создают свои новые манифесты, из которых дальше создаются кастомные ресурсы (CR), которыми можно управлять.
Операторы — это, по сути, реализация различных способов управления софтом. Сегодня существует удобный фреймворк, который позволяет оценивать зрелость операторов.
![](https://habrastorage.org/getpro/habr/upload_files/1fe/10b/d0e/1fe10bd0ee2e8982e4f6b54965e3f7a6.png)
Операторы первого уровня позволяют создавать только базовые конфигурации для приложений. На втором уровне появились гладкие обновления, на третьем — управление всем жизненным циклом приложений с бэкапами и восстановлением после сбоев. Операторы четвёртого уровня предполагают комплексный мониторинг, который позволяет извлекать сложные метрики. Уровень «автопилот» означает, что мы только регулируем параметры, а все нужные изменения оператор вносит сам.
Далее поговорим про операторы, которыми чаще всего пользуются инженеры во «Фланте», и на их примере посмотрим, с какими подводными камнями можно столкнуться при использовании операторов.
Какие операторы мы используем и в чём их особенности
Для каждого оператора мы приведём количество звёзд на GitHub, нашу оценку уровня зрелости по фреймворку выше и количество открытых/закрытых issues для понимания, развивается ли оператор.
Звёзды на GitHub: 1,6k
Зрелость: 3
Открытые/закрытые issues: 101/542
ClickHouse-оператор от Altinity мы используем давно и успешно. Он сам «из коробки» управляет процессами шардирования и масштабирования — достаточно задать количество реплик.
На примере этого оператора покажем, как оператор работает с кастомными ресурсами.
Чаще всего можно задеплоить только какой-то cluster-wide-оператор, который подписывается на все кастомные ресурсы в кластере. Поэтому, например, если в кластере есть и dev, и prod, нельзя обновить оператор только на dev и потестировать там его новую версию. Но если оператор может отслеживать конкретные пространства имён, тогда можно указать, что на пространство имён dev нужно установить новую версию. При этом старая продолжит существовать на prod’е. Последние версии Altinity такое умеют.
Вторая проблема вытекает из первой: иногда операторы подписываются на кастомные ресурсы и последовательно, один за другим, приводят их к требуемому виду, то есть образуется одна очередь. Поэтому, например, если сломалась инсталляция на dev’е, а на prod’е в это время закончилось место, вы добавляете кастомные ресурсы, увеличиваете диск. В целом, оператор может сам заказать в облаке диск большего объёма и сделать всё бесперебойно, но сейчас он наблюдает за упавшей dev-инсталляцией. Если у него один поток и он обрабатывает всё в одну очередь, ничего не произойдёт. Это решается деплоем двух операторов — по одному на каждое пространство имён.
Redis
Тут стоит сразу отметить, что практически все операторы не умеют строить Redis-кластер. Благо, нашим клиентам это обычно и не нужно — мы используем обычные Redis failover на Sentinel, и этого достаточно.
Звёзды на GitHub: 1,4k
Зрелость: 2
Открытые/закрытые issues: 5/324
Redis-оператор от Spotahome мы используем давно.
Стандартная ситуация: пришло много трафика — в результате закончился диск или RAM и реплика встала, нужно задеплоить её заново. Проблема этого оператора в хардкоде проб. Таким образом, реплика пытается задеплоиться и падает по пробе. Приходится выключить оператор (масштабировать его до нуля), задеплоить реплику и снова его включить. То есть нужен оператор оператора.
Поскольку Redis однопоточный, зачастую не хватает производительности. Поэтому мы часто просто в образе меняем бинарник Redis на KeyDB. Оператору от Spotahome без разницы, что он запускает, а значит, наш кастомный образ работает.
redis-operator by Opstree Solutions
Звёзды на GitHub: 622
Зрелость: 1
Открытые/закрытые issues: 58/327
Этот оператор, как видно, моложе предыдущего и уступает ему по уровню зрелости. Он здесь только потому, что однажды нам понадобилось создать мультиЦОД-инсталляцию Redis, а оператор от Spotahome не поддерживает TLS.
Звёзды на GitHub: 4,3k
Зрелость: 3
Открытые/закрытые issues: 106/2248
Мы используем оператор от Strimzi — проекта инкубатора CNCF. Оператор неплохо написан, довольно зрелый, и его используют все сообщества CNCF.
Kafka изначально Cloud Native, то есть был разработан так, чтобы легко масштабироваться.
Из интересных особенностей: за сроком годности сертификатов нужно следить самостоятельно.
На примере этого оператора покажем, как разработчики добавляют дополнительные уровни абстракции, когда им не хватает стандартных примитивов Kubernetes.
Как мы знаем, поды в StatefulSet нумеруются от нуля до бесконечности. В Strimzi привязались к ID подов с помощью ID брокеров — это не позволяло удалять брокеры из середины. Поэтому они переписали примитив StatefulSet и создали свой StrimziPodSet. По сути, это набор подов, которые со своими ID брокеров позволяют динамически менять какие-то ресурсы. С одной стороны, это помогает и добавляет гибкости, с другой — даёт новую абстракцию. Когда падает узел, на котором запущен под, мы знаем, как поведёт себя StatefulSet: остановится, если диск локальный, или переедет в другое место. Поведение StrimziPodSet предсказать нельзя: документация не всегда помогает разобраться, поэтому приходится читать код.
PostgreSQL
Звёзды на GitHub: 3,8k
Зрелость: 3
Открытые/закрытые issues: 517/784
Вопрос развёртывания реляционных баз данных в Kubernetes изначально спорный, но мы давно и успешно делаем это с PostgreSQL.
Мы используем оператор от Zalando, в комплекте с которым идёт Patroni. Последний хорошо показывает себя как когда мы запускаем в кластере и сам Patroni, и PostgreSQL, так и когда используем PostgreSQL на виртуальных машинах или железе рядом с кластером, а в кластере работает только Patroni.
Стоит отметить отличный процесс in-place-обновлений. В образе конкретной версии оператора есть несколько идущих подряд версий компонента PostgreSQL. Это позволяет «на лету» обновить версию psql со всеми необходимыми миграциями, без перезапуска контейнеров. Однако версия PostgreSQL всегда связана с какой-то конкретной версией оператора. Поэтому, если используется оператор и выходит новая версия компонента, нельзя сразу же обновить её — надо дождаться поддержки новой версии в операторе. Также оператор может быть завязан на версию Kubernetes. Если у вас по каким-то причинам более старая версия, например из-за deprecated API, тоже придётся ждать.
При использовании этого оператора мы столкнулись с тем, что у нас повсеместно снимаются бэкапы PostgreSQL с помощью pg_basebackup. В операторе есть хардкод на восемь WAL’ов, и на высокопроизводительных базах данных мы просто не успевали делать бэкапы. Оказалось, раньше разработчики оператора использовали WAL-E, а в последнее время перешли на WAL-G. Мы разобрались, как всё устроено, и просто переделали свои бэкапы.
Звёзды на GitHub: 2,9k
Зрелость: 1
Открытые/закрытые issues: 197/1226
Мы помним истории с Elasticsearch, HashiCorp, поэтому, чтобы не зависеть от коммерческих проектов, смотрим на комьюнити-решения. Этот оператор моложе предыдущего, но явно развивается.
Сейчас он напрямую управляет подами, что плохо учитывается в автовосстановлении: когда под падает, приходится вмешиваться в работу оператора, потому что он неправильно восстанавливает, переключает на реплику.
Звёзды на GitHub: 965
Зрелость: 2
Открытые/закрытые issues: 174/334
За последний год мы попробовали около 20 операторов, и большинство из них заточено на групповую репликацию, которая появилась в MySQL 8. Нам же нужно поддерживать MySQL более старых версий — 5.6 и 5.7, которые есть у клиентов. Более того, нужна простая master-slave-инсталляция, ведь довольно сложно объяснить клиенту, зачем ему покупать ещё один огромный сервер под базу данных.
Среди всех кандидатов мы выбрали для себя оператор от Bitpoke, которые когда-то создали Cloud Native WordPress. Недавно мы запустили с оператором первый prod — до этого он был только на dev.
У него есть «детские болячки»: например, он не умеет делать бэкапы бинлога, так как у него нет PITR. Мы используем обходные пути, чтобы решить эту проблему.
Подведём итоги по операторам. Из плюсов:
Операторы снижают порог входа.
В зависимости от уровня зрелости автоматизируют рутину и какие-то этапы жизненного цикла приложений.
Унифицируют подходы: если есть оператор, можно быть уверенным, что всё единообразно и не нужно беспокоиться о поддержке и обновлении «зоопарка» чартов компонента.
Главный минус — операторы добавляют уровни абстракции, с которыми приходится разбираться, а иногда и вовсе нужно анализировать код операторов, чтобы понять причину их того или иного поведения.
Стоит помнить, что именно пользователи делают операторы более зрелыми. Поэтому важно оставлять обратную связь на их работу.
Выводы
Прежде чем запускать Stateful-компонент в Kubernetes, стоит оценить возможности инфраструктуры и общую целесообразность. Не всегда размещение Stateful в Kubernetes — панацея или «серебряная пуля». Иногда имеет смысл использовать уже имеющиеся решения и наработанные практики.
Запуск Stateful в Kubernetes требует применения уже известных подходов к построению инфраструктуры, однако имеет свою специфику и нюансы. Это стоит иметь в виду в начале проектирования подобного рода архитектур.
Чтобы автоматизировать те или иные этапы жизненного цикла Stateful, можно использовать операторы. Для решения одной задачи их можно найти как минимум несколько, а иногда — не один десяток. Может оказаться, что у оператора нет части нужной функциональности, поэтому придётся либо менять заранее выбранный оператор на новый, либо дорабатывать используемый самим. Так что важно изначально подобрать подходящий под нужды вариант, который требует минимум доработок.
В целом, сегодня в Kubernetes одинаково удобно управлять как Stateless-, так и Stateful-приложениями. Зачастую комфортность использования Stateful в приложении зависит от вендора, который предоставляет услуги Kubernetes, но если смотреть на картину в общем, Kubernetes эволюционирует и Stateful’у в Kubernetes — быть.
Видео и слайды
Видео с выступления (~49 минут):
P.S.
Читайте также в нашем блоге: