Notre blog
contient déjà des articles sur les
capacités des opérateurs de Kubernetes et comment
écrire vous-même un opérateur simple . Cette fois, nous voulons attirer votre attention sur notre solution Open Source, qui élève la création d'opérateurs à un niveau super facile - familiarisez-vous avec l'
opérateur shell !
Pourquoi?
L'idée d'un opérateur shell est assez simple: abonnez-vous aux événements des objets Kubernetes, et lors de la réception de ces événements, démarrez un programme externe, en lui fournissant des informations sur l'événement:

Le besoin s'est fait sentir chez nous lorsque, lors du fonctionnement des clusters, de petites tâches ont commencé à apparaître que nous voulions vraiment automatiser de la bonne manière. Toutes ces tâches mineures ont été résolues à l'aide de scripts bash simples, bien que, comme vous le savez, les opérateurs soient mieux écrits en Golang. De toute évidence, investir dans un développement d'opérateur à grande échelle pour chacune de ces petites tâches serait inefficace.
Opérateur en 15 minutes
Examinons un exemple de ce qui peut être automatisé dans un cluster Kubernetes et comment l'aide-opérateur va aider. Un exemple serait le suivant: duplication d'un secret pour accéder au registre docker.
Les pods qui utilisent des images du registre privé doivent contenir dans leur manifeste un lien vers un secret contenant des données pour accéder au registre. Ce secret doit être créé dans chaque espace de noms avant de créer des pods. Il est tout à fait possible de le faire manuellement, mais si nous configurons des environnements dynamiques, il y aura beaucoup d'espace de noms pour une application. Et si les applications ne sont pas non plus 2-3, le nombre de secrets devient très important. Et encore une chose au sujet des secrets: je voudrais changer la clé pour accéder au registre de temps en temps. En conséquence,
les opérations manuelles en tant que solution sont
totalement inefficaces - vous devez automatiser la création et la mise à jour des secrets.
Automatisation simple
Nous allons écrire un script shell qui s'exécute une fois toutes les N secondes et vérifie l'espace de noms pour un secret, et s'il n'y a pas de secret, il est créé. L'avantage de cette solution est qu'elle ressemble à un script shell dans cron - une approche classique et compréhensible. L'inconvénient est que dans l'intervalle entre ses lancements, un nouvel espace de noms peut être créé et pendant un certain temps, il restera sans secret, ce qui entraînera des erreurs lors du lancement des pods.
Automatisation avec shell-operator
Pour que notre script fonctionne correctement, le lancement classique de cron doit être remplacé par le lancement à l'espace de noms d'événement est ajouté: dans ce cas, vous pouvez réussir à créer un secret avant de l'utiliser. Voyons comment implémenter cela en utilisant l'opérateur shell.
Tout d'abord, analysons le script. Les scripts en termes d'opérateur shell sont appelés hooks. Chaque hook au démarrage avec l'indicateur
--config
informe l'opérateur du shell de ses liaisons, c'est-à-dire par quels événements il doit être lancé. Dans notre cas, nous utiliserons
onKubernetesEvent
:
Il est décrit ici que nous nous intéressons aux événements pour ajouter (
add
) des objets de type
namespace
.
Maintenant, vous devez ajouter le code qui sera exécuté lorsque l'événement se produira:
Super! Le résultat est un petit et beau script. Pour la «faire revivre», il reste deux étapes: préparer l'image et l'exécuter dans le cluster.
Préparer une image avec un crochet
Si vous regardez le script, vous pouvez voir que les
jq
kubectl
et
jq
. Cela signifie que l'image doit avoir les éléments suivants: notre hook, un opérateur shell qui surveillera les événements et lancera le hook, ainsi que les commandes utilisées par le hook (kubectl et jq).
Hub.docker.com a déjà une image toute prête dans laquelle shell-operator, kubectl et jq sont emballés. Il reste à ajouter le hook avec un simple
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
Lancement du cluster
Encore une fois, regardons le crochet et écrivons cette fois quelles actions et avec quels objets il effectue dans le cluster:
- S'abonner aux événements d'espace de noms
- crée un secret dans des espaces de noms autres que celui où il s'exécute.
Il s'avère que le pod dans lequel notre image sera lancée doit avoir des autorisations pour ces actions. Cela peut être fait en créant votre propre ServiceAccount. L'autorisation doit être effectuée sous la forme de ClusterRole et ClusterRoleBinding, car nous nous intéressons aux objets de partout dans le cluster.
La description finale dans YAML ressemble à ceci:
--- 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
Vous pouvez exécuter l'image assemblée sous la forme d'un déploiement simple:
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
Pour plus de commodité, un espace de noms séparé est créé où l'opérateur shell sera lancé et les manifestes créés seront appliqués:
$ kubectl create ns example-monitor-namespaces $ kubectl -n example-monitor-namespaces apply -f rbac.yaml $ kubectl -n example-monitor-namespaces apply -f deployment.yaml
C'est tout: l'opérateur shell va démarrer, s'abonner aux événements de création d'espace de noms et démarrer le hook si nécessaire.

