Kubernetes (не) для всех

Kate

Administrator
Команда форума

Интро​

Когда мы собрались писать статью про Kubernetes, у нас была куча идей для мемов, картинок и интересных историй из жизни. Стоило только заварить эфиопских зёрен, достать пару куросанчиков (пасхалка раскроется на курсе). Всё перевернулось с ног на голову. И знаете почему?

Какими бы интересными ни были истории из жизни, статья про Kubernetes всё равно превращается в «очередную статью про Kubernetes». Вот вам история Kubernetes, вот его архитектура, а вот манифесты и пример приложения. И так каждый день: приложения, манифесты, релизы. Снова релизы, откаты релизов.

Даже работая YAML-программистом и выкатывая по 10 раз на дню в Kubernetes очередной релиз, задумываешься об однообразии процессов. Не хочется растягивать резину и петь дифирамбы kubectl, а хочется, чтобы за твоим приложением в Kubernetes следила автоматика: выкатывала новые версии, восстанавливала после сбоя и перезапускала, если закончилась память. Поэтому рано или поздно на свет должны были появиться операторы Kubernetes!

Operator Pattern​

Как известно, лень — это двигатель прогресса. Когда инженеры начали работать с Kubernetes, они быстро нашли себе замену — Kubernetes Operators, чтобы иметь время на Disco Elysium и разговоры о том, что Java уже в прошлом, Go ведёт нас в будущее, а JavaScript считает 2 + 2 = 22.

Слово «Operator» здесь не случайно, цель оператора — автоматизировать рутину и заменить собой человека, то бишь оператора Kubernetes.

Хотя сам Kubernetes, конечно же, создавался для автоматизации рутины: из коробки в кластере у вас есть полный набор инструментов для развёртывания приложений и контроля их работоспособности. В сердце Kubernetes работают контроллеры, отслеживающие состояние подопечных ресурсов.

Рис 1. Kubernetes-контроллер отслеживает состояние подопечных ресурсов
Рис 1. Kubernetes-контроллер отслеживает состояние подопечных ресурсов
Вот пример детального описания типового оператора:

1. В кластере присутствует Custom Resource (назовём его MyDB), который вы можете использовать для запуска базы данных в Kubernetes.

2. Здесь же в кластере работает Deployment, который запускает поды контроллера оператора.

3. Код контроллера периодически опрашивает Control Plane кластера Kubernetes на предмет настроенных MyDB:

  • если вы создали новый MyDB, оператор запускает процесс запуска базы данных: создаёт StatefulSet, подключает диски с помощью PersistentVolumeClaims, создаёт Service для подключения к БД;
  • если вы удаляете MyDB, оператор может сделать бэкап ваших данных и удалить StatefulSet и PersistentVolumes.
4. Оператор может выполнять вспомогательные операции, например обновление версии БД. При изменении конфигурации ресурса MyDB код контроллера оператора может выполнить аккуратное обновление подов запущенного StatefulSet для базы данных.

Примеры операторов​

Работу операторов можно сравнить с парадигмой SaaS (software as a service): используя привычный Kubernetes API, вы можете запускать готовые к использованию кластеры баз данных, брокеров сообщений или CI/CD системы.

Рис 2. Операторы для запуска GitLab в Kubernetes
Рис 2. Операторы для запуска GitLab в Kubernetes
Для всех популярных технологий, таких как Redis, Kafka, PostgreSQL, уже созданы операторы. В репозитории https://operatorhub.io можно найти качественные решения для запуска сервисов, готовых к промышленной эксплуатации.

Простейший оператор выполняет развёртывание приложения при создании ресурса, а в более сложных сценариях берёт на себя функциональность мониторинга, бэкапов и автоматического обновления сервисов. Шестой разряд сварщика — полный автопилот, когда контроллер оператора следит за состоянием сервиса, автоматически поправляет конфигурацию и количество реплик приложения в зависимости от нагрузки, а ещё следит за бесшовными обновлениями.

Рис 3. Уровни возможности операторов
Рис 3. Уровни возможности операторовРеальная история № 1


