Представяме ви shell-operator: създаването на оператори за Kubernetes току-що стана по-лесно

Вече има статии в нашия блог, които говорят за операторски възможности в Kubernetes и как напишете сами прост оператор. Този път бихме искали да представим на вашето внимание нашето решение с отворен код, което извежда създаването на оператори на супер лесно ниво - вижте shell-оператор!

Защо?

Идеята за shell-оператор е доста проста: абонирайте се за събития от обекти на Kubernetes и когато тези събития бъдат получени, стартирайте външна програма, предоставяйки й информация за събитието:

Представяме ви shell-operator: създаването на оператори за 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:

$ cat Dockerfile
FROM flant/shell-operator:v1.0.0-beta.1-alpine3.9
ADD namespace-hook.sh /hooks

$ docker build -t registry.example.com/my-operator:v1 . 
$ docker push registry.example.com/my-operator:v1

Работи в клъстер

Нека отново да разгледаме куката и този път да запишем какви действия и с какви обекти извършва в клъстера:

  1. абонира се за събития за създаване на пространство от имена;
  2. създава тайна в пространства от имена, различни от това, където е стартирана.

Оказва се, че подът, в който ще се стартира нашето изображение, трябва да има разрешения за извършване на тези действия. Това може да стане чрез създаване на собствен ServiceAccount. Разрешението трябва да бъде направено под формата на ClusterRole и ClusterRoleBinding, защото интересуваме се от обекти от целия клъстер.

Крайното описание в YAML ще изглежда така:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: monitor-namespaces-acc

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: monitor-namespaces
rules:
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["get", "watch", "list"]
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "list", "create", "patch"]

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: monitor-namespaces
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: monitor-namespaces
subjects:
  - kind: ServiceAccount
    name: monitor-namespaces-acc
    namespace: example-monitor-namespaces

Можете да стартирате сглобеното изображение като просто разполагане:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-operator
spec:
  template:
    spec:
      containers:
      - name: my-operator
        image: registry.example.com/my-operator:v1
      serviceAccountName: monitor-namespaces-acc

За удобство се създава отделно пространство от имена, където ще се стартира shell-операторът и ще се прилагат създадените манифести:

$ kubectl create ns example-monitor-namespaces
$ kubectl -n example-monitor-namespaces apply -f rbac.yaml
$ kubectl -n example-monitor-namespaces apply -f deployment.yaml

Това е всичко: shell-операторът ще стартира, ще се абонира за събития за създаване на пространство от имена и ще стартира куката, когато е необходимо.

Представяме ви shell-operator: създаването на оператори за Kubernetes току-що стана по-лесно

По този начин, прост шел скрипт, превърнат в истински оператор за Kubernetes и работи като част от клъстер. И всичко това без сложния процес на разработване на оператори в Golang:

Представяме ви shell-operator: създаването на оператори за Kubernetes току-що стана по-лесно

Има още една илюстрация по този въпрос...Представяме ви shell-operator: създаването на оператори за Kubernetes току-що стана по-лесно

Неговото значение ще разкрием по-подробно в някоя от следващите публикации.

филтриране

Проследяването на обекти е добро, но често има нужда да се реагира промяна на някои свойства на обекта, например, за да промените броя на репликите в Deployment или да промените етикетите на обекти.

Когато пристигне събитие, операторът на обвивката получава JSON манифеста на обекта. Можем да изберем свойствата, които ни интересуват в този JSON и да стартираме куката само когато се променят. Има поле за това jqFilter, където трябва да укажете jq израза, който ще бъде приложен към JSON манифеста.

Например, за да отговорите на промените в етикетите за обекти за разполагане, трябва да филтрирате полето labels извън полето metadata. Конфигурацията ще бъде така:

cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "deployment",
  "event":["update"],
  "jqFilter": ".metadata.labels"
}
]}
EOF

Този израз на jqFilter превръща дългия JSON манифест на Deployment в кратък JSON с етикети:

Представяме ви shell-operator: създаването на оператори за Kubernetes току-що стана по-лесно

shell-operator ще стартира куката само когато този кратък JSON се промени, а промените в други свойства ще бъдат игнорирани.

Контекст за стартиране на кука

Конфигурацията на кука ви позволява да посочите няколко опции за събития - например 2 опции за събития от Kubernetes и 2 графици:

{"onKubernetesEvent":[
  {"name":"OnCreatePod",
  "kind": "pod",
  "event":["add"]
  },
  {"name":"OnModifiedNamespace",
  "kind": "namespace",
  "event":["update"],
  "jqFilter": ".metadata.labels"
  }
],
"schedule": [
{ "name":"every 10 min",
  "crontab":"* */10 * * * *"
}, {"name":"on Mondays at 12:10",
"crontab": "* 10 12 * * 1"
]}

Малко отклонение: да, shell-operator поддържа стартиране на скриптове в стил crontab. Повече подробности можете да намерите в документация.

За да различи защо куката е стартирана, shell-операторът създава временен файл и предава пътя до него в променлива на куката BINDING_CONTEXT_TYPE. Файлът съдържа JSON описание на причината за стартиране на куката. Например, на всеки 10 минути куката ще се изпълнява със следното съдържание:

[{ "binding": "every 10 min"}]

... и в понеделник ще започне с това:

[{ "binding": "every 10 min"}, { "binding": "on Mondays at 12:10"}]

За onKubernetesEvent Ще има повече JSON тригери, защото съдържа описание на обекта:

[
 {
 "binding": "onCreatePod",
 "resourceEvent": "add",
 "resourceKind": "pod",
 "resourceName": "foo",
 "resourceNamespace": "bar"
 }
]

Съдържанието на полетата може да се разбере от имената им, а повече подробности можете да прочетете в документация. Пример за получаване на име на ресурс от поле resourceName използването на jq вече е показано в кука, която репликира тайни:

jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH

Можете да получите други полета по подобен начин.

Каква е следващата?

В хранилището на проекта, в /примери директории, има примери за кукички, които са готови за изпълнение на клъстер. Когато пишете ваши собствени кукички, можете да ги използвате като основа.

Има поддръжка за събиране на показатели с помощта на Prometheus - наличните показатели са описани в раздела МЕТРИКА.

Както може би се досещате, shell-операторът е написан на Go и се разпространява под лиценз с отворен код (Apache 2.0). Ще бъдем благодарни за всяка помощ за развитие проект в GitHub: и звезди, и проблеми, и заявки за изтегляне.

Повдигайки завесата на тайната, ще ви информираме също, че черупката е оператор малък част от нашата система, която може да поддържа актуални добавките, инсталирани в клъстера Kubernetes, и да изпълнява различни автоматични действия. Прочетете повече за тази система каза буквално в понеделник на HighLoad++ 2019 в Санкт Петербург - скоро ще публикуваме видеото и преписа на този доклад.

Имаме план да отворим останалата част от системата: addon-operator и нашата колекция от кукички и модули. Между другото, addon-operator вече е достъпно на github, но документацията за него все още е на път. Пускането на колекцията от модули е планирано за лятото.

Остани настроен!

PS

Прочетете също в нашия блог:

Източник: www.habr.com

Добавяне на нов коментар