Ainsi, un
simple script shell est devenu un véritable opérateur pour Kubernetes et fonctionne comme une partie intégrante du cluster. Et tout cela - sans le processus compliqué de développement d'opérateurs sur Golang:

Filtrage
Le suivi des objets est bon, mais il est souvent nécessaire de répondre aux
modifications de certaines propriétés d'un objet , par exemple à une modification du nombre de répliques dans un déploiement ou à une modification des étiquettes d'un objet.
Lorsqu'un événement arrive, l'opérateur shell reçoit le manifeste JSON de l'objet. Vous pouvez sélectionner les propriétés qui nous intéressent dans ce JSON et exécuter le hook
uniquement lorsqu'elles changent. Pour ce faire, le champ
jqFilter
est
jqFilter
, où vous devez spécifier l'expression jq qui sera appliquée au manifeste JSON.
Par exemple, pour répondre aux modifications d'étiquette sur les objets de déploiement, vous devez filtrer le champ d'
labels
partir du champ de
metadata
. La configuration sera comme ceci:
cat <<EOF { "onKubernetesEvent": [ { "kind": "deployment", "event":["update"], "jqFilter": ".metadata.labels" } ]} EOF
Cette expression dans jqFilter transforme le manifeste JSON long du déploiement en JSON court avec des étiquettes:

L'opérateur shell ne déclenchera un hook que lorsque ce court JSON change et que les modifications apportées à d'autres propriétés sont ignorées.
Contexte de lancement du crochet
La configuration du hook vous permet de spécifier plusieurs options pour les événements - par exemple, 2 options pour les événements de Kubernetes et 2 planifications:
{"onKubernetesEvent":[ {"name":"OnCreatePod", "kind": "pod", "event":["add"] }, {"name":"OnModifiedNamespace", "kind": "namespace", "event":["update"], "jqFilter": ".metadata.labels" } ], "schedule": [ { "name":"every 10 min", "crontab":"0 */10 * * * *" }, {"name":"on Mondays at 12:10", "crontab": "0 10 12 * * 1" ]}
Une petite digression: oui, shell-operator prend en charge l' exécution de scripts de style crontab . Vous pouvez en lire plus dans la documentation .Pour distinguer la raison du lancement du hook, l'opérateur shell crée un fichier temporaire et lui transmet le chemin d'accès au
BINDING_CONTEXT_TYPE
dans la variable
BINDING_CONTEXT_TYPE
. Le fichier contient une description JSON de la raison du démarrage du hook. Par exemple, toutes les 10 minutes, un hook démarre avec le contenu suivant:
[{ "binding": "every 10 min"}]
... et lundi, cela commencera par ceci:
[{ "binding": "every 10 min"}, { "binding": "on Mondays at 12:10"}]
Pour
onKubernetesEvent
aura plus de tir JSON depuis il contient une description de l'objet:
[ { "binding": "onCreatePod", "resourceEvent": "add", "resourceKind": "pod", "resourceName": "foo", "resourceNamespace": "bar" } ]
Le contenu des champs peut être compris à partir de leurs noms, et plus en détail - lire dans la
documentation . Un exemple d'obtention du nom de ressource à partir du champ
resourceName
aide de jq a déjà été montré dans un hook qui réplique des secrets:
jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH
De même, vous pouvez obtenir les champs restants.
Et ensuite?
Dans le référentiel de projet, dans
le répertoire / examples , il existe des exemples de hooks prêts à être exécutés dans le cluster. Lorsque vous écrivez vos crochets, vous pouvez les prendre comme base.
La collecte des métriques à l'aide de Prometheus est prise en charge - les métriques disponibles sont écrites dans la section
METRICS .
Comme vous pouvez le deviner, l'opérateur shell est écrit en Go et distribué sous la licence Open Source (Apache 2.0). Nous serons reconnaissants pour toute aide sur le développement du
projet sur GitHub : astérisques, problèmes et demandes de tirage.
Ouvrant le voile du secret, nous informons également que l'opérateur shell est une
petite partie de notre système, qui peut maintenir à jour les modules complémentaires installés dans le cluster Kubernetes et effectuer diverses actions automatiques. Nous avons
parlé de ce système plus en détail lundi à HighLoad ++ 2019 à Saint-Pétersbourg - la vidéo et la transcription de ce rapport seront publiées sous peu.
Nous avons un plan pour ouvrir le reste de ce système: addon-operator et notre collection de crochets et de modules. Soit dit en passant, addon-operator est déjà
disponible sur GitHub , mais sa documentation est toujours en cours. La sortie de la collection de modules est prévue en été.
Restez à l'écoute!
PS
Lisez aussi dans notre blog: