Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

8 de abril en la conferencia Santo HighLoad++ 2019, como parte de la sección “DevOps y Operaciones”, se entregó el informe “Ampliando y complementando Kubernetes”, en cuya elaboración participaron tres empleados de la empresa Flant. En él, hablamos de numerosas situaciones en las que queríamos ampliar y complementar las capacidades de Kubernetes, pero para las que no encontramos una solución sencilla y preparada. Disponemos de las soluciones necesarias en forma de proyectos Open Source, y este discurso también está dedicado a ellos.

Por tradición, nos complace presentarles vídeo del informe (50 minutos, mucho más informativo que el artículo) y el resumen principal en forma de texto. ¡Ir!

Núcleo y adiciones en K8

Kubernetes está cambiando la industria y los enfoques de administración establecidos desde hace mucho tiempo:

  • Gracias a él abstracciones, ya no operamos con conceptos como configurar una configuración o ejecutar un comando (Chef, Ansible...), sino que utilizamos agrupaciones de contenedores, servicios, etc.
  • Podemos preparar aplicaciones sin pensar en los matices de la sitio específico, en el que se lanzará: bare metal, nube de uno de los proveedores, etc.
  • Con los K8 nunca has sido más accesible mejores practicas sobre la organización de la infraestructura: técnicas de escalado, autocuración, tolerancia a fallos, etc.

Pero, por supuesto, no todo es tan sencillo: Kubernetes también trajo sus propios nuevos desafíos.

Kubernetes no es una cosechadora que resuelve todos los problemas de todos los usuarios. núcleo Kubernetes es responsable únicamente de un conjunto de funciones mínimas necesarias que están presentes en cada uno grupo:

Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

El núcleo de Kubernetes define un conjunto básico de primitivas para agrupar contenedores, gestionar el tráfico, etc. Hablamos de ellos con más detalle en informe hace 2 años.

Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

Por otro lado, K8s ofrece grandes oportunidades para ampliar las funciones disponibles, lo que ayuda a cerrar otras. específico — necesidades del usuario. Las adiciones a Kubernetes son responsabilidad de los administradores de clústeres, quienes deben instalar y configurar todo lo necesario para que su clúster esté “en la forma correcta” [para resolver sus problemas específicos]. ¿Qué tipo de adiciones son estas? Veamos algunos ejemplos.

Ejemplos de complementos

Una vez instalado Kubernetes, nos sorprenderá que la red tan necesaria para la interacción de los pods tanto dentro de un nodo como entre nodos no funcione por sí sola. El kernel de Kubernetes no garantiza las conexiones necesarias, sino que determina la red interfaz (CNI) para complementos de terceros. Debemos instalar uno de estos complementos, que será el encargado de la configuración de la red.

Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

Un ejemplo cercano son las soluciones de almacenamiento de datos (disco local, dispositivo de bloque de red, Ceph...). Inicialmente estaban en el núcleo, pero con la llegada CSI la situación cambia a algo similar a lo ya descrito: la interfaz está en Kubernetes y su implementación está en módulos de terceros.

Otros ejemplos incluyen:

  • Ingreso-controladores (ver su reseña en nuestro artículo reciente).
  • administrador de certificados:

    Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

  • operadores es una clase completa de complementos (que incluye el administrador de certificados mencionado), definen primitivas y controladores. La lógica de su trabajo está limitada únicamente por nuestra imaginación y nos permite convertir componentes de infraestructura ya preparados (por ejemplo, un DBMS) en primitivos, con los que es mucho más fácil trabajar (que con un conjunto de contenedores y sus configuraciones). Se han escrito una gran cantidad de operadores; aunque muchos de ellos aún no están listos para la producción, es sólo cuestión de tiempo:

    Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

  • Métrica - otra ilustración de cómo Kubernetes separó la interfaz (Metrics API) de la implementación (complementos de terceros como el adaptador Prometheus, el agente de clúster Datadog...).
  • para seguimiento y estadísticas, donde en la práctica no sólo se necesitan Prometeo y Grafana, pero también kube-state-metrics, node-exporter, etc.

Y esta no es una lista completa de adiciones... Por ejemplo, en la empresa Flant actualmente instalamos 29 adiciones (todos los cuales crean un total de 249 objetos de Kubernetes). En pocas palabras, no podemos ver la vida de un clúster sin adiciones.

Automatización

