BLOG

Gestión de miles de clústeres Edge Kubernetes con GitOps

Miniatura F5
F5
Publicado el 18 de diciembre de 2019

En Volterra, el trabajo del equipo SRE es operar una plataforma edge global basada en SaaS . Tenemos que resolver varios desafíos en la gestión de una gran cantidad de clústeres de aplicação en varios estados (es decir, en línea, fuera de línea, administrador inactivo, etc.) y lo hacemos aprovechando el ecosistema y las herramientas de Kubernetes con un modelo declarativo basado en extracción usando GitOps.

En este blog describiremos:

Uso de GitOps para administrar y monitorear eficazmente una gran flota de infraestructura (hosts físicos o en la nube) y clústeres K8

  1. Las herramientas que construimos para resolver problemas relacionados con CI/CD
  2. Orquestación de objetos y gestión de configuración
  3. Observabilidad en toda la flota de clústeres Infra y K8s

Profundizaremos en las lecciones aprendidas a escala (3000 sitios de borde), que también se trataron en mi reciente charla en Cloud Native Rejekts en San Diego.

TL;DR (Resumen)

  1. No pudimos encontrar una solución simple y lista para usar que pudiera usarse para implementar y operar miles (y potencialmente millones) de clústeres de aplicação e infraestructura en la nube pública, en ubicaciones locales o nómadas.
     
  2. Esta solución necesitaba proporcionar la gestión del ciclo de vida de los hosts (físicos o en la nube), el plano de control de Kubernetes, las cargas de trabajo de las aplicação y la configuración continua de varios servicios. Además, la solución debía cumplir con nuestros requisitos de diseño de SRE: definición declarativa, ciclo de vida inmutable, gitops y sin acceso directo a los clústeres.
     
  3. Después de evaluar varios proyectos de código abierto (por ejemplo, Kubespray+Ansible (para la implementación de Kubernetes) o Helm/Spinnaker (para la gestión de cargas de trabajo), llegamos a la conclusión de que ninguna de estas soluciones podría satisfacer nuestros requisitos anteriores sin agregar una sobrecarga de software significativa en cada sitio de borde. Como resultado, decidimos construir nuestro propio demonio de software basado en Golang que realizaba la gestión del ciclo de vida del host (físico o en la nube), el plano de control de Kubernetes y las cargas de trabajo de las aplicação .
     
  4. A medida que ampliamos el sistema a 3000 clústeres y más (dentro de un solo inquilino), todas nuestras suposiciones sobre nuestros proveedores de nube pública, la escalabilidad de nuestros daemons de software, las herramientas de operaciones y la infraestructura de observabilidad se rompieron sistemáticamente. Cada uno de ellos requirió la reestructuración de algunos de nuestros componentes de software para superar estos desafíos.

Definición del borde

  • Customer Edge (CE) : son ubicaciones de clientes en la nube (como AWS, Azure, GCP o nube privada), ubicaciones locales (como fábricas, instalaciones de petróleo y gas, etc.) o ubicaciones nómadas (como automoción, robótica, etc.). CE es administrado por el equipo SRE de Volterra pero los clientes también pueden implementarlo a pedido en las ubicaciones que elijan.
  • Bordes regionales (RE) : son puntos de presencia (PoP) de Volterra en instalaciones de coubicación en los principales mercados metropolitanos que están interconectados con nuestra propia red troncal privada altamente interconectada. Estos sitios de borde regionales también se utilizan para interconectar de forma segura ubicaciones de borde del cliente (CE) y/o exponer servicios de aplicação a Internet público. Los sitios de RE están completamente administrados y son propiedad del equipo de operaciones de infraestructura de Volterra (Infra SRE).
administrar01
Figura 1: Descripción general del sistema

El diagrama de arquitectura (Figura 1) anterior muestra la conectividad lógica entre nuestros RE y CE, donde cada CE establece conexiones redundantes (IPSec o SSL VPN) con el RE más cercano.

Requisitos para la gestión de borde

Cuando comenzamos a diseñar nuestra plataforma hace aproximadamente 2 años, nuestro equipo de producto nos pidió que resolviéramos los siguientes desafíos:

  1. Escalabilidad del sistema : nuestros clientes necesitaban que respaldáramos miles (eventualmente millones) de sitios perimetrales de clientes y esto es muy diferente a ejecutar un puñado de clústeres de Kubernetes en regiones de la nube. Por ejemplo, uno de nuestros clientes tiene aproximadamente 17 000 tiendas de conveniencia y otro opera más de 20 000 estaciones de carga. Esta escala significó que tuvimos que construir nuestras herramientas de un modo muy diferente a como lo haríamos si manejáramos unos pocos grupos.
  2. Implementación sin intervención : cualquiera debería poder implementar un nuevo sitio sin necesidad de tener muchos conocimientos de hardware, software o Kubernetes. El sitio de borde necesitaba comportarse como una caja negra que se enciende, llama a casa y se conecta a Internet.
  3. Gestión de flotas : gestión simplificada de miles de sitios y cargas de trabajo sin necesidad de tratarlos individualmente. Cualquier sitio puede quedar fuera de línea o no estar disponible en el momento de un cambio o actualización solicitados. Como resultado, los sitios tienen que buscar actualizaciones cuando están en línea.
  4. Tolerancia a fallas : los sitios de borde deben estar operativos incluso después de una falla de cualquier componente. Todo debe administrarse de forma remota y proporcionar funciones como restablecimiento de fábrica o reconstrucción del sitio en caso de falla. Tuvimos que asumir que no hay acceso físico al sitio.

Principios de diseño (¡No Kubectl!) ¡Sin Ansible! ¡Sin paquetes!)

