image
Фото: Jukan Tateisi | Unsplash


В этой статье мы подробно рассмотрим, как создать собственный оператор 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). Если есть различия, он их исправляет.



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, но можно выбрать любой другой язык. Экспериментируйте!