Los operadores están diseñados para automatizar las operaciones rutinarias que encontramos todos los días. A continuación se muestran ejemplos de la vida real en los que escribir un operador sería una excelente solución:

  1. Hay un registro privado (es decir, que requiere iniciar sesión) con imágenes para la aplicación. Se supone que a cada pod se le asigna un secreto especial que permite la autenticación en el registro. Nuestra tarea es garantizar que este secreto se encuentre en el espacio de nombres para que los pods puedan descargar imágenes. Puede haber muchas aplicaciones (cada una de las cuales necesita un secreto) y es útil actualizar los secretos con regularidad, por lo que se elimina la opción de diseñar los secretos a mano. Aquí es donde el operador viene al rescate: creamos un controlador que esperará a que aparezca el espacio de nombres y, en función de este evento, agregará un secreto al espacio de nombres.
  2. Deje que por defecto el acceso desde los pods a Internet esté prohibido. Pero a veces puede ser necesario: es lógico que el mecanismo de permiso de acceso funcione de forma sencilla, sin requerir habilidades específicas, por ejemplo, mediante la presencia de una determinada etiqueta en el espacio de nombres. ¿Cómo puede ayudarnos el operador aquí? Se crea un controlador que espera a que aparezca la etiqueta en el espacio de nombres y agrega la política adecuada para el acceso a Internet.
  3. Una situación similar: supongamos que necesitáramos agregar un cierto mancha, si tiene una etiqueta similar (con algún tipo de prefijo). Las acciones con el operador son obvias...

En cualquier cluster, las tareas rutinarias deben resolverse y correctamente esto se puede hacer usando operadores.

Resumiendo todas las historias descritas, llegamos a la conclusión de que para trabajar cómodamente en Kubernetes necesitas: pero) instalar complementos, B) desarrollar operadores (para resolver tareas administrativas diarias).

¿Cómo escribir una declaración para Kubernetes?

En general, el esquema es sencillo:

Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

... pero luego resulta que:

  • La API de Kubernetes no es algo trivial y requiere mucho tiempo para dominarlo;
  • La programación tampoco es para todos (el lenguaje Go fue elegido como el lenguaje preferido porque existe un marco especial para ello). SDK del operador);
  • La situación es similar con el propio marco.

El resultado final: escribir un controlador (el operador) tiene que gastar recursos significativos para estudiar material. Esto estaría justificado para operadores "grandes", por ejemplo, para el DBMS MySQL. Pero si recordamos los ejemplos descritos anteriormente (desvelar secretos, acceder a los pods a Internet...), que también queremos hacer correctamente, entonces entenderemos que el esfuerzo invertido superará el resultado que necesitamos ahora:

Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

En general, surge un dilema: gastar muchos recursos y encontrar la herramienta adecuada para escribir declaraciones, o hacerlo a la antigua usanza (pero rápidamente). Para solucionarlo, para encontrar un compromiso entre estos extremos, creamos nuestro propio proyecto: operador de shell (ver también su anuncio reciente en el centro).

operador de shell

¿Cómo trabaja? El clúster tiene un pod que contiene un binario de Go con un operador de shell. Junto a él hay un conjunto de manos (más detalles sobre ellos - ver más abajo). El propio operador shell se suscribe a ciertos desarrollos en la API de Kubernetes, ante lo cual lanza los correspondientes ganchos.

¿Cómo sabe el operador de shell qué ganchos llamar en qué eventos? Esta información se transmite al operador de shell a través de los propios ganchos, y lo hacen de forma muy sencilla.

Un gancho es un script Bash o cualquier otro archivo ejecutable que acepte un único argumento. --config y responde con JSON. Este último determina qué objetos le interesan y a qué eventos (para estos objetos) se debe responder:

Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

Ilustraré la implementación en el operador de shell de uno de nuestros ejemplos: descomponiendo secretos para acceder a un registro privado con imágenes de aplicaciones. Consiste en dos etapas.

Práctica: 1. Escribe un gancho

En primer lugar, en el gancho procesaremos. --config, indicando que estamos interesados ​​en los espacios de nombres, y en concreto, el momento de su creación:

[[ $1 == "--config" ]] ; then
  cat << EOF
{
  "onKubernetesEvent": [
    {
      "kind": "namespace",
      "event": ["add"]
    }
  ]
}
EOF
…

¿Cómo sería la lógica? También bastante simple:

…
else
  createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH)
  kubectl create -n ${createdNamespace} -f - << EOF
Kind: Secret
...
EOF
fi

El primer paso es averiguar qué espacio de nombres se creó y el segundo es crearlo usando kubectl secreto para este espacio de nombres.

Práctica: 2. Armar la imagen

Todo lo que queda es pasar el gancho creado al operador de shell. ¿Cómo hacerlo? El operador de shell en sí viene como una imagen de Docker, por lo que nuestra tarea es agregar el gancho a un directorio especial en esta imagen:

FROM flant/shell-operator:v1.0.0-beta.1
ADD my-handler.sh /hooks

Ya sólo queda montarlo y empujarlo:

$ docker build -t registry.example.com/my-operator:v1 .
$ docker push registry.example.com/my-operator:v1

El toque final es implementar la imagen en el clúster. Para hacer esto, escribamos Despliegue:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-operator
spec:
  template:
    spec:
      containers:
      - name: my-operator
        image: registry.example.com/my-operator:v1 # 1
      serviceAccountName: my-operator              # 2

Hay dos puntos a los que prestar atención:

  1. indicación de la imagen recién creada;
  2. Este es un componente del sistema que (como mínimo) necesita derechos para suscribirse a eventos en Kubernetes y asignar secretos a espacios de nombres, por lo que creamos una cuenta de servicio (y un conjunto de reglas) para el enlace.

Resultado: resolvimos nuestro problema. a parientes para Kubernetes de una manera que crea un operador para descomponer secretos.

Otras características del operador shell

Para limitar los objetos del tipo elegido con los que funcionará el gancho, se pueden filtrar, seleccionando según determinadas etiquetas (o utilizando matchExpressions):

"onKubernetesEvent": [
  {
    "selector": {
      "matchLabels": {
        "foo": "bar",
       },
       "matchExpressions": [
         {
           "key": "allow",
           "operation": "In",
           "values": ["wan", "warehouse"],
         },
       ],
     }
     …
  }
]

Previsto mecanismo de deduplicación, que, utilizando un filtro jq, le permite convertir objetos JSON grandes en objetos pequeños, donde solo quedan aquellos parámetros que queremos monitorear para detectar cambios.

Cuando se llama a un gancho, el operador de shell lo pasa datos del objeto, que se puede utilizar para cualquier necesidad.

Los eventos que activan los ganchos no se limitan a los eventos de Kubernetes: el operador de shell brinda soporte para llamando ganchos por tiempo (similar a crontab en un programador tradicional), así como un evento especial en el arranque. Todos estos eventos se pueden combinar y asignar al mismo gancho.

Y dos características más del operador shell:

  1. Funciona asincrónicamente. Dado que se recibió un evento de Kubernetes (como la creación de un objeto), podrían haber ocurrido otros eventos (como la eliminación del mismo objeto) en el clúster, y los enlaces deben tener en cuenta esto. Si el enlace se ejecutó con un error, de forma predeterminada será recordar hasta que se complete con éxito (este comportamiento se puede cambiar).
  2. exporta métrica para Prometheus, con el que puede comprender si el operador de shell está funcionando, averiguar la cantidad de errores para cada enlace y el tamaño de la cola actual.

Para resumir esta parte del informe:

Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

Instalación de complementos

Para trabajar cómodamente con Kubernetes, también se mencionó la necesidad de instalar complementos. Se lo contaré usando el ejemplo del camino de nuestra empresa sobre cómo lo hacemos ahora.

Comenzamos a trabajar con Kubernetes con varios clústeres, la única adición fue Ingress. Había que instalarlo de forma diferente en cada cluster, y realizamos varias configuraciones YAML para diferentes entornos: bare metal, AWS...

Como había más clusters, había más configuraciones. Además, mejoramos estas configuraciones, como resultado de lo cual se volvieron bastante heterogéneas:

Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

Para poner todo en orden, comenzamos con un script (install-ingress.sh), que tomó como argumento el tipo de clúster en el que implementaremos, generó la configuración YAML necesaria y la implementó en Kubernetes.