Considerando nuestros requisitos y desafíos de operar un sistema altamente distribuido, decidimos establecer varios principios que necesitábamos que nuestro equipo de SRE siguiera para reducir los problemas posteriores:

  1. Definición declarativa : todo el sistema debe describirse de forma declarativa, ya que esto nos permite crear un modelo de abstracción fácil y realizar la validación contra el modelo.
     
  2. Gestión del ciclo de vida inmutable : en el pasado, trabajábamos en grandes instalaciones de nube privada utilizando herramientas LCM mutables como Ansible, Salt o Puppet. Esta vez, queríamos mantener el sistema operativo base muy simple e intentar enviar todo como un contenedor sin administración de paquetes ni necesidad de herramientas de administración de configuración.
     
  3. GitOps : proporciona un modelo operativo estándar para administrar clústeres de Kubernetes. También nos ayuda a obtener aprobaciones, auditorías y flujos de trabajo de cambios de manera inmediata sin necesidad de crear un sistema de gestión de flujo de trabajo adicional. Por lo tanto decidimos que todo debe pasar por Git.
     
  4. No kubectl : este fue uno de los principios más importantes, ya que a nadie se le permite el acceso directo a los clústeres de aplicação . Como resultado, eliminamos la capacidad de ejecutar kubectl dentro de clústeres de borde individuales o usar scripts que se ejecutan desde lugares centrales, incluidos los sistemas de CD centralizados. Los sistemas de CD centralizados con un método push son buenos para decenas de clústeres, pero ciertamente no son adecuados para miles sin una garantía de disponibilidad de red del 100%.
     
  5. Tecnología (o herramientas) sin publicidad : nuestra experiencia pasada ha demostrado que muchas de las herramientas de código abierto más populares no están a la altura de las expectativas. Si bien evaluamos varios proyectos comunitarios para entregar infraestructura, K8 y cargas de trabajo de aplicação (como Helm, Spinnaker y Terraform), terminamos usando solo Terraform para la parte de infraestructura virtual y desarrollamos un código personalizado que describiremos en las siguientes partes de este blog.

Gestión del ciclo de vida del sitio

Como parte de la administración del ciclo de vida del sitio perimetral, tuvimos que resolver cómo aprovisionar el sistema operativo host, realizar configuraciones básicas (por ejemplo, administración de usuarios, autoridad de certificación, páginas enormes, etc.), activar K8, implementar cargas de trabajo y administrar los cambios de configuración en curso.

Una de las opciones que consideramos pero finalmente rechazamos fue utilizar KubeSpray+Ansible (para administrar el sistema operativo e implementar K8) y Helm/Spinnaker (para implementar cargas de trabajo). La razón por la que rechazamos esto fue que habría requerido que administráramos 2 o 3 herramientas de código abierto y luego realizar modificaciones significativas para cumplir con nuestros requisitos, que continuaron creciendo a medida que agregamos más funciones como escalamiento automático de clústeres de borde, soporte para módulos TPM seguros, actualizaciones diferenciales, etc.

