A idea dun operador de shell é bastante sinxela: subscríbase a eventos de obxectos Kubernetes e, cando se reciban estes eventos, inicie un programa externo, proporcionándolle información sobre o evento:
A necesidade desta xurdiu cando, durante o funcionamento dos clústeres, comezaron a aparecer pequenas tarefas que realmente queriamos automatizar da maneira correcta. Todas estas pequenas tarefas resolvéronse mediante scripts bash sinxelos, aínda que, como sabes, é mellor escribir operadores en Golang. Obviamente, investir no desenvolvemento a gran escala dun operador para cada pequena tarefa sería ineficaz.
Operador en 15 minutos
Vexamos un exemplo do que se pode automatizar nun clúster de Kubernetes e como pode axudar o operador de shell. Un exemplo sería o seguinte: replicar un segredo para acceder ao rexistro docker.
Os pods que utilicen imaxes dun rexistro privado deben conter no seu manifesto unha ligazón a un segredo con datos para acceder ao rexistro. Este segredo debe crearse en cada espazo de nomes antes de crear pods. Isto pódese facer manualmente, pero se configuramos ambientes dinámicos, entón o espazo de nomes dunha aplicación será moito. E se tampouco hai 2-3 aplicacións... o número de segredos faise moi grande. E unha cousa máis sobre os segredos: gustaríame cambiar a clave para acceder ao rexistro de cando en vez. Finalmente, operacións manuais como solución completamente ineficaz — Necesitamos automatizar a creación e actualización de segredos.
Automatización sinxela
Escribamos un script de shell que se execute unha vez cada N segundos e comprobe os espazos de nomes para detectar a presenza dun segredo e, se non hai segredo, créase. A vantaxe desta solución é que parece un script de shell en cron, un enfoque clásico e comprensible para todos. A desvantaxe é que no intervalo entre os seus lanzamentos pódese crear un novo espazo de nomes que permanecerá durante algún tempo sen segredo, o que provocará erros no lanzamento dos pods.
Automatización con operador shell
Para que o noso script funcione correctamente, o clásico lanzamento cron debe substituírse por un lanzamento cando se engade un espazo de nomes: neste caso, pode crear un segredo antes de usalo. Vexamos como implementar isto usando shell-operator.
En primeiro lugar, vexamos o guión. Os scripts en termos de operador de shell chámanse ganchos. Cada gancho cando corre cunha bandeira --config informa ao operador de shell sobre os seus enlaces, é dicir. sobre que eventos debería lanzarse. No noso caso utilizaremos onKubernetesEvent:
#!/bin/bash
if [[ $1 == "--config" ]] ; then
cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "namespace",
"event":["add"]
}
]}
EOF
fi
Descríbese aquí que estamos interesados en engadir eventos (add) obxectos de tipo namespace.
Agora cómpre engadir o código que se executará cando se produza o 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
Genial! O resultado foi un pequeno e fermoso guión. Para "revivir" quedan dous pasos: preparar a imaxe e lanzala no clúster.
Preparación dunha imaxe cun gancho
Se miras o script, podes ver que se usan os comandos kubectl и jq. Isto significa que a imaxe debe ter as seguintes cousas: o noso gancho, un operador de shell que escoitará eventos e executará o gancho, e os comandos utilizados polo gancho (kubectl e jq). Hub.docker.com xa ten unha imaxe preparada na que se empaquetan shell-operator, kubectl e jq. Só queda engadir un simple gancho Dockerfile:
Vexamos de novo o gancho e esta vez anotemos que accións e con que obxectos realiza no clúster:
subscríbese a eventos de creación de espazos de nomes;
crea un segredo en espazos de nomes distintos a aquel onde se inicia.
Resulta que o pod no que se lanzará a nosa imaxe debe ter permisos para realizar estas accións. Isto pódese facer creando a súa propia conta de servizo. O permiso debe realizarse en forma de ClusterRole e ClusterRoleBinding, porque interésanos os obxectos de todo o clúster.
Iso é todo: o operador de shell iniciarase, subscribirase aos eventos de creación de espazos de nomes e executará o gancho cando sexa necesario.
Así, o un simple script de shell convertido nun operador real para Kubernetes e funciona como parte dun clúster. E todo isto sen o complexo proceso de desenvolvemento de operadores en Golang:
Hai outra ilustración sobre este asunto...
Desvelaremos o seu significado con máis detalle nunha das seguintes publicacións.
filtrado
Rastrexar obxectos é bo, pero moitas veces hai que reaccionar modificando algunhas propiedades do obxecto, por exemplo, para cambiar o número de réplicas en Implementación ou para cambiar as etiquetas dos obxectos.
Cando chega un evento, o operador de shell recibe o manifesto JSON do obxecto. Podemos seleccionar as propiedades que nos interesan neste JSON e executar o gancho só cando cambian. Hai un campo para iso jqFilter, onde precisa especificar a expresión jq que se aplicará ao manifesto JSON.
Por exemplo, para responder aos cambios nas etiquetas dos obxectos de Implementación, cómpre filtrar o campo labels fóra do campo metadata. A configuración será así:
Unha pequena digresión: si, admite o operador de shell executando scripts de estilo crontab. Podes atopar máis detalles en documentación.
Para distinguir por que se lanzou o gancho, o operador de shell crea un ficheiro temporal e pasa o camiño a el nunha variable ao gancho BINDING_CONTEXT_TYPE. O ficheiro contén unha descrición JSON do motivo para executar o gancho. Por exemplo, cada 10 minutos o gancho executarase co seguinte contido:
Os contidos dos campos pódense entender a partir dos seus nomes e pódense ler máis detalles documentación. Un exemplo de obter un nome de recurso dun campo resourceName usando jq xa se mostrou nun gancho que replica segredos:
jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH
Podes obter outros campos dun xeito similar.
Cal é o próximo?
No repositorio do proxecto, en directorios /examples, hai exemplos de ganchos que están listos para executarse nun clúster. Ao escribir os teus propios ganchos, podes utilizalos como base.
Hai soporte para recoller métricas usando Prometheus; as métricas dispoñibles descríbense na sección MÉTRICA.
Como podes adiviñar, o shell-operator está escrito en Go e distribúese baixo unha licenza de código aberto (Apache 2.0). Agradeceremos calquera axuda para o desenvolvemento proxecto en GitHub: e estrelas, e problemas e solicitudes de extracción.
Levantando o veo do segredo, tamén lle informaremos que é o operador de shell pequeno parte do noso sistema que pode manter actualizados os complementos instalados no clúster de Kubernetes e realizar varias accións automáticas. Ler máis sobre este sistema contou literalmente o luns en HighLoad++ 2019 en San Petersburgo - en breve publicaremos o vídeo e a transcrición deste informe.
Temos un plan para abrir o resto deste sistema: o add-operator e a nosa colección de ganchos e módulos. Por certo, o add-operator xa está dispoñible en github, pero a documentación para iso aínda está en camiño. O lanzamento da colección de módulos está previsto para o verán.