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
fi
Grande! 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:
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:
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ì:
Questa 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:
Una piccola digressione: sì, supporta l'operatore shell eseguendo script in stile crontab. Maggiori dettagli possono essere trovati in documentazione.
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:
Il contenuto dei campi può essere compreso dai loro nomi e si possono leggere maggiori dettagli documentazione. 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_PATH
Puoi ottenere altri campi in modo simile.
Quali sono le prospettive?
Nel repository del progetto, in /directory di esempi, 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 METRICA.
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 progetto su GitHub: 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 detto 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à disponibile su github, ma la relativa documentazione è ancora in arrivo. L'uscita della raccolta di moduli è prevista per l'estate.