Como nuestro objetivo era mantenerlo simple y minimizar la cantidad de componentes que se ejecutan directamente en el sistema operativo (fuera de Kubernetes), decidimos escribir un demonio Golang liviano llamado Volterra Platform Manager (VPM). Este es el único contenedor Docker systemd en el sistema operativo y actúa como una navaja suiza que realiza muchas funciones:

Ciclo de vida del host

VPM es responsable de administrar el ciclo de vida del sistema operativo host, incluida la instalación, las actualizaciones, los parches, la configuración, etc. Hay muchos aspectos que deben configurarse (por ejemplo, asignación de páginas enormes, /etc/hosts, etc.)

  1. Gestión de actualizaciones del sistema operativo : lamentablemente, el borde no se limita únicamente a Kubernetes y tenemos que gestionar la versión del kernel y del sistema operativo en general. Nuestra ventaja se basa en CoreOS (o CentOS según la necesidad del cliente) con una partición activa y pasiva. Las actualizaciones siempre se descargan a la partición pasiva cuando se programa una actualización. Un reinicio es el último paso de la actualización, donde se intercambian las particiones activas y pasivas. La única parte sensible es la estrategia de reinicio (para un clúster de varios nodos) porque no se pueden reiniciar todos los nodos al mismo tiempo. Implementamos nuestro propio bloqueo de reinicio etcd (en VPM) donde los nodos de un clúster se reinician uno por uno.
  2. Gestión de acceso de usuarios al sistema operativo : nuestra necesidad es restringir los usuarios y su acceso a ssh y a la consola de forma remota. VPM realiza todas esas operaciones, como las rotaciones de CA ssh.
  3. Dado que desarrollamos nuestra propia ruta de datos L3-L7 , eso requiere que configuremos páginas enormes de 2 M o 1 G en el sistema operativo host según el tipo de hardware (o máquina virtual en la nube).

Ciclo de vida de Kubernetes

Administración para proporcionar el ciclo de vida para el manifiesto de Kubernetes. En lugar de utilizar Helm, decidimos utilizar la biblioteca cliente-go K8s, que integramos en VPM y utilizamos varias características de esta biblioteca:

  1. Implementación optimista vs. pesimista : esta función nos permite categorizar las aplicações en las que debemos esperar hasta que estén en buen estado. Simplemente busca anotaciones en el manifiesto de K8 ves.io/deploy: positive .

    Optimista = crear recursos y no esperar el estado. Es muy similar al comando apply de Kubernetes, donde no se sabe si los pods reales se inician correctamente.

    Pesimista = esperar el estado del recurso de Kubernetes. Por ejemplo, la implementación espera hasta que todos los pods estén listos. Esto es similar al nuevo comando kubectl wait .

  2. Acciones de preactualización como pre-extracción : a veces no es posible confiar en las actualizaciones continuas de K8, especialmente cuando se envía el plano de datos de red. La razón es que se destruye la cápsula vieja y luego se retira la nueva. Sin embargo, en el caso del plano de datos, se pierde la conectividad de red. Por lo tanto, no se puede extraer la nueva imagen del contenedor y el pod nunca se iniciará. La anotación de metadatos ves.io/prepull con una lista de imágenes activa la acción de extracción antes de que se aplique el manifiesto de K8.
  3. Reintentos y reversiones en caso de fallas aplicadas. Esta es una situación muy común cuando un servidor API de K8s tiene algunas interrupciones intermitentes.

Configuración en curso

Además de las configuraciones relacionadas con los manifiestos de K8, también necesitamos configurar varios servicios de Volterra a través de sus API. Un ejemplo son las configuraciones VPN IPsec/SSL: VPM recibe la configuración de nuestro plano de control global y las programa en nodos individuales.

Restablecimiento de fábrica

Esta función nos permite restablecer una caja de forma remota a su estado original y realizar nuevamente todo el proceso de instalación y registro. Es una característica muy importante para recuperar un sitio que necesita acceso físico o a la consola.

Si bien la gestión del ciclo de vida de K8 puede parecer un gran tema de discusión para muchas personas, para nuestro equipo probablemente representa solo el 40-50 % del volumen de trabajo total.