Компания Google начала разработку оператора для управления базами данных Oracle. Оператор был выпущен в open-source под названием El Carro. El Carro (с исп. «автомобиль» — догадайтесь, почему оператор для Oracle называется именно так) позволяет запускать инстансы Oracle RDBMS, создавать резервные копии и выполнять импорт дампов, созданных с помощью Oracle Data Pump.

Разработка оператора​

Наверняка вам захотелось создать собственный Kubernetes Operator? Да точно захотелось! Для этого вам понадобятся:

  • минимальный опыт программирования на любом языке;
  • установленный Minikube;
  • лютая жажда познания нового и интересного.

Настройка окружения​

Для старта нужен работающий Kubernetes-кластер, и Minikube — самый простой способ его получить.

Установите Minikube для вашей операционной системы по инструкции и проверьте работу:

$ minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

Установка Go​

Код контроллера оператора может быть написан на любом языке программирования, но мы рассмотрим примеры на популярном сейчас Go. Установите версию 1.19 для вашей операционной системы.

Не переживайте, вам не придётся писать много кода для этого простого примера. Основной шаблон для вас создаст помощник — Operator SDK, набор инструментов для разработки операторов.

Установка Operator SDK​

В процессе разработки лучше сосредоточиться на описании бизнес-логики контроллера (как запускать, обновлять и масштабировать ваш сервис), а подготовку логики взаимодействия с Kubernetes API доверить Operator SDK. Вместе с набором вспомогательных скриптов SDK предоставляет утилиту operator-sdk, которая создаст файловую структуру проекта оператора, код прослойки между вашим контроллером и Kubernetes API и примеры манифестов для Kubernetes.

Установите последнюю версию для вашей операционной системы по инструкции.

Реальная история № 2




Запуск оператора​

Для примера запустим очень простой оператор, который... ничего не делает. SDK поможет нам создать образ контейнера с кодом контроллера оператора, создать Custom Resource Definition для расширения Kubernetes API и запустить деплоймент контроллера оператора. После чего попробуем создать Custom Resource и проверим логи контроллера оператора.

Генерация кода​

Утилита operator-sdk позволяет создать шаблон проекта для начала разработки кода контроллера оператора. Команда init инициализирует проект:

mkdir kubernetes-operator && cd kubernetes-operator
operator-sdk init --domain itd27m01.com --repo github.com/itd27m01/kubernetes-operator
Параметр --domain задаёт доменное имя для API-группы в Kubernetes API, в рамках которого будут создаваться ваши Custom Resources. API-группы –– это механизм для логической разбивки монолитного API.

Важно: Если вы используете macOS на M1, добавьте плагин `--plugins=go/v4-alpha` для поддержки этой платформы.

API и CRD​

Теперь в кластере Kubernetes нужно создать Custom Resource Definition (CRD). Это расширения для Control Plane, за которыми будет следить контроллер оператора.

Первой командой создадим файлы с Go-кодом для описания CRD и шаблона кода контроллера:

operator-sdk create api --group example --version v1alpha1 --kind MyCRD --resource --controller
В файле controllers/mycrd_controller.go вы найдёте функцию Reconcile, в которой будет код логики вашего контроллера:

func (r *MyCRDReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)

// TODO(user): your logic here

return ctrl.Result{}, nil
}
На этом этапе для разработки полноценного оператора не мешало бы добавить код логики вашего контроллера, но для простоты добавим код логирования того момента, когда ресурс появляется в кластере:

func (r *MyCRDReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// Воспользуемся логгером для создания сообщения
log := log.FromContext(ctx)

// В этом блоке кода контроллер сделает запрос на получение информации о ресурсах MyCRD
example := &examplev1alpha1.MyCRD{}
err := r.Get(ctx, req.NamespacedName, example)
if err != nil {
log.Error(err, "Failed to get example resource")
}

// Эта строчка должна появиться в логах контроллера
log.Info(fmt.Sprintf("A new example resource found: %s", example.Name))

return ctrl.Result{}, nil
}

