Kubernetes Operators: CoreOS объясняет как управлять stateful-приложениями через Custom Resources
Разбираем паттерн Kubernetes Operator на примере Etcd Operator и Prometheus Operator: Custom Resources, контроллеры и зачем это нужно для stateful-сервисов.
CoreOS развивает концепцию Kubernetes Operators - управление stateful-приложениями через Custom Resource Definitions и контроллеры
Мы давно присматриваемся к тому что CoreOS называет Operators - паттерн появился ещё в 2016-м в их блоге, но только сейчас, когда Kubernetes 1.9 дал стабильный Workloads API и StatefulSet в GA, это перестало выглядеть как академическая идея и начало ощущаться как практический инструмент. На прошлой неделе мы наконец сели и потратили несколько дней на нормальный разбор: что это такое, как работает Etcd Operator и Prometheus Operator, и есть ли нам от этого польза прямо сейчас.
Что такое Operator и зачем он нужен
Идея в следующем. Kubernetes умеет управлять stateless-приложениями более-менее из коробки: Deployment следит за количеством реплик, делает rolling update, рестартует упавшие поды. Это работает потому что stateless-приложения идемпотентны - любой экземпляр заменяем любым.
С stateful-приложениями всё сложнее. Etcd-кластер - это не три одинаковых пода. Там есть лидер, есть quorum, есть процедура добавления члена и процедура вывода из кластера. Prometheus в кластере с Alertmanager тоже требует специфической конфигурации. Если просто завернуть это в StatefulSet и надеяться на лучшее - рано или поздно получишь split-brain или потерянный член кластера.
Operator - это по сути способ закодировать операционное знание о конкретном приложении прямо в Kubernetes. Он состоит из двух частей:
- Custom Resource Definition (CRD). Это новый тип объекта в Kubernetes API, который вы определяете сами. Например,
EtcdCluster- объект который описывает желаемый etcd-кластер: количество узлов, версия, параметры TLS. - Контроллер. Обычный Pod в кластере, который смотрит на объекты вашего типа через Kubernetes Watch API и приводит реальное состояние к желаемому. Именно контроллер знает как правильно добавить узел в etcd-кластер, как провести rolling upgrade без потери quorum, что делать если один из подов пропал.
Это расширяет «примирение желаемого и реального состояния» - ту же механику что использует встроенный контроллер Deployment - на произвольно сложные stateful-приложения.
Etcd Operator: смотрим на реальный код
CoreOS выложил Etcd Operator как открытый проект на GitHub. Мы развернули его в тестовом кластере и посмотрели как это работает изнутри.
После установки оператора в кластере появляется новый тип: EtcdCluster. Создаёшь вот такой объект:
apiVersion: "etcd.database.coreos.com/v1beta2"
kind: "EtcdCluster"
metadata:
name: "example-etcd-cluster"
spec:
size: 3
version: "3.2.13"
И всё - через несколько секунд в кластере появляются три пода с etcd, настроенные как кластер. Operator сам разобрался с первоначальной конфигурацией, bootstrap-токенами, peer-URL-ами.
Дальше интереснее. Если удалить один из подов - operator замечает это через Watch API и добавляет новый член в кластер через etcdctl member add, а не просто запускает новый pod как это сделал бы голый StatefulSet. Разница принципиальная: правильная процедура вступления в кластер вместо грубого рестарта.
Масштабирование: меняем spec.size: 5 в объекте EtcdCluster - operator по одному добавляет два новых члена, дожидаясь между добавлениями пока кластер вернётся в здоровое состояние. Это именно та логика которую обычно пишут в runbook для ручного масштабирования.
Prometheus Operator: другой уровень абстракции
Prometheus Operator от CoreOS решает немного другую задачу. Prometheus сам по себе не особо сложен в запуске - проблема в конфигурации. Когда у вас несколько команд, каждая хочет свой набор метрик, своих alerting-правил, и при этом вы не хотите чтобы одна команда случайно сломала конфигурацию другой - без автоматики начинается боль.
Prometheus Operator вводит несколько CRD:
Prometheus- сам экземпляр Prometheus с параметрами хранения, retention, ресурсами.ServiceMonitor- описывает какие сервисы мониторить и как. Ссылается на сервисы через label selector.PrometheusRule- набор alerting-правил.
Механика красивая. Каждая команда создаёт ServiceMonitor в своём namespace - описывает свои сервисы. Prometheus Operator видит эти объекты, генерирует конфиг Prometheus и перезапускает его (через /-/reload). Команда инфраструктуры контролирует сам экземпляр Prometheus, но не становится бутылочным горлышком для каждой новой цели мониторинга.
На нашей managed-инфраструктуре мы как раз сталкивались с этой проблемой: одна команда хочет добавить мониторинг нового сервиса, обращается к нам, мы правим конфиг Prometheus руками. Не масштабируется. Prometheus Operator выглядит как внятное решение.
Что нам это даёт и где пока трётся
После нескольких дней экспериментов мнение двоякое.
Плюсы реальные. Operator действительно инкапсулирует операционную сложность. Etcd Operator знает про quorum - вы про quorum думать не должны. Prometheus Operator убирает ручную правку конфигов. Декларативный API через Custom Resources органично вписывается в kubectl - kubectl get etcdclusters работает как ожидаешь.
Сложности тоже реальные. CRD это расширение API Kubernetes, и если оператор упадёт или зависнет - ваши объекты EtcdCluster будут существовать в etcd кластера, но никто не будет на них реагировать. Это не так страшно пока оператор просто не работает - страшнее если оператор работает неправильно и начинает принимать автоматические решения которых вы не ожидали. Нужно хорошо понимать что происходит внутри, прежде чем доверять ему production.
Второй момент: версионирование CRD. Etcd Operator сейчас в v1beta2 - это не production-стабильный контракт. API может поменяться между версиями оператора. Нам уже пришлось один раз мигрировать объекты при обновлении версии в тестовом кластере - ничего критичного, но следить нужно.
Третье: отладка. Когда что-то идёт не так - смотришь в логи оператора, а не в привычный kubectl describe pod. Добавляется ещё один слой, за которым надо уметь смотреть.
Где мы сейчас
Etcd Operator пока остаётся в тестовом кластере - хотим понять его поведение при различных сценариях отказа прежде чем трогать что-то клиентское. Prometheus Operator выглядит более зрелым и более понятным в части операционных рисков - планируем попробовать его на одном из кластеров в апреле.
Паттерн в целом кажется правильным ответом на вопрос «как управлять сложными stateful-вещами в Kubernetes». Не единственный возможный ответ, но логичный. Посмотрим как это будет работать за пределами тестового стенда.