Presentamos el operador de shell: hacer que los operadores de Kubernetes sean aún más fáciles

Ya ha habido artículos en nuestro blog sobre las capacidades de los operadores en Kubernetes y cómo escribir un operador simple usted mismo . Esta vez queremos llamar su atención sobre nuestra solución de código abierto, que lleva la creación de operadores a un nivel súper fácil: ¡familiarícese con el operador de shell !

Por qué


La idea de un operador de shell es bastante simple: suscríbase a eventos de objetos de Kubernetes, y cuando reciba estos eventos, inicie un programa externo, proporcionándole información sobre el evento:



La necesidad surgió con nosotros cuando, durante la operación de los clústeres, comenzaron a aparecer pequeñas tareas que realmente queríamos automatizar de la manera correcta. Todas estas tareas menores se resolvieron con la ayuda de simples secuencias de comandos bash, aunque, como saben, los operadores están mejor escritos en Golang. Obviamente, invertir en un desarrollo de operador a gran escala para cada tarea tan pequeña sería ineficiente.

Operador en 15 minutos


Veamos un ejemplo de lo que se puede automatizar en un clúster de Kubernetes y cómo ayudará el operador de shell. Un ejemplo sería el siguiente: duplicar un secreto para acceder al registro de docker.

Los pods que usan imágenes del registro privado deben contener en su manifiesto un enlace a un secreto con datos para acceder al registro. Este secreto debe crearse en cada espacio de nombres antes de crear pods. Es bastante posible hacer esto manualmente, pero si configuramos entornos dinámicos, habrá mucho espacio de nombres para una aplicación. Y si las aplicaciones tampoco son 2-3 ... el número de secretos se vuelve muy grande. Y una cosa más sobre los secretos: me gustaría cambiar la clave para acceder al registro de vez en cuando. Como resultado, las operaciones manuales como solución son completamente ineficaces : debe automatizar la creación y actualización de secretos.

Automatización sencilla


Escribiremos un script de shell que se ejecuta una vez cada N segundos y verifica el espacio de nombres para un secreto, y si no hay secreto, entonces se crea. La ventaja de esta solución es que parece un script de shell en cron, un enfoque clásico y comprensible. La desventaja es que en el intervalo entre sus lanzamientos se puede crear un nuevo espacio de nombres y durante algún tiempo permanecerá sin un secreto, lo que conducirá a errores en el lanzamiento de pods.

Automatización con operador de shell


Para que nuestro script funcione correctamente, el lanzamiento clásico de cron debe ser reemplazado por el lanzamiento en el espacio de nombres del evento: en este caso, puede lograr crear un secreto antes de usarlo. Veamos cómo implementar esto usando el operador de shell.

Primero, analicemos el script. Las secuencias de comandos en términos de operador de shell se denominan ganchos. Cada --config al inicio con la bandera --config le dice al operador de shell acerca de sus enlaces, es decir por qué eventos necesita ser lanzado. En nuestro caso, utilizaremos onKubernetesEvent :

 #!/bin/bash if [[ $1 == "--config" ]] ; then cat <<EOF { "onKubernetesEvent": [ { "kind": "namespace", "event":["add"] } ]} EOF fi 

Aquí se describe que estamos interesados ​​en eventos para agregar ( add ) objetos de tipo namespace de namespace .

Ahora debe agregar el código que se ejecutará cuando ocurra el 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 El resultado es un guión pequeño y hermoso. Para "revivirlo", quedan dos pasos: preparar la imagen y ejecutarla en el clúster.

Preparando una imagen con un gancho


Si observa el script, puede ver que jq kubectl y jq . Esto significa que la imagen debe tener lo siguiente: nuestro gancho, un operador de shell que supervisará los eventos y ejecutará el gancho, así como los comandos utilizados por el gancho (kubectl y jq). Hub.docker.com ya tiene una imagen preparada en la que se empaquetan shell-operator, kubectl y jq. Queda por agregar el gancho con 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 

Lanzamiento de clúster


Una vez más, veamos el gancho y esta vez escriba qué acciones y con qué objetos realiza en el clúster:

  1. Se suscribe a eventos de espacio de nombres
  2. crea un secreto en espacios de nombres distintos de donde se está ejecutando.

Resulta que el pod en el que se lanzará nuestra imagen debe tener permisos para estas acciones. Esto se puede hacer creando su propia cuenta de servicio. El permiso debe hacerse en forma de ClusterRole y ClusterRoleBinding, porque Estamos interesados ​​en objetos de todo el clúster.