Aprovisionamiento sin intervención

El aprovisionamiento sin intervención del sitio perimetral en cualquier ubicación (nube, local o perimetral nómada) es una funcionalidad fundamental, ya que no podemos esperar tener acceso a sitios individuales ni queremos contratar a tantos expertos en Kubernetes para instalar y administrar sitios individuales. Simplemente no se puede escalar a miles.

El siguiente diagrama (Figura 2) muestra cómo VPM está involucrado en el proceso de registro de un nuevo sitio:

administrar02
Figura 2: Flujo de aprovisionamiento sin intervención
  1. Una vez encendido, el VPM que se ejecuta en el CE (representado por el cuadro verde) presentará un token de registro a nuestro plano de control global (GC) para crear un nuevo registro. El token de registro se proporciona como parte de cloud-init de la VM en la nube y puede ser una clave ingresada por una persona durante el proceso de arranque o programada en el TPM para hardware de borde.
  2. GC recibe la solicitud con el token, lo que le permite crear un nuevo registro bajo un inquilino (codificado en el token). El operador del cliente puede ver inmediatamente el nuevo sitio perimetral en el mapa y aprobarlo con la posibilidad de ingresar el nombre y otros parámetros de configuración.
  3. El controlador VP dentro de GC genera configuración (por ejemplo, decide quién es el maestro, el subordinado, etc. de K8s) y certificados para etcd, K8s y VPM.
  4. VPM comienza a arrancar el sitio, incluida la descarga de imágenes de Docker, la configuración de hugepages, la instalación del clúster K8 y el inicio de los servicios del plano de control de Volterra.
  5. VPM configura túneles redundantes (IPSec/SSL VPN) a los dos sitios de borde regional más cercanos que se utilizarán para el tráfico de datos y la interconectividad entre sitios y la red pública.

Como puede ver, todo el proceso está completamente automatizado y el usuario no necesita saber nada sobre la configuración detallada ni ejecutar ningún paso manual. Se necesitan aproximadamente 5 minutos para poner todo el dispositivo en estado en línea y listo para atender aplicaciones y solicitudes de los clientes.

Actualizaciones de software de infraestructura

La actualización es una de las cosas más complicadas que tuvimos que resolver. Definamos qué se está actualizando en los sitios de borde: 

  • Actualizaciones del sistema operativo : esto cubre el kernel y todos los paquetes del sistema. Cualquiera que haya utilizado una distribución estándar del sistema operativo Linux conoce el dolor que supone actualizar entre versiones menores (por ejemplo, pasar de Ubuntu 16.04.x a 16.04.y) y el dolor mucho mayor que supone actualizar entre versiones principales (por ejemplo, pasar de Ubuntu 16.04 a 18.04). En el caso de miles de sitios, las actualizaciones deben ser deterministas y no pueden comportarse de manera diferente en los distintos sitios. Por lo tanto, elegimos CoreOS y CentOS Atomic con la capacidad de seguir actualizaciones A/B con 2 particiones y un sistema de archivos de solo lectura en las rutas. Esto nos da la posibilidad de revertir inmediatamente cambiando el orden de arranque de las particiones y mantener el sistema operativo consistente sin mantener los paquetes del sistema operativo. Sin embargo, ya no podemos actualizar componentes individuales en el sistema, por ejemplo el servidor openssh, simplemente instalando un nuevo paquete. Los cambios en los componentes individuales deben publicarse como una nueva versión inmutable del sistema operativo.
     
  • Actualizaciones de software : esto cubre los servicios de control VPM, etcd, Kubernetes y Volterra que se ejecutan como cargas de trabajo de K8s. Como ya mencioné, nuestro objetivo es tener todo dentro de K8s funcionando como contenedor systemd. Afortunadamente, pudimos convertir todo como cargas de trabajo de K8 excepto 3 servicios: VPM, etcd y kubelet.

Hay dos métodos conocidos que podrían usarse para enviar actualizaciones a sitios perimetrales: 

  1. Basado en push : un método de push generalmente lo realiza una herramienta de CD (entrega continua) centralizada como Spinnaker, Jenkins o CM basado en Ansible. En este caso, una herramienta central necesitaría tener acceso al sitio o clúster de destino y debería estar disponible para realizar la acción.
  2. Basado en extracción : un método basado en extracción obtiene información de actualización de forma independiente sin ningún mecanismo de entrega centralizado. Se escala mejor y también elimina la necesidad de almacenar las credenciales de todos los sitios en un solo lugar.

