Фото: Jukan Tateisi | Unsplash В этой статье мы подробно рассмотрим, как создать собственный оператор Kubernetes с нуля. Операторы — это такие программные расширения, которые используют кастомные ресурсы (kind), чтобы управлять приложениями. Подробности читайте в официальной документации. Возьмем самый простой пример — приложение HelloApp. Чтобы задеплоить HelloApp, создадим ресурс Kubernetes.  Смотрим на kind: HelloApp — это кастомное определение ресурса (Custom Resource Definition, CRD), которое обрабатывается нашим оператором (или контроллером). Здесь мы с нуля создадим такой оператор (или контроллер). Пока я не начал создавать операторы, я думал, что для этого подходит только Go. Это миф. Реализовать оператор (контроллер) можно на любом языке и в любом рантайме, которые могут быть клиентом для Kubernetes API. Скриншот из документации по Kubernetes:  По сути Kubernetes — это такая большая система мониторинга. Все функции доступны через API (сервер API). Допустим, мы пишем приложение, которое как клиент обращается к серверу API Kubernetes и выполняет нужные действия. Это и есть оператор. Так что язык здесь не важен. Но раз Go — это нативный язык рантайма Kubernetes, и у него очень много библиотек для реализации операторов, обычно выбирают его. Нам понадобятся: Статья разделена на несколько частей: Часть 1: создание проекта оператора
Часть 2: реализация логики оператора
Часть 3: создание CRD
Часть 4: установка CRD
Часть 5: запуск оператора за пределами кластера
Часть 6: отладка оператора за пределами кластера
Часть 7: запуск оператора в кластере Чтобы создать структуру проекта, используем Operator SDK. При написании руководства я использовал Operator SDK версии 1.5. Чтобы подробно изучить создание проекта с помощью SDK, читайте официальную документацию. Для краткости я расскажу о создании проекта в двух словах. С помощью этих команд мы подготовили минимальную структуру проекта для дальнейшей работы.  Примечания о некоторых важных файлах: Логика оператора предельно проста. Когда применяется CRD (ниже), оператор (контроллер) должен создать деплоймент для kind HelloApp с числом подов, которое мы указали в spec.size.  Полный исходный код вы найдете в этом репозитории. В spec у нас два поля: image (образ) и size (размер). Чтобы добавить их в spec, редактируем файл [4] из первой части статьи.  Логику контроллера можно найти здесь. Тут никакой магии. Ничего особенного он не делает — просто проверяет наличие деплоймента для HelloApp и пытается создать его, если он отсутствует. Наконец он проверяет, что число подов соответствует указанному в spec.size, а если нет — создает больше подов. Вот и все. Подробности об API читайте в официальной документации.  Контроллеры реализуют интерфейс Reconciler, который предоставляет метод Reconcile. Функция Reconcile вызывается для таких событий в кластере, как операции CRUD (create read update delete — «Создание чтение обновление удаление», прим. переводчика), и сравнивает фактический статус ресурса (kind) с ожидаемым (spec). Если есть различия, он их исправляет.  Файл main.go создается Operator-SDK и содержит дополнительный код. Для простоты я удалил все лишнее и оставил только то, что нужно для запуска оператора. Изучите файл main.go. Вот самая важная часть кода в файле main.go:  ctrl.GetConfigOrDie() попытается считать конфигурацию кластера Kubernetes из файла ~/.kube/config и получит информацию о подключении. Файл ~/.kube/config выглядит примерно так:  Здесь IP сервера, сертификаты и т. д. Это главная часть. Метод GetConfigOrDie() считывает эту информацию, и на ее основе ctrl.NewManager() создает менеджер для контроллера. Остается просто вызвать API с сервера API Kubernetes. Здесь и начинается магия. Просто изучите файл main.go, и все станет ясно. Даже kubectl CLI использует вызовы API к серверу API Kubernetes. Выполните эту команду и убедитесь сами:  Итак, логика контроллера HelloApp готова. Теперь надо создать для нее CRD. В корне demo-operator выполняем команду: Она создает CRD в каталоге
~/demo-operator/config/crd/bases  В каталоге ~/demo-operator/config/samples создается пример.  Чтобы запустить оператор, нужен локальный кластер. Используем kind, чтобы создать кластер на локальном хосте. Есть два способа установить определения CRD в кластере: просто выполняем команду или переходим в ~/demo-operator/config/crd/bases и выполняем команду Результат будет одинаковым. Это самый простой способ запустить и отладить логику оператора. Чтобы заглянуть внутрь Kubernetes, лучше всего начать с Вот что мы видим:  Давайте задеплоим наш кастомный ресурс — перейдем в каталог samples и применим ресурс: Посмотрим, что мы создали в неймспейсе test:  А теперь самое интересное:  Вот он — наш кастомный ресурс! Здесь начинается самое увлекательное — можно отлаживать оператор построчно, чтобы узнать много интересного. Для этого, во-первых, CRD должны быть установлены в кластере, как в части 4 (про установку CRD). Убедитесь, что локальный кластер k8s запущен. При установке плагина Go в Visual Studio Code устанавливается и отладчик. Нажимаем Run → Start Debugging в VS Code, и конфигурация выполняется автоматически.  Ставим точки останова в нужных местах — и пожалуйста:  Открываем терминал, переходим к каталогу ~/demo-operator/config/samples
и деплоим CRD. В первый раз запустится цикл Reconcile, и программа будет останавливаться в точках останова в функции Reconcile, как на рисунке выше. Для обновления или удаления тоже будет вызываться функция Reconcile. Поэкспериментируйте, пока не поймете, как это работает. Кастомный оператор — это просто набор файлов конфигурации (YAML) и docker-образ самого оператора. Минимальный набор файлов для кастомного оператора:  В папке ~/demo-operator/config/rbac мы видим много файлов конфигурации. Прямо сейчас нам нужны не все. Но какие? Заглянем в код нашего контроллера.  Вот что мы видим: У нас тут есть комментарии, которые начинаются со знака +. Это так называемые маркеры. Больше о маркерах Kubernetes можно узнать здесь. С помощью этих маркеров controller-gen CLI создает все артефакты (CRD, RBAC и т. д.). После добавления маркеров достаточно выполнить команду make manifests. Она создаст или обновит все, что нужно, — CRD, роли, привязки ролей и т. д. Кастомный оператор — это просто docker-образ со всеми своими артефактами для развертывания в кластере Kubernetes. Если посмотреть структуру проекта, там уже будет Dockerfile, созданный Operator-SDK. Ничего не надо делать вручную.  Перейдем в корень проекта и выполним команду для сборки docker-образа: Укажите свой репозиторий docker. После создания образа отправим его в registry следующей командой: Все готово. Для простоты я вручную создам папку с именем dist в том же проекте и добавлю 5 файлов, о которых говорил в начале раздела.  Создаем 1-Namespace.yaml (укажите любое имя). В этом неймспейсе будет установлен оператор. Файлы 2, 3 и 4 можно скопировать из папки rbac. Копируем файл 5 из папки crd/bases. Наконец, создаем 6-Controller.yaml, чтобы развернуть оператор. Не забудьте изменить неймспейсы в файлах конфигурации. Вот готовые файлы.  Все готово. У меня есть кластер Kubernetes на три ноды на виртуальной машине CentOS. Подробнее о создании кластера см. здесь. Я просто загружу эти файлы конфигурации на мастер-ноду Kubernetes и разверну оператор. Приступим. Я скопировал каталог dist на мастер-ноду кластера, а теперь просто сделаю kubectl apply для всех файлов конфигурации.  Неймспейс я назвал demo-operator-system. Проверим, развернут ли в нем оператор.  Отлично. У меня развернут кастомный оператор и CRD. Имя CRD — helloapps.apps.anupam.com, как указано в файле конфигурации 5-apps.anupam.com_helloapps.yaml. Теперь можно создать кастомный ресурс, или kind, то есть HelloApp. Откроем еще один терминал мастер-ноды для проверки логов кастомного оператора, а в другом терминале задеплоим ресурс HelloApp.  Как видно на скриншоте, после деплоя кастомного ресурса HelloApp (нижний терминал) в верхнем терминале появились логи. Это лучший способ отладки оператора при развертывании в кластере Kubernetes. Теперь проверим, создан ли деплоймент для кастомного ресурса HelloApp. Пожалуйста!  Деплоймент на месте, и при этом он создал под в соответствии со спецификациями (size=1). Давайте получим доступ к приложению из кластера, вызвав его по IP пода.  Вот и все. Мы получили от приложения ответ. В этой статье мы попытались создать очень простой кастомный оператор Kubernetes с нуля с помощью Operator-SDK. Здесь мы использовали Go, но можно выбрать любой другой язык. Экспериментируйте! Редактор