[Перевод] Пишем оператор Kubernetes с нуля

image

В этой статье мы подробно рассмотрим, как создать собственный оператор Kubernetes с нуля. Операторы — это такие программные расширения, которые используют кастомные ресурсы (kind), чтобы управлять приложениями. Подробности читайте в официальной документации.

Возьмем самый простой пример — приложение HelloApp. Чтобы задеплоить HelloApp, создадим ресурс Kubernetes.

image

Смотрим на kind: HelloApp — это кастомное определение ресурса (Custom Resource Definition, CRD), которое обрабатывается нашим оператором (или контроллером). Здесь мы с нуля создадим такой оператор (или контроллер).


Миф

Пока я не начал создавать операторы, я думал, что для этого подходит только Go. Это миф.

Реализовать оператор (контроллер) можно на любом языке и в любом рантайме, которые могут быть клиентом для Kubernetes API.

Скриншот из документации по Kubernetes:

image

По сути Kubernetes — это такая большая система мониторинга. Все функции доступны через API (сервер API). Допустим, мы пишем приложение, которое как клиент обращается к серверу API Kubernetes и выполняет нужные действия. Это и есть оператор. Так что язык здесь не важен. Но раз Go — это нативный язык рантайма Kubernetes, и у него очень много библиотек для реализации операторов, обычно выбирают его.


Софт для этого руководства

Нам понадобятся:


  1. Go lang (1.16)
  2. Operator SDK (1.5)
  3. Kind
  4. Visual Studio Code с плагином Go

Этапы процесса

Статья разделена на несколько частей:

Часть 1: создание проекта оператора
Часть 2: реализация логики оператора
Часть 3: создание CRD
Часть 4: установка CRD
Часть 5: запуск оператора за пределами кластера
Часть 6: отладка оператора за пределами кластера
Часть 7: запуск оператора в кластере


Часть 1: создание проекта оператора

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

$ mkdir demo-operator
$ cd demo-operator
$ operator-sdk init --domain anupam.com --repo 
github.com/anupamgogoi/demo-operator

Создание API и контроллера

$ operator-sdk create api --group apps --version v1 --kind HelloApp --resource --controller

С помощью этих команд мы подготовили минимальную структуру проекта для дальнейшей работы.

image

Примечания о некоторых важных файлах:


  1. Это Makefile со всеми командами, которые понадобятся, чтобы создать артефакты для оператора. Выполните make help, и увидите все доступные команды.
  2. Это центральная точка входа для оператора с основной функцией. Кроме того, это центральная точка входа для отладки оператора в локальном кластере.
  3. Контроллер. Здесь находится основная логика оператора.
  4. Структура кастомного ресурса.
  5. Информация о группе и версии, которую мы указали при создании оператора. Подробности о группе и версии читайте в официальной документации.

Часть 2: реализация логики оператора

Логика оператора предельно проста. Когда применяется CRD (ниже), оператор (контроллер) должен создать деплоймент для kind HelloApp с числом подов, которое мы указали в spec.size.

image

Полный исходный код вы найдете в этом репозитории. В spec у нас два поля: image (образ) и size (размер). Чтобы добавить их в spec, редактируем файл [4] из первой части статьи.

image

Логику контроллера можно найти здесь. Тут никакой магии. Ничего особенного он не делает — просто проверяет наличие деплоймента для HelloApp и пытается создать его, если он отсутствует. Наконец он проверяет, что число подов соответствует указанному в spec.size, а если нет — создает больше подов. Вот и все. Подробности об API читайте в официальной документации.

image

Контроллеры реализуют интерфейс Reconciler, который предоставляет метод Reconcile. Функция Reconcile вызывается для таких событий в кластере, как операции CRUD (create read update delete — «Создание чтение обновление удаление», прим. переводчика), и сравнивает фактический статус ресурса (kind) с ожидаемым (spec). Если есть различия, он их исправляет.

t_c4bttyvfs-kdxpuqiau4gnivw.png


main.go

Файл main.go создается Operator-SDK и содержит дополнительный код. Для простоты я удалил все лишнее и оставил только то, что нужно для запуска оператора. Изучите файл main.go.

Вот самая важная часть кода в файле main.go:

image

ctrl.GetConfigOrDie () попытается считать конфигурацию кластера Kubernetes из файла ~/.kube/config и получит информацию о подключении. Файл ~/.kube/config выглядит примерно так:

image

Здесь IP сервера, сертификаты и т. д. Это главная часть. Метод GetConfigOrDie () считывает эту информацию, и на ее основе ctrl.NewManager () создает менеджер для контроллера. Остается просто вызвать API с сервера API Kubernetes. Здесь и начинается магия. Просто изучите файл main.go, и все станет ясно.

Даже kubectl CLI использует вызовы API к серверу API Kubernetes. Выполните эту команду и убедитесь сами:

$ kubectl get nodes --v=8

image


Часть 3: создание CRD

Итак, логика контроллера HelloApp готова. Теперь надо создать для нее CRD. В корне demo-operator выполняем команду:

$ make manifests

Она создает CRD в каталоге
~/demo-operator/config/crd/bases

image

В каталоге ~/demo-operator/config/samples создается пример.

image


Часть 4: установка CRD

Чтобы запустить оператор, нужен локальный кластер. Используем kind, чтобы создать кластер на локальном хосте.

$ kind create cluster --name k8s

Есть два способа установить определения CRD в кластере: просто выполняем команду

$ make install

или переходим в ~/demo-operator/config/crd/bases и выполняем команду

$ kubectl apply -f .

Результат будет одинаковым.


Часть 5: запуск оператора за пределами кластера