Nuestro objetivo con la actualización era maximizar la simplicidad y la confiabilidad, similar a las actualizaciones de teléfonos celulares estándar. Además, hay otras consideraciones que la estrategia de actualización debía satisfacer: el contexto de actualización podía ser únicamente con el operador del sitio, o el dispositivo podía estar fuera de línea o no disponible durante algún tiempo debido a problemas de conectividad, etc. Estos requisitos podrían satisfacerse más fácilmente con el método pull y por eso decidimos adoptarlo para satisfacer nuestras necesidades.

GitOps

Además, elegimos GitOps porque nos permitió proporcionar un modelo operativo estándar para gestionar clústeres de Kubernetes, flujos de trabajo y cambios de auditoría a nuestro equipo de SRE.

Para resolver los problemas de escalabilidad de miles de sitios, ideamos la arquitectura para SRE que se muestra en la Figura 3:

administrar03
Figura 3: Flujo de GitOps

Primero, quiero enfatizar que no usamos Git solo para almacenar estados o manifiestos. La razón es que nuestra plataforma no solo tiene que gestionar los manifiestos de K8, sino también las configuraciones de API en curso, versiones de K8, etc. En nuestro caso, los manifiestos de K8 representan aproximadamente el 60% de toda la configuración declarativa. Por este motivo tuvimos que idear nuestra propia abstracción DSL, que se almacena en git. Además, dado que Git no proporciona una API ni ninguna capacidad de fusión de parámetros, tuvimos que desarrollar daemons Golang adicionales para SRE: API de configuración, ejecutor y controlador VP.

Repasemos el flujo de trabajo de lanzamiento de una nueva versión de software en el borde del cliente utilizando nuestra plataforma SaaS: 

  1. El operador decide lanzar una nueva versión y abre una solicitud de fusión (MR) contra el modelo git
     
  2. Una vez que se aprueba y se fusiona este MR, CI activa la acción para cargar una configuración de modelo git en nuestro demonio SRE Config-API. Este demonio tiene varias API para la fusión de parámetros, configuración de DNS interna, etc.
     
  3. Config-API es supervisado por el demonio Executor; inmediatamente después de que se cargan los cambios de git, comienza a renderizar los manifiestos finales de K8 con la versión en la anotación. Estos manifiestos luego se cargan en el almacenamiento de Artifact (similar a S3) en la ruta ce01-site// .yml
     
  4. Una vez que se renderiza la nueva versión y se carga en el almacenamiento de artefactos, Executor produce un nuevo estado con una versión disponible para la API del cliente; esto es muy similar a la nueva versión disponible en los teléfonos celulares.
     
  5. El cliente (u operador) puede programar una actualización de su sitio a la última versión y esta información se pasa a VP-Controller. VP-Controller es el demonio responsable de la gestión del sitio, incluido el aprovisionamiento, el desmantelamiento o la migración a una ubicación diferente. Esto ya se explicó parcialmente en el aprovisionamiento sin intervención y es responsable de actualizar los sitios de borde a través de la API mTLS.
     
  6. El último paso del diagrama ocurre en el sitio de borde: una vez que la conexión VPN IPSec/SSL está activa, VP-Controller notifica al VPM en el borde para descargar actualizaciones con la nueva versión; sin embargo, si la conectividad se interrumpe o tiene problemas intermitentes, el VPM sondea en busca de una actualización cada 5 minutos.
     
  7. Se obtienen nuevos manifiestos y configuraciones de K8s y se implementan en K8s. Usando la función de implementación pesimista descrita en la sección anterior, VPM espera hasta que todos los pods estén listos
     
  8. Como último paso, VPM envía el estado de la actualización al controlador VP y se envía como estado a la API del cliente.

Puedes ver una demostración del flujo de trabajo completo aquí:

Lecciones aprendidas de las pruebas en 3000 sitios perimetrales