En resumen, nuestro camino posterior y el razonamiento asociado a él fueron los siguientes:

  • para trabajar con configuraciones YAML, se requiere un motor de plantillas (en las primeras etapas es simple sed);
  • con el aumento en el número de clústeres, surgió la necesidad de realizar actualizaciones automáticas (la primera solución fue colocar el script en Git, actualizarlo usando cron y ejecutarlo);
  • Se requirió un guión similar para Prometeo (install-prometheus.sh), sin embargo, se destaca por el hecho de que requiere muchos más datos de entrada, así como su almacenamiento (en el buen sentido, centralizado y en un clúster), y algunos datos (contraseñas) podrían generarse automáticamente:

    Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

  • El riesgo de implementar algo incorrecto en un número cada vez mayor de clústeres crecía constantemente, por lo que nos dimos cuenta de que los instaladores (es decir, dos guiones: para Ingress y Prometheus) se necesitaba staging (varias ramas en Git, varios crons para actualizarlos en los clusters correspondientes: estables o de prueba);
  • с kubectl apply se ha vuelto difícil trabajar con él porque no es declarativo y solo puede crear objetos, pero no tomar decisiones sobre su estado/eliminarlos;
  • Nos faltaban algunas funciones que no habíamos implementado en absoluto en ese momento:
    • control total sobre el resultado de las actualizaciones del clúster,
    • determinación automática de algunos parámetros (entrada para scripts de instalación) en función de los datos que se pueden obtener del clúster (descubrimiento),
    • su desarrollo lógico en forma de descubrimiento continuo.

Implementamos toda esta experiencia acumulada en el marco de nuestro otro proyecto: operador-adicional.

operador adicional

Se basa en el operador shell ya mencionado. Todo el sistema se ve así:

Se agrega lo siguiente a los ganchos del operador de shell:

  • almacenamiento de valores,
  • Carta de timón,
  • componente que monitorea el almacén de valores y, en caso de algún cambio, le pide a Helm que vuelva a ejecutar el gráfico.

Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

Por lo tanto, podemos reaccionar ante un evento en Kubernetes, lanzar un gancho y desde este gancho podemos realizar cambios en el almacenamiento, después de lo cual el gráfico se volverá a descargar. En el diagrama resultante, separamos el conjunto de ganchos y el gráfico en un componente, al que llamamos módulo:

Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

Puede haber muchos módulos y a ellos les agregamos ganchos globales, un almacén de valores globales y un componente que monitorea este almacén global.

Ahora, cuando algo sucede en Kubernetes, podemos reaccionar usando un gancho global y cambiar algo en la tienda global. Este cambio se notará y hará que se implementen todos los módulos del clúster:

Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

Este esquema satisface todos los requisitos para instalar complementos mencionados anteriormente:

  • Helm es responsable de las plantillas y la declaratividad.
  • El problema de la actualización automática se resolvió mediante un enlace global, que va al registro según un cronograma y, si ve una nueva imagen del sistema allí, la implementa (es decir, "él mismo").
  • El almacenamiento de configuraciones en el clúster se implementa usando Mapa de configuración, que contiene los datos primarios para los almacenamientos (al inicio, se cargan en los almacenamientos).
  • Los problemas con la generación, el descubrimiento y el descubrimiento continuo de contraseñas se resolvieron mediante ganchos.
  • La puesta en escena se logra gracias a las etiquetas, que Docker admite desde el primer momento.
  • El resultado se monitorea mediante métricas mediante las cuales podemos comprender el estado.

Todo este sistema se implementa en forma de un único binario en Go, que se llama operador adicional. Esto hace que el diagrama parezca más simple:

Ampliando y complementando Kubernetes (revisión y reportaje en vídeo)

El componente principal de este diagrama es un conjunto de módulos. (resaltado en gris a continuación). Ahora podemos escribir un módulo para el complemento requerido con un poco de esfuerzo y estar seguros de que se instalará en cada clúster, se actualizará y responderá a los eventos que necesita en el clúster.

Usos "planos" operador-adicional en más de 70 clústeres de Kubernetes. Estado actual - versión alfa. Ahora estamos preparando documentación para lanzar la beta, pero por ahora en el repositorio. ejemplos disponibles, a partir del cual puedes crear tu propio complemento.

¿Dónde puedo conseguir los módulos para el operador adicional? Publicar nuestra biblioteca es el siguiente paso para nosotros; planeamos hacerlo en el verano.

Vídeos y diapositivas

Vídeo de la actuación (~50 minutos):

Presentación del informe:

PS

Otros reportajes en nuestro blog:

También te pueden interesar las siguientes publicaciones:

Fuente: habr.com

Añadir un comentario