Это самый простой способ запустить и отладить логику оператора. Чтобы заглянуть внутрь Kubernetes, лучше всего начать с

$ cd demo-operator
$ go run main.go

Вот что мы видим:

image

Давайте задеплоим наш кастомный ресурс — перейдем в каталог samples и применим ресурс:

$ kubectl create ns test
$ kubectl apply -f apps_v1_helloapp.yaml -n test

Посмотрим, что мы создали в неймспейсе test:

$ kubectl get all -n test

image

А теперь самое интересное:

$ kubectl get HelloApp -n test

image

Вот он — наш кастомный ресурс!


Часть 6: отладка оператора за пределами кластера

Здесь начинается самое увлекательное — можно отлаживать оператор построчно, чтобы узнать много интересного.

Для этого, во-первых, CRD должны быть установлены в кластере, как в части 4 (про установку CRD).

Убедитесь, что локальный кластер k8s запущен. При установке плагина Go в Visual Studio Code устанавливается и отладчик. Нажимаем Run → Start Debugging в VS Code, и конфигурация выполняется автоматически.

image

Ставим точки останова в нужных местах — и пожалуйста:

image

Открываем терминал, переходим к каталогу ~/demo-operator/config/samples
и деплоим CRD.

$ kubectl apply -f apps_v1_helloapp.yaml -n test

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


Часть 7: запуск оператора в кластере

Кастомный оператор — это просто набор файлов конфигурации (YAML) и docker-образ самого оператора. Минимальный набор файлов для кастомного оператора:


  1. Файл конфигурации для создания неймспейса.
  2. Файл конфигурации для создания сервисаккаунта.
  3. Файл конфигурации для определения ролей, которые понадобятся оператору для работы с API Kubernetes.
  4. Файл конфигурации для привязки ролей к сервисаккаунту из пункта 2.
  5. Наконец, файл конфигурации для развертывания самого оператора.

Создание ролей для оператора

image

В папке ~/demo-operator/config/rbac мы видим много файлов конфигурации. Прямо сейчас нам нужны не все. Но какие?

Заглянем в код нашего контроллера.

image

Вот что мы видим:


  1. Сейчас мы выполняем операцию Get, чтобы проверить наличие ресурса HelloApp. Значит, нам нужно разрешение на Get для ресурса HelloApp, который входит в группу API apps.anupam.com.
  2. Точно так же мы делаем Get, чтобы проверить ресурс Deployment. Кстати, ресурс Deployment в Kubernetes входит в группу API apps.
  3. Мы делаем Update для ресурса Deployment, который входит в группу API apps.

У нас тут есть комментарии, которые начинаются со знака +.

//+kubebuilder:rbac:groups=apps.anupam.com,resources=helloapps,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=apps.anupam.com,resources=helloapps/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=apps.anupam.com,resources=helloapps/finalizers,verbs=update

Это так называемые маркеры. Больше о маркерах Kubernetes можно узнать здесь. С помощью этих маркеров controller-gen CLI создает все артефакты (CRD, RBAC и т. д.).

После добавления маркеров достаточно выполнить команду make manifests. Она создаст или обновит все, что нужно, — CRD, роли, привязки ролей и т. д.


Создание Docker-образа для оператора

Кастомный оператор — это просто docker-образ со всеми своими артефактами для развертывания в кластере Kubernetes.

Если посмотреть структуру проекта, там уже будет Dockerfile, созданный Operator-SDK. Ничего не надо делать вручную.

image

Перейдем в корень проекта и выполним команду для сборки docker-образа:

$ make docker-build IMG=anupamgogoi/demo-operator:latest

Укажите свой репозиторий docker. После создания образа отправим его в registry следующей командой:

make docker-push IMG=anupamgogoi/demo-operator:latest

Все готово.


Создание готового пакета

Для простоты я вручную создам папку с именем dist в том же проекте и добавлю 5 файлов, о которых говорил в начале раздела.

image

Создаем 1-Namespace.yaml (укажите любое имя). В этом неймспейсе будет установлен оператор. Файлы 2, 3 и 4 можно скопировать из папки rbac. Копируем файл 5 из папки crd/bases. Наконец, создаем 6-Controller.yaml, чтобы развернуть оператор. Не забудьте изменить неймспейсы в файлах конфигурации. Вот готовые файлы.

image

Все готово.


Запуск оператора в кластере

У меня есть кластер Kubernetes на три ноды на виртуальной машине CentOS. Подробнее о создании кластера см. здесь. Я просто загружу эти файлы конфигурации на мастер-ноду Kubernetes и разверну оператор. Приступим.

Я скопировал каталог dist на мастер-ноду кластера, а теперь просто сделаю kubectl apply для всех файлов конфигурации.

image

Неймспейс я назвал demo-operator-system. Проверим, развернут ли в нем оператор.

image

Отлично. У меня развернут кастомный оператор и CRD. Имя CRD — helloapps.apps.anupam.com, как указано в файле конфигурации 5-apps.anupam.com_helloapps.yaml.

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

image

Как видно на скриншоте, после деплоя кастомного ресурса HelloApp (нижний терминал) в верхнем терминале появились логи. Это лучший способ отладки оператора при развертывании в кластере Kubernetes.

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

$ kubectl get all -n test

Пожалуйста!

image

Деплоймент на месте, и при этом он создал под в соответствии со спецификациями (size=1).

Давайте получим доступ к приложению из кластера, вызвав его по IP пода.

image

Вот и все. Мы получили от приложения ответ.


Заключение

В этой статье мы попытались создать очень простой кастомный оператор Kubernetes с нуля с помощью Operator-SDK. Здесь мы использовали Go, но можно выбрать любой другой язык. Экспериментируйте!

© Habrahabr.ru