En secciones anteriores, describimos cómo se utilizan nuestras herramientas para implementar y administrar el ciclo de vida de los sitios perimetrales. Para validar nuestro diseño, decidimos construir un entorno a gran escala con tres mil sitios de borde del cliente (como se muestra en la Figura 4)

administrar04
Figura 4: 3000 sitios de borde del cliente

Utilizamos Terraform para aprovisionar 3000 máquinas virtuales en AWS, Azure, Google y nuestra propia nube local para simular la escala. Todas esas máquinas virtuales eran CE independientes (sitios de borde del cliente) que establecían túneles redundantes hacia nuestros sitios de borde regionales (también conocidos como PoP).

La siguiente captura de pantalla es de nuestro panel SRE y muestra números de borde en ubicaciones representadas por el tamaño del círculo. Al momento de tomar la captura de pantalla teníamos alrededor de 2711 sitios de borde saludables y 356 no saludables.

administrar05
Figura 5–3000 Implementación del sitio de borde del cliente

Conclusiones clave:Operaciones

Como parte del escalamiento, encontramos algunos problemas en la configuración y el aspecto operativo que nos obligaron a realizar modificaciones en nuestros daemons de software. Además, nos topamos con muchos problemas con un proveedor de nube que llevaron a la apertura de múltiples tickets de soporte; por ejemplo, latencia de respuesta de API, imposibilidad de obtener más de 500 máquinas virtuales en una sola región, etc. 

  1. Optimizar VP-Controller : inicialmente, procesamos el registro en serie y cada uno tomó alrededor de dos minutos, ya que necesitábamos generar varios certificados para etcd, kubernetes y VPM. Optimizamos esta vez mediante claves pregeneradas utilizando mayor entropía y paralelización con una mayor cantidad de trabajadores. Esto nos permitió procesar el registro para 100 sitios en menos de 20 segundos. Logramos brindar servicio a los 3000 sitios perimetrales consumiendo solo alrededor de una vCPU y 2 GB de RAM en nuestro VP-Controller.
     
  2. Optimizar la entrega de imágenes de Docker : cuando comenzamos a escalar, nos dimos cuenta de que la cantidad de datos transmitidos para los sitios perimetrales es enorme. Cada borde descargó alrededor de 600 MB (multiplicado por 3000), por lo que se transmitieron 1,8 TB de datos totales. Además, estábamos reconstruyendo sitios de borde varias veces durante nuestras pruebas, por lo que ese número en realidad sería mucho mayor. Como resultado, tuvimos que optimizar el tamaño de nuestras imágenes Docker y crear imágenes ISO y en la nube con imágenes Docker extraídas previamente para reducir la descarga. Si bien aún utilizamos un servicio de registro de contenedores en la nube pública, estamos trabajando activamente en un diseño para distribuir nuestro registro de contenedores a través de RE (PoP) y realizar actualizaciones incrementales (diferencias binarias).
     
  3. Optimice las operaciones de la base de datos de control global : todos nuestros servicios de control de Volterra se basan en el marco de servicio Golang que utiliza ETCD como base de datos. Cada sitio se representa como un objeto de configuración. Cada objeto de configuración del sitio tiene algunos StatusObjects, como actualización de software, información de hardware o estado de ipsec. Estos StatusObjects son generados por varios componentes de la plataforma y todos están referenciados en una API de configuración global. Al alcanzar los 3000 sitios, tuvimos que realizar ciertas optimizaciones en nuestro esquema de objetos. Por ejemplo, limitar la cantidad de tipos de StatusObjects aceptados por la API de configuración global o decidimos moverlos a una instancia ETCD dedicada para reducir el riesgo de sobrecargar la base de datos de objetos de configuración. Esto nos permite brindar una mejor disponibilidad y tiempo de respuesta para la base de datos de configuración y también nos permite reconstruir la base de datos de estado en caso de fallas. Otro ejemplo de optimización fue dejar de realizar operaciones innecesarias de listas de objetos del sitio en todos los inquilinos o introducir índices secundarios para reducir la carga en la base de datos.

Conclusiones clave:observabilidad

La observabilidad en un sistema distribuido planteó un conjunto mucho mayor de desafíos a medida que escalamos el sistema.