La descripción final en YAML es algo como esto:

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

Puede ejecutar la imagen ensamblada como una Implementación 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 


Por conveniencia, se crea un espacio de nombres separado donde se iniciará el operador de shell y se aplicarán los manifiestos 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 


Eso es todo: el operador de shell se iniciará, se suscribirá a los eventos de creación de espacios de nombres e iniciará el enlace cuando sea necesario.



Por lo tanto, un simple script de shell se convirtió en un operador real para Kubernetes y funciona como una parte integral del clúster. Y todo esto, sin el complicado proceso de desarrollar operadores en Golang:



Hay otra ilustración sobre este tema ...


Revelaremos su significado con más detalle en una de las siguientes publicaciones. ACTUALIZADO (1 de mayo de 2019): consulte " Ampliación y expansión de Kubernetes (informe de revisión y video) ".

Filtrado


El seguimiento de objetos es bueno, pero a menudo es necesario responder a cambios en algunas propiedades de un objeto , por ejemplo, a un cambio en el número de réplicas en una Implementación, o a un cambio en las etiquetas de un objeto.

Cuando llega un evento, el operador de shell recibe el manifiesto JSON del objeto. Puede seleccionar las propiedades que nos interesan en este JSON y ejecutar el enlace solo cuando cambien. Para hacer esto, se jqFilter campo jqFilter , donde debe especificar la expresión jq que se aplicará al manifiesto JSON.

Por ejemplo, para responder a los cambios de etiquetas en los objetos de implementación, debe filtrar el campo de labels campo de metadata . La configuración será así:

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

Esta expresión en jqFilter convierte el manifiesto JSON largo de implementación en JSON corto con etiquetas:



El operador de shell solo activará un enlace cuando este breve JSON cambie y se ignoren los cambios a otras propiedades.

Contexto de lanzamiento de gancho


La configuración de enlace le permite especificar varias opciones para eventos, por ejemplo, 2 opciones para eventos de Kubernetes y 2 horarios:

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

Una pequeña digresión: sí, el operador de shell admite la ejecución de scripts de estilo crontab . Puedes leer más en la documentación .

Para distinguir por qué se lanzó el gancho, el operador de shell crea un archivo temporal y le pasa la ruta al BINDING_CONTEXT_TYPE en la variable BINDING_CONTEXT_TYPE . El archivo contiene una descripción JSON de la razón por la que comenzó el enlace. Por ejemplo, cada 10 minutos un gancho comenzará con los siguientes contenidos:

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

... y el lunes comenzará con esto:

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

Para onKubernetesEvent habrá más disparos JSON desde Contiene una descripción del objeto:

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

El contenido de los campos puede entenderse a partir de sus nombres y, con más detalle, lea la documentación . Un ejemplo de cómo obtener el nombre del resourceName campo resourceName usando jq ya se ha mostrado en un gancho que replica secretos:

 jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH 

Del mismo modo, puede obtener los campos restantes.

Que sigue


En el repositorio del proyecto, en el directorio / examples , hay ejemplos de enlaces que están listos para ejecutarse en el clúster. Al escribir sus ganchos, puede tomarlos como base.

Hay soporte para recopilar métricas usando Prometheus: las métricas disponibles están escritas en la sección METRICS .

Como puede suponer, el operador de shell está escrito en Go y distribuido bajo la licencia Open Source (Apache 2.0). Agradeceremos cualquier ayuda para desarrollar el proyecto en GitHub : asteriscos, problemas y solicitudes de extracción.

Al abrir el velo del secreto, también informamos que el operador de shell es una pequeña parte de nuestro sistema, que puede mantener actualizados los complementos instalados en el clúster de Kubernetes y realizar varias acciones automáticas. Hablamos sobre este sistema con más detalle el lunes en HighLoad ++ 2019 en San Petersburgo: en breve se publicará un video y una transcripción de este informe.

Tenemos un plan para abrir el resto de este sistema: addon-operator y nuestra colección de ganchos y módulos. Por cierto, addon-operator ya está disponible en GitHub , pero la documentación aún está en camino. El lanzamiento de la colección de módulos está previsto para el verano.

Estén atentos!

PS


Lea también en nuestro blog:

Source: https://habr.com/ru/post/447442/


All Articles