Идеята за shell-оператор е доста проста: абонирайте се за събития от обекти на Kubernetes и когато тези събития бъдат получени, стартирайте външна програма, предоставяйки й информация за събитието:
Нуждата от него възникна, когато по време на работа на клъстери започнаха да се появяват малки задачи, които наистина искахме да автоматизираме по правилния начин. Всички тези малки задачи бяха решени с помощта на прости bash скриптове, въпреки че, както знаете, е по-добре да пишете оператори на Golang. Очевидно инвестирането в пълномащабно разработване на оператор за всяка такава малка задача би било неефективно.
Оператор за 15 минути
Нека да разгледаме пример за това какво може да се автоматизира в клъстер на Kubernetes и как операторът на обвивката може да помогне. Пример би бил следният: репликиране на тайна за достъп до регистъра на докерите.
Подовете, които използват изображения от частен регистър, трябва да съдържат в своя манифест връзка към тайна с данни за достъп до регистъра. Тази тайна трябва да бъде създадена във всяко пространство от имена, преди да се създадат подове. Това може да стане ръчно, но ако настроим динамични среди, тогава пространството от имена за едно приложение ще стане много. И ако няма и 2-3 приложения... броят на тайните става много голям. И още нещо за тайните: бих искал да сменям ключа за достъп до системния регистър от време на време. В крайна сметка, ръчни операции като решение напълно неефективни — трябва да автоматизираме създаването и актуализирането на тайни.
Проста автоматизация
Нека напишем шел скрипт, който се изпълнява веднъж на всеки N секунди и проверява пространствата от имена за наличие на тайна и ако няма тайна, тогава тя се създава. Предимството на това решение е, че изглежда като shell скрипт в cron – класически и разбираем подход за всеки. Недостатъкът е, че в интервала между стартиранията му може да се създаде ново пространство от имена и известно време да остане без тайна, което ще доведе до грешки при стартирането на pods.
Автоматизация с шел-оператор
За да работи правилно нашият скрипт, класическото стартиране на cron трябва да бъде заменено със стартиране, когато се добави пространство от имена: в този случай можете да създадете тайна, преди да я използвате. Нека видим как да приложим това с помощта на shell-operator.
Първо, нека да разгледаме сценария. Скриптовете в термините на оператора на обвивката се наричат кукички. Всяка кука, когато се изпълнява с флаг --config информира shell-оператора за своите обвързвания, т.е. при какви събития трябва да се стартира. В нашия случай ще използваме onKubernetesEvent:
#!/bin/bash
if [[ $1 == "--config" ]] ; then
cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "namespace",
"event":["add"]
}
]}
EOF
fi
Тук е описано, че се интересуваме от добавяне на събития (add) обекти от тип namespace.
Сега трябва да добавите кода, който ще бъде изпълнен, когато настъпи събитието:
#!/bin/bash
if [[ $1 == "--config" ]] ; then
# конфигурация
cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "namespace",
"event":["add"]
}
]}
EOF
else
# реакция:
# узнать, какой namespace появился
createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH)
# создать в нём нужный секрет
kubectl create -n ${createdNamespace} -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
...
data:
...
EOF
fi
Страхотен! Резултатът беше малък, красив сценарий. За да го „съживите“, остават две стъпки: подгответе изображението и го стартирайте в клъстера.
Подготовка на изображение с кука
Ако погледнете скрипта, можете да видите, че командите се използват kubectl и jq. Това означава, че изображението трябва да има следните неща: нашата кука, shell-оператор, който ще наблюдава събитията и ще изпълнява куката, и командите, използвани от куката (kubectl и jq). Hub.docker.com вече има готов образ, в който са пакетирани shell-operator, kubectl и jq. Остава само да добавите обикновена кука Dockerfile:
Нека отново да разгледаме куката и този път да запишем какви действия и с какви обекти извършва в клъстера:
абонира се за събития за създаване на пространство от имена;
създава тайна в пространства от имена, различни от това, където е стартирана.
Оказва се, че подът, в който ще се стартира нашето изображение, трябва да има разрешения за извършване на тези действия. Това може да стане чрез създаване на собствен ServiceAccount. Разрешението трябва да бъде направено под формата на ClusterRole и ClusterRoleBinding, защото интересуваме се от обекти от целия клъстер.
Това е всичко: shell-операторът ще стартира, ще се абонира за събития за създаване на пространство от имена и ще стартира куката, когато е необходимо.
По този начин, прост шел скрипт, превърнат в истински оператор за Kubernetes и работи като част от клъстер. И всичко това без сложния процес на разработване на оператори в Golang:
Има още една илюстрация по този въпрос...
Неговото значение ще разкрием по-подробно в някоя от следващите публикации.
филтриране
Проследяването на обекти е добро, но често има нужда да се реагира промяна на някои свойства на обекта, например, за да промените броя на репликите в Deployment или да промените етикетите на обекти.
Когато пристигне събитие, операторът на обвивката получава JSON манифеста на обекта. Можем да изберем свойствата, които ни интересуват в този JSON и да стартираме куката само когато се променят. Има поле за това jqFilter, където трябва да укажете jq израза, който ще бъде приложен към JSON манифеста.
Например, за да отговорите на промените в етикетите за обекти за разполагане, трябва да филтрирате полето labels извън полето metadata. Конфигурацията ще бъде така:
Малко отклонение: да, shell-operator поддържа стартиране на скриптове в стил crontab. Повече подробности можете да намерите в документация.
За да различи защо куката е стартирана, shell-операторът създава временен файл и предава пътя до него в променлива на куката BINDING_CONTEXT_TYPE. Файлът съдържа JSON описание на причината за стартиране на куката. Например, на всеки 10 минути куката ще се изпълнява със следното съдържание:
Съдържанието на полетата може да се разбере от имената им, а повече подробности можете да прочетете в документация. Пример за получаване на име на ресурс от поле resourceName използването на jq вече е показано в кука, която репликира тайни:
jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH
Можете да получите други полета по подобен начин.
Каква е следващата?
В хранилището на проекта, в /примери директории, има примери за кукички, които са готови за изпълнение на клъстер. Когато пишете ваши собствени кукички, можете да ги използвате като основа.
Има поддръжка за събиране на показатели с помощта на Prometheus - наличните показатели са описани в раздела МЕТРИКА.
Както може би се досещате, shell-операторът е написан на Go и се разпространява под лиценз с отворен код (Apache 2.0). Ще бъдем благодарни за всяка помощ за развитие проект в GitHub: и звезди, и проблеми, и заявки за изтегляне.
Повдигайки завесата на тайната, ще ви информираме също, че черупката е оператор малък част от нашата система, която може да поддържа актуални добавките, инсталирани в клъстера Kubernetes, и да изпълнява различни автоматични действия. Прочетете повече за тази система каза буквално в понеделник на HighLoad++ 2019 в Санкт Петербург - скоро ще публикуваме видеото и преписа на този доклад.
Имаме план да отворим останалата част от системата: addon-operator и нашата колекция от кукички и модули. Между другото, addon-operator вече е достъпно на github, но документацията за него все още е на път. Пускането на колекцията от модули е планирано за лятото.