Inicialmente, para las métricas comenzamos con la federación de Prometheus: Prometheus central en control global que federa Promethei en bordes regionales (RE), que extrae sus métricas de servicio y federa las métricas de sus CE conectados. Prometheus de nivel superior evaluó las alertas y sirvió como fuente de métricas para análisis posteriores. Llegamos rápidamente a los límites de este enfoque (alrededor de 1000 CE) y tratamos de minimizar el impacto del creciente número de CE. Comenzamos a generar series precalculadas para histogramas y otras métricas de alta cardinalidad. Esto nos ahorró uno o dos días y luego tuvimos que emplear listas blancas para las métricas. Al final, pudimos reducir el número de métricas de series de tiempo de alrededor de 60.000 a 2000 para cada sitio de CE.

Finalmente, después de seguir escalando más allá de los 3000 sitios CE y de funcionar durante muchos días en producción, quedó claro que esto no era escalable y tuvimos que repensar nuestra infraestructura de monitoreo. Decidimos eliminar el Prometheus de nivel superior (bajo control global) y dividir el Prometheus en cada RE en dos instancias separadas. Uno es responsable de recopilar métricas de servicios locales y el segundo de federar métricas de CE. Ambos generan alertas y envían métricas al almacenamiento central en Cortex. Cortex se utiliza como fuente de análisis y visualización y no es parte del flujo de alerta de monitoreo central. Probamos varias soluciones de métricas centralizadas, a saber, Thanos y M3db, y descubrimos que Cortex era la que mejor se adaptaba a nuestras necesidades.

administrar06
Figura 6 — Arquitectura de recopilación de métricas

La siguiente captura de pantalla (Figura 7) muestra el consumo de memoria al raspar prometheus-cef en el momento de 3000 puntos finales. Lo interesante son los 29,7 GB de RAM consumidos, que en realidad no es tanta dada la escala del sistema. Se puede optimizar aún más dividiendo el raspado en varios de ellos o moviendo la escritura remota a Cortex directamente al borde mismo.

administrar07
Figura 7 — Utilización de recursos en el sitio de energía renovable

La siguiente captura de pantalla (Figura 8) muestra la cantidad de memoria y recursos de CPU que necesitábamos para los ingestadores Cortex (máximo 19 GB de RAM) y distribuidores a esta escala. La mayor ventaja de Cortex es el escalamiento horizontal, que nos permite agregar más réplicas en comparación con Prometheus, donde el escalamiento debe realizarse verticalmente.

administrar08
Figura 8 — Utilización de recursos en el control global

Para registrar la infraestructura en CE y RE, utilizamos servicios de Fluentbit por nodo que recopilan los eventos de registro del servicio y del sistema y los reenvían a Fluentd en el RE conectado. Fluentd reenvía los datos al ElasticSearch presente en el RE. Elastalert evalúa los datos de ElasticSearch y se establecen reglas para crear alertas de Alertmanager. Estamos utilizando nuestra integración personalizada de Elastalert con Alertmananger para producir alertas con las mismas etiquetas que produce Prometheus.

Los puntos clave de nuestro recorrido de monitoreo:

  • Uso de los nuevos filtros de federación de Prometheus para descartar métricas y etiquetas no utilizadas

    - Inicialmente teníamos alrededor de 50.000 series de tiempo por CE con un promedio de 15 etiquetas

    - Lo optimizamos a 2000 por CE en promedio

    Listas while simples para nombres de métricas y listas negras para nombres de etiquetas

  • Pasar de la federación global Prometheus al clúster Cortex

    - Prometheus centralizado eliminó todos los Prometheus de RE y CE

    - En el año 1000 d. C. se volvió insostenible gestionar la cantidad de métricas.

    - Actualmente tenemos Prometheus en cada RE (federando a Promethei de los CE conectados) con RW a Cortex

  • Clústeres y registros de Elasticsearch

    - Arquitectura de registro descentralizada

    - Fluentbit como recopilador en cada nodo reenvía registros a Fluentd (agregador) en RE

    - ElasticSearch se implementa en cada RE mediante la búsqueda de clúster remoto para consultar registros desde una única instancia de Kibana

resumen

Espero que este blog le brinde una idea de todo lo que debe tenerse en cuenta para administrar miles de sitios y clústeres de borde implementados en todo el mundo. Aunque hemos podido cumplir y validar la mayoría de nuestros requisitos de diseño iniciales, aún nos quedan muchas mejoras por delante…