Далее нужно описать структуру и поля управляемых контроллером ресурсов. Для этого нужно зайти в файл api/v1alpha1/mycrd_types.go. SDK заполнил основную часть шаблона кода и добавил пример для поля в спецификации ресурса:

// MyCRDSpec defines the desired state of MyCRD
type MyCRDSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file

// Foo is an example field of MyCRD. Edit mycrd_types.go to remove/update
Foo string `json:"foo,omitempty"`
Оставим эту структуру как есть, нам этого достаточно.

Реальная история № 3

Установка​

На этом этапе уже можно запустить работающую версию оператора. Правда, его контроллер ничего не будет делать, если вы не добавили код логики.

Соберите образ контроллера и опубликуйте его в Registry (в примере неявно используется docker.io — укажите свой Registry или замените на своё имя пользователя Docker Hub):

make docker-build docker-push IMG='itd27m01/kubernetes-operator:v0.0.1'
И теперь установите оператор в кластер Kubernetes:

make deploy IMG="itd27m01/kubernetes-operator:v0.0.1"
Команда выше запустит деплоймент для контроллера оператора и установит Custom Resource Definition в кластере Kubernetes. Манифест для MyCRD можно посмотреть в файле config/crd/bases/example.itd27m01.com_mycrds.yaml.

Сейчас уже можно попробовать создать Custom Resource:

$ kubectl apply -f config/samples/example_v1alpha1_mycrd.yaml
mycrd.example.itd27m01.com/mycrd-sample created
И проверить логи контроллера:

CONTROLLER_POD_NAME=$(kubectl get pods -n kubernetes-operator-system --no-headers -o custom-columns=":metadata.name")

kubectl logs $CONTROLLER_POD_NAME -n kubernetes-operator-system

# Вывод:
<...>

1.6675597079600263e+09 INFO A new example resource found: mycrd-sample

<...>

Это та самая строчка в логах: «A new example resource found: mycrd-sample», которую мы добавили в код контроллера для обработки события создания Custom Resource.

Эпилог​

Тот, кто дочитал до этого места, почувствует недосказанность. А где же самое мясо? Где код работы с Kubernetes API?

В одной небольшой статье такие детали не расскажешь. Даже при создании нашего курса по DevOps пришлось хорошенько потрудиться, чтобы собрать из Kubernetes самое лучшее для закладки обучающего фундамента.

А сейчас предлагаем обратиться к документации SDK и пройтись по дополнительным материалам.

  • В гайдах Operator SDK можно найти хороший пример оператора для запуска сервиса Memcache.
  • Код Reconcile функции контроллера оператора доступен в примерах проекта на GitHub.
  • Логику работы функции можно хорошо проследить по её тексту: контроллер создаёт деплоймент с заданным в Custom Resource количеством реплик для Memcached-сервиса. Манифест деплоймента формируется в соответствующем методе deploymentForMemcached.
На самом деле бизнес-логику можно написать с помощью любого языка программирования или фреймворка автоматизации. Один из вариантов — запуск Ansible-плейбука при реакции на создание Custom Resource. Благодаря тому, что в коллекции community.kubernetes есть весь набор модулей для управления объектами Kubernetes, кода придётся написать меньше. Пример такого оператора также есть в документации SDK.

Ключевая мысль​

Оператор Kubernetes –– это способ развёртывания и управления приложением в Kubernetes. Как развёртывание, так и управление происходит с помощью знакомого вам Kubernetes API путём добавления Custom Resources и запуска контроллера оператора –– специфичного для приложения кода, который будет присматривать за подами приложения, вовремя обновлять его и выполнять любые другие операции, которые заложит разработчик оператора в код контроллера.

Процесс разработки кода контроллера значительно упрощается за счёт использования фреймворка Operator SDK. Вместе с набором вспомогательных скриптов для сборки образа контейнера с кодом контроллера SDK сгенерирует необходимый код контроллера, а разработчику останется только реализовать бизнес-логику работы с приложением.

Полезные материалы​

 
Сверху