Ci sono già stati articoli sul nostro blog che ne parlano e come . Questa volta vorremmo presentare alla vostra attenzione la nostra soluzione Open Source, che porta la creazione di operatori a un livello semplicissimo: guardate !
Perché?
L'idea di un operatore di shell è abbastanza semplice: iscriversi agli eventi degli oggetti Kubernetes e, quando questi eventi vengono ricevuti, avviare un programma esterno, fornendogli informazioni sull'evento:

La necessità è nata quando, durante il funzionamento dei cluster, hanno cominciato ad apparire piccoli compiti che volevamo davvero automatizzare nel modo giusto. Tutti questi piccoli compiti sono stati risolti utilizzando semplici script bash, anche se, come sai, è meglio scrivere gli operatori in Golang. Ovviamente, investire nello sviluppo su vasta scala di un operatore per ciascun compito così piccolo sarebbe inefficace.
Operatore tra 15 minuti
Diamo un'occhiata a un esempio di cosa può essere automatizzato in un cluster Kubernetes e di come l'operatore shell può essere d'aiuto. Un esempio potrebbe essere il seguente: replicare un segreto per accedere al registro della finestra mobile.
I pod che utilizzano immagini da un registro privato devono contenere nel loro manifest un collegamento a un segreto con i dati per l'accesso al registro. Questo segreto deve essere creato in ogni spazio dei nomi prima di creare i pod. Questo può essere fatto manualmente, ma se configuriamo ambienti dinamici, lo spazio dei nomi per un'applicazione diventerà molto. E se poi non ci sono 2-3 applicazioni... il numero di segreti diventa molto grande. E ancora una cosa sui segreti: vorrei cambiare di tanto in tanto la chiave per accedere al registro. Infine, operazioni manuali come soluzione completamente inefficace — dobbiamo automatizzare la creazione e l'aggiornamento dei segreti.
Automazione semplice
Scriviamo uno script di shell che viene eseguito una volta ogni N secondi e controlla gli spazi dei nomi per la presenza di un segreto e, se non è presente alcun segreto, viene creato. Il vantaggio di questa soluzione è che sembra uno script di shell in cron: un approccio classico e comprensibile a tutti. Lo svantaggio è che nell'intervallo tra i suoi lanci può essere creato un nuovo spazio dei nomi e per qualche tempo rimarrà senza segreto, il che porterà a errori nell'avvio dei pod.
Automazione con operatore shell
Affinché il nostro script funzioni correttamente, è necessario sostituire il classico lancio di cron con un lancio quando viene aggiunto un namespace: in questo caso è possibile creare un segreto prima di utilizzarlo. Vediamo come implementarlo utilizzando shell-operator.
Per prima cosa, diamo un'occhiata alla sceneggiatura. Gli script in termini di operatori shell sono chiamati hook. Ogni gancio quando viene eseguito con una bandiera --config informa l'operatore shell sui suoi collegamenti, cioè su quali eventi dovrebbe essere lanciato. Nel nostro caso useremo onKubernetesEvent:
#!/bin/bash
if [[ $1 == "--config" ]] ; then
cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "namespace",
"event":["add"]
}
]}
EOF
fi Qui viene descritto che siamo interessati ad aggiungere eventi (add) oggetti di tipo namespace.
Ora devi aggiungere il codice che verrà eseguito quando si verifica l'evento:
#!/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
fiGrande! Il risultato è stato una piccola, bellissima sceneggiatura. Per “ravvivarla” restano due passaggi: preparare l'immagine e lanciarla nel cluster.
Preparare un'immagine con un gancio
Se guardi lo script, puoi vedere che i comandi vengono utilizzati kubectl и jq. Ciò significa che l'immagine deve avere le seguenti cose: il nostro hook, un operatore di shell che monitorerà gli eventi ed eseguirà l'hook, e i comandi utilizzati dall'hook (kubectl e jq). Hub.docker.com ha già un'immagine già pronta in cui sono pacchettizzati shell-operator, kubectl e jq. Non resta che aggiungere un semplice gancio 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:v1In esecuzione in un cluster
Diamo nuovamente un'occhiata all'hook e questa volta scriviamo quali azioni e con quali oggetti esegue nel cluster:
- si iscrive agli eventi di creazione dello spazio dei nomi;
- crea un segreto in spazi dei nomi diversi da quello in cui viene avviato.
Si scopre che il pod in cui verrà avviata la nostra immagine deve disporre delle autorizzazioni per eseguire queste azioni. Questo può essere fatto creando il tuo ServiceAccount. L'autorizzazione deve essere rilasciata sotto forma di ClusterRole e ClusterRoleBinding, perché siamo interessati agli oggetti dell'intero cluster.
La descrizione finale in YAML sarà simile a questa:
---
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-namespacesPuoi avviare l'immagine assemblata come una semplice distribuzione:
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-accPer comodità, viene creato uno spazio dei nomi separato in cui verrà avviato l'operatore shell e verranno applicati i manifest creati:
$ kubectl create ns example-monitor-namespaces
$ kubectl -n example-monitor-namespaces apply -f rbac.yaml
$ kubectl -n example-monitor-namespaces apply -f deployment.yaml
Questo è tutto: l'operatore shell verrà avviato, si iscriverà agli eventi di creazione dello spazio dei nomi ed eseguirà l'hook quando necessario.
![]()
Così, la un semplice script di shell trasformato in un vero e proprio operatore per Kubernetes e funziona come parte di un cluster. E tutto questo senza il complesso processo di sviluppo degli operatori a Golang:

C'è un altro esempio su questo argomento...
Ne riveleremo il significato in modo più dettagliato in una delle seguenti pubblicazioni.
filtraggio
Tracciare gli oggetti è utile, ma spesso è necessario reagire modificando alcune proprietà dell'oggetto, ad esempio, per modificare il numero di repliche in Distribuzione o per modificare le etichette degli oggetti.
Quando arriva un evento, l'operatore shell riceve il manifest JSON dell'oggetto. Possiamo selezionare le proprietà che ci interessano in questo JSON ed eseguire l'hook solo quando cambiano. C'è un campo per questo jqFilter, dove è necessario specificare l'espressione jq che verrà applicata al manifest JSON.
Ad esempio, per rispondere alle modifiche nelle etichette per gli oggetti di distribuzione, è necessario filtrare il campo labels fuori dal campo metadata. La configurazione sarà così:
cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "deployment",
"event":["update"],
"jqFilter": ".metadata.labels"
}
]}
EOFQuesta espressione jqFilter trasforma il lungo manifest JSON di Deployment in un breve JSON con etichette:

shell-operator eseguirà l'hook solo quando questo breve JSON cambia e le modifiche ad altre proprietà verranno ignorate.
Hook contesto di lancio
La configurazione dell'hook ti consente di specificare diverse opzioni per gli eventi, ad esempio 2 opzioni per eventi da Kubernetes e 2 pianificazioni:
{"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"
]}Una piccola digressione: sì, supporta l'operatore shell eseguendo script in stile crontab. Maggiori dettagli possono essere trovati in .
Per distinguere il motivo per cui è stato lanciato l'hook, l'operatore shell crea un file temporaneo e ne passa il percorso in una variabile all'hook BINDING_CONTEXT_TYPE. Il file contiene una descrizione JSON del motivo dell'esecuzione dell'hook. Ad esempio, ogni 10 minuti verrà eseguito l'hook con il seguente contenuto:
[{ "binding": "every 10 min"}]...e lunedì si inizierà così:
[{ "binding": "every 10 min"}, { "binding": "on Mondays at 12:10"}] per onKubernetesEvent Ci saranno più trigger JSON, perché contiene una descrizione dell'oggetto:
[
{
"binding": "onCreatePod",
"resourceEvent": "add",
"resourceKind": "pod",
"resourceName": "foo",
"resourceNamespace": "bar"
}
] Il contenuto dei campi può essere compreso dai loro nomi e si possono leggere maggiori dettagli . Un esempio di come ottenere un nome di risorsa da un campo resourceName l'uso di jq è già stato mostrato in un hook che replica i segreti:
jq -r '.[0].resourceName' $BINDING_CONTEXT_PATHPuoi ottenere altri campi in modo simile.
Quali sono le prospettive?
Nel repository del progetto, in , ci sono esempi di hook pronti per essere eseguiti su un cluster. Quando scrivi i tuoi hook, puoi usarli come base.
È disponibile il supporto per la raccolta di parametri utilizzando Prometheus: i parametri disponibili sono descritti nella sezione .
Come puoi immaginare, l'operatore shell è scritto in Go e distribuito con licenza Open Source (Apache 2.0). Saremo grati per qualsiasi assistenza allo sviluppo : e stelle, problemi e richieste pull.
Sollevando il velo di segretezza, vi informeremo anche che l'operatore shell è piccolo parte del nostro sistema che può mantenere aggiornati i componenti aggiuntivi installati nel cluster Kubernetes ed eseguire varie azioni automatiche. Ulteriori informazioni su questo sistema letteralmente lunedì all'HighLoad++ 2019 a San Pietroburgo: presto pubblicheremo il video e la trascrizione di questo rapporto.
Abbiamo un piano per rendere accessibile il resto di questo sistema: l'operatore aggiuntivo e la nostra collezione di hook e moduli. A proposito, addon-operator lo è già , ma la relativa documentazione è ancora in arrivo. L'uscita della raccolta di moduli è prevista per l'estate.
Rimanete sintonizzati!
PS
Leggi anche sul nostro blog:
- «";
- «";
- «";
- «";
- «'.
Fonte: habr.com
