Presentación de shell-operator: crear operadores para Kubernetes é máis fácil

Xa houbo artigos no noso blog falando capacidades do operador en Kubernetes e como escribe ti mesmo un operador sinxelo. Desta volta queremos presentarlle á súa atención a nosa solución de código aberto, que leva a creación de operadores a un nivel súper sinxelo. operador de shell!

Por que?

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:

Presentación de shell-operator: crear operadores para Kubernetes é máis fácil

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:

$ 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

Correndo nun clúster

Vexamos de novo o gancho e esta vez anotemos que accións e con que obxectos realiza no clúster:

  1. subscríbese a eventos de creación de espazos de nomes;
  2. 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.

A descrición final en YAML terá un aspecto así:

---
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

Podes lanzar a imaxe ensamblada como unha simple implementación:

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

Para comodidade, créase un espazo de nomes separado onde se iniciará o operador de shell e aplicaranse os manifestos creados:

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

Iso é todo: o operador de shell iniciarase, subscribirase aos eventos de creación de espazos de nomes e executará o gancho cando sexa necesario.

Presentación de shell-operator: crear operadores para Kubernetes é máis fácil

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:

Presentación de shell-operator: crear operadores para Kubernetes é máis fácil

Hai outra ilustración sobre este asunto...Presentación de shell-operator: crear operadores para Kubernetes é máis fácil

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 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í:

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

Esta expresión jqFilter converte o manifesto JSON longo de Deployment en JSON curto con etiquetas:

Presentación de shell-operator: crear operadores para Kubernetes é máis fácil

shell-operator só executará o gancho cando este curto JSON cambie, e os cambios noutras propiedades serán ignorados.

Contexto de lanzamento do gancho

A configuración do gancho permítelle especificar varias opcións para eventos, por exemplo, 2 opcións para eventos de Kubernetes e 2 programacións:

{"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"
]}

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:

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

... e o luns comezará con isto:

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

Para onKubernetesEvent Haberá máis activadores JSON, porque contén unha descrición do obxecto:

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

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.

Sexa atento!

PS

Lea tamén no noso blog:

Fonte: www.habr.com

Engadir un comentario