EKS Fargate: Clúster híbrido de Kubernetes en AWS

EKS Fargate: Clúster híbrido de Kubernetes en AWS

Cluster de Kubernetes EKS Fargate AWS: Aprende cómo crear un clúster de Kubernetes que también pueda utilizar todo el poder de la computación sin servidor usando AWS Fargate

EKS Fargate: Clúster híbrido de Kubernetes AWS
Foto de Drew Graham en Unsplash

Sabemos que hay varios movimientos y paradigmas que nos están empujando fuertemente a cambiar nuestras arquitecturas tratando de aprovechar mucho más los servicios gestionados y cuidando el nivel operativo para que podamos centrarnos en lo que realmente es importante para nuestro propio negocio: crear aplicaciones y entregar valor a través de ellas.

AWS de Amazon ha sido un socio crítico durante ese viaje, especialmente en el mundo de los contenedores. Con el lanzamiento de EKS hace algún tiempo, pudieron proporcionar un servicio de Kubernetes gestionado que todos pueden usar, pero también introduciendo la solución CaaS Fargate también nos da el poder de ejecutar una carga de trabajo de contenedor de manera sin servidor, sin necesidad de preocuparse por nada más.

Pero podrías estar pensando si esos servicios pueden trabajar juntos. Y la respuesta corta es sí. Pero aún más importante que eso, estamos viendo que también pueden trabajar en un modo mixto:

Así que puedes tener un clúster EKS que tiene algunos nodos que son servicios Fargate y algunos nodos que son máquinas EC2 normales para cargas de trabajo que están trabajando de manera con estado o se ajustan mejor a un enfoque tradicional de EC2. Y todo funciona bajo las mismas reglas y es gestionado por el mismo clúster EKS.

Entonces, eso suena increíble, pero, ¿cómo podemos hacer eso? Vamos a ver.

eksctl

Para llegar a ese punto hay una herramienta que necesitamos presentar primero, y esa herramienta se llama eksctl y es una utilidad de línea de comandos que nos ayuda a realizar cualquier acción para interactuar con el servicio EKS y simplifica mucho el trabajo a realizar y también permite automatizar la mayoría de las tareas en un modo no requerido por humanos. Así que, lo primero que necesitamos es tener eksctl listo en nuestras plataformas. Veamos cómo podemos obtener eso.

Aquí tenemos todos los detalles de Amazon sobre cómo instalar eksctl en diferentes plataformas, no importa si estás usando Windows, Linux o MacOS X:

Después de hacer eso, podemos verificar que hemos instalado el software eksctl ejecutando el comando:

eksctl version

Y deberíamos obtener una salida similar a esta:

EKS Fargate: Clúster híbrido de Kubernetes en AWS
Salida del comando eksctl version

Así que después de hacer eso, podemos ver que tenemos acceso a todo el poder detrás del servicio EKS simplemente escribiendo estos simples comandos en nuestra ventana de consola.

Creando el Clúster Híbrido EKS

Ahora, vamos a crear un entorno mixto con algunas máquinas EC2 y habilitar el soporte Fargate para EKS. Para hacer eso, comenzaremos con el siguiente comando:

eksctl create cluster --version=1.15 --name=cluster-test-hybrid --region=eu-west-1 --max-pods-per-node=1000 --fargate
[ℹ]  eksctl version 0.26.0
[ℹ]  usando la región eu-west-1
[ℹ]  configurando zonas de disponibilidad a [eu-west-1c eu-west-1a eu-west-1b]
[ℹ]  subredes para eu-west-1c - pública:192.168.0.0/19 privada:192.168.96.0/19
[ℹ]  subredes para eu-west-1a - pública:192.168.32.0/19 privada:192.168.128.0/19
[ℹ]  subredes para eu-west-1b - pública:192.168.64.0/19 privada:192.168.160.0/19
[ℹ]  usando la versión de Kubernetes 1.15
[ℹ]  creando el clúster EKS "cluster-test-hybrid" en la región "eu-west-1" con perfil Fargate
[ℹ]  si encuentras algún problema, revisa la consola de CloudFormation o intenta 'eksctl utils describe-stacks --region=eu-west-1 --cluster=cluster-test-hybrid'
[ℹ]  el registro de CloudWatch no estará habilitado para el clúster "cluster-test-hybrid" en "eu-west-1"
[ℹ]  puedes habilitarlo con 'eksctl utils update-cluster-logging --region=eu-west-1 --cluster=cluster-test-hybrid'
[ℹ]  el acceso al punto final de la API de Kubernetes usará el valor predeterminado de {publicAccess=true, privateAccess=false} para el clúster "cluster-test-hybrid" en "eu-west-1"
[ℹ]  2 tareas secuenciales: { crear el plano de control del clúster "cluster-test-hybrid", crear perfiles fargate }
[ℹ]  construyendo la pila del clúster "eksctl-cluster-test-hybrid-cluster"
[ℹ]  desplegando la pila "eksctl-cluster-test-hybrid-cluster"
[ℹ]  creando el perfil Fargate "fp-default" en el clúster EKS "cluster-test-hybrid"
[ℹ]  creado el perfil Fargate "fp-default" en el clúster EKS "cluster-test-hybrid"
[ℹ]  "coredns" ahora es programable en Fargate
[ℹ]  "coredns" ahora está programado en Fargate
[ℹ]  los pods de "coredns" ahora están programados en Fargate
[ℹ]  esperando la disponibilidad del plano de control...
[✔]  guardado kubeconfig como "C:\Users\avazquez/.kube/config"
[ℹ]  sin tareas
[✔]  todos los recursos del clúster EKS para "cluster-test-hybrid" han sido creados
[ℹ]  el comando kubectl debería funcionar con "C:\Users\avazquez/.kube/config", intenta 'kubectl get nodes'
[✔]  el clúster EKS "cluster-test-hybrid" en la región "eu-west-1" está listo

Este comando configurará el clúster EKS habilitando el soporte Fargate.

NOTA: Lo primero que deberíamos notar es que el soporte Fargate para EKS aún no está disponible en todas las regiones de AWS. Por lo tanto, dependiendo de la región que estés utilizando, podrías obtener un error. En este momento, esto solo está habilitado en US East (N. Virginia), US East (Ohio), US West (Oregon), Europa (Irlanda), Europa (Frankfurt), Asia Pacífico (Singapur), Asia Pacífico (Sídney), Asia Pacífico (Tokio) según la información de los Anuncios de AWS: https://aws.amazon.com/about-aws/whats-new/2020/04/eks-adds-fargate-support-in-frankfurt-oregon-singapore-and-sydney-aws-regions/

Así que, ahora, deberíamos agregar a ese clúster un Grupo de Nodos. Un Grupo de Nodos es un conjunto de instancias EC2 que van a ser gestionadas como parte de él. Y para hacer eso, utilizaremos el siguiente comando:

eksctl create nodegroup --cluster cluster-test-hybrid --managed
[ℹ]  eksctl version 0.26.0
[ℹ]  usando la región eu-west-1
[ℹ]  se usará la versión 1.15 para el/los nuevo(s) grupo(s) de nodos basado en la versión del plano de control
[ℹ]  el grupo de nodos "ng-1262d9c0" está presente en la configuración dada, pero falta en el clúster
[ℹ]  1 grupo de nodos (ng-1262d9c0) fue incluido (basado en las reglas de inclusión/exclusión)
[ℹ]  se creará una pila de CloudFormation para cada uno de los 1 grupos de nodos gestionados en el clúster "cluster-test-hybrid"
[ℹ]  2 tareas secuenciales: { corregir la compatibilidad del clúster, 1 tarea: { 1 tarea: { crear grupo de nodos gestionado "ng-1262d9c0" } } }
[ℹ]  comprobando la pila del clúster para recursos faltantes
[ℹ]  la pila del clúster tiene todos los recursos requeridos
[ℹ]  construyendo la pila del grupo de nodos gestionado "eksctl-cluster-test-hybrid-nodegroup-ng-1262d9c0"
[ℹ]  desplegando la pila "eksctl-cluster-test-hybrid-nodegroup-ng-1262d9c0"
[ℹ]  sin tareas
[✔]  creado 0 grupo(s) de nodos en el clúster "cluster-test-hybrid"
[ℹ]  el grupo de nodos "ng-1262d9c0" tiene 2 nodo(s)
[ℹ]  el nodo "ip-192-168-69-215.eu-west-1.compute.internal" está listo
[ℹ]  el nodo "ip-192-168-9-111.eu-west-1.compute.internal" está listo
[ℹ]  esperando que al menos 2 nodo(s) estén listos en "ng-1262d9c0"
[ℹ]  el grupo de nodos "ng-1262d9c0" tiene 2 nodo(s)
[ℹ]  el nodo "ip-192-168-69-215.eu-west-1.compute.internal" está listo
[ℹ]  el nodo "ip-192-168-9-111.eu-west-1.compute.internal" está listo
[✔]  creado 1 grupo(s) de nodos gestionado(s) en el clúster "cluster-test-hybrid"
[ℹ]  comprobando la configuración del grupo de seguridad para todos los grupos de nodos
[ℹ]  todos los grupos de nodos tienen la configuración actualizada

Así que ahora deberíamos poder usar kubectl para gestionar este nuevo clúster. Si no tienes instalado kubectl o no has oído hablar de él. Esta es la herramienta de línea de comandos que nos permite gestionar tu Clúster de Kubernetes y puedes instalarla basándote en la documentación mostrada aquí:

Así que, ahora, deberíamos empezar a echar un vistazo a la infraestructura que tenemos. Así que si escribimos el siguiente comando para ver los nodos a nuestra disposición:

kubectl get nodes

Vemos una salida similar a esta:

NAME                                                    STATUS   ROLES    AGE   VERSION
fargate-ip-192-168-102-22.eu-west-1.compute.internal    Ready    <none>   10m   v1.15.10-eks-094994
fargate-ip-192-168-112-125.eu-west-1.compute.internal   Ready    <none>   10m   v1.15.10-eks-094994
ip-192-168-69-215.eu-west-1.compute.internal            Ready    <none>   85s   v1.15.11-eks-bf8eea
ip-192-168-9-111.eu-west-1.compute.internal             Ready    <none>   87s   v1.15.11-eks-bf8eea

Como puedes ver, tenemos 4 “nodos”, dos que comienzan con el nombre fargate que son nodos fargate y dos que solo comienzan con ip-… y esos son las instancias EC2 tradicionales. Y después de ese momento, eso es todo, tenemos nuestro entorno mixto listo para usar.

Podemos verificar el mismo clúster usando la página de AWS EKS para ver esa configuración con más detalle. Si ingresamos en la página de EKS para este clúster, vemos en la pestaña de Computación la siguiente información:

EKS Fargate: Clúster híbrido de Kubernetes en AWS

Vemos bajo Grupos de Nodos los datos sobre las máquinas EC2 que son gestionadas como parte de este clúster y como puedes ver vimos 2 como la Capacidad Deseada y por eso tenemos 2 instancias EC2 en nuestro clúster. Y con respecto al perfil Fargate, vemos los espacios de nombres configurados como predeterminado y kube-system y eso significa que todos los despliegues a esos espacios de nombres se van a desplegar usando Tareas Fargate.

Resumen

En los siguientes artículos de esta serie, veremos cómo progresar en nuestro clúster Híbrido, desplegar cargas de trabajo, escalarlo en función de la demanda que estamos recibiendo, habilitar la integración con otros servicios como AWS CloudWatch, y así sucesivamente. Así que, mantente atento, y no olvides seguir mis artículos para no perderte ninguna nueva actualización tan pronto como esté disponible para ti.

Serie Híbrida EKS: Cambiando espacios de nombres de implementación sin servidor

Serie Híbrida EKS: Cambiando espacios de nombres de implementación sin servidor
Serie Híbrida EKS: Cambiando espacios de nombres de implementación sin servidor
Foto de Tanner Boriack en Unsplash

En el post anterior de esta serie sobre cómo configurar un clúster EKS híbrido utilizando tanto máquinas EC2 tradicionales como opciones sin servidor usando Fargate, pudimos crear el clúster EKS con ambas modalidades de implementación disponibles. ¡Si aún no lo has visto, hazlo ahora!

https://medium.com/@alexandrev/hybrid-aws-kubernetes-cluster-using-eks-ec2-and-fargate-13198d864baa

En ese punto, tenemos un clúster vacío con todo listo para desplegar nuevas cargas de trabajo, pero aún necesitamos configurar algunas cosas antes de realizar el despliegue. Lo primero es decidir qué cargas de trabajo se van a desplegar usando la opción sin servidor y cuáles usarán la opción tradicional de EC2.

Por defecto, todas las cargas de trabajo desplegadas en los espacios de nombres default y kube-system como puedes ver en la imagen a continuación desde la Consola de AWS:

Serie Híbrida EKS: Cambiando espacios de nombres de implementación sin servidor

Eso significa que todas las cargas de trabajo del espacio de nombres default y del espacio de nombres kube-system se desplegarán de manera sin servidor. Si eso es lo que deseas, perfecto. Pero a veces te gustaría comenzar con un conjunto delimitado de espacios de nombres donde te gustaría usar la opción sin servidor y confiar en el despliegue tradicional.

Podemos verificar esa misma información usando eksctl y para hacerlo necesitamos escribir el siguiente comando:

eksctl get fargateprofile --cluster cluster-test-hybrid -o yaml

La salida de ese comando debería ser algo similar a la información que podemos ver en la Consola de AWS:

- name: fp-default
 podExecutionRoleARN: arn:aws:iam::938784100097:role/eksctl-cluster-test-hybrid-FargatePodExecutionRole-1S12LVS5S2L62
 selectors:
 — namespace: default
 — namespace: kube-system
 subnets:
 — subnet-022f9cc3fd1180bb8
 — subnet-0aaecd5250ebcb02e
 — subnet-01b0bae6fa66ecd31

NOTA: Si no recuerdas el nombre de tu clúster solo necesitas escribir el comando eksctl get clusters

Entonces, esto es lo que vamos a hacer y para hacerlo lo primero que necesitamos hacer es crear un nuevo espacio de nombres llamado “serverless” que va a contener nuestro despliegue sin servidor y para hacerlo usamos un comando kubectl de la siguiente manera:

kubectl create namespace serverless

Y ahora, solo necesitamos crear un nuevo perfil de fargate que va a reemplazar el que tenemos en este momento y para hacerlo necesitamos usar nuevamente eksctl para manejar ese trabajo:

eksctl create fargateprofile --cluster cluster-test-hybrid --name fp-serverless-profile --namespace serverless

NOTA: También podemos usar no solo el espacio de nombres para limitar el alcance de nuestro despliegue sin servidor sino también etiquetas, por lo que podemos tener en el mismo espacio de nombres cargas de trabajo que se despliegan usando el despliegue tradicional y otras usando la modalidad sin servidor. Eso nos dará todas las posibilidades para diseñar tu clúster como desees. Para hacerlo, añadiremos el argumento labels en un formato clave=valor.

Y obtendremos una salida similar a esta:

[ℹ] creando perfil de Fargate “fp-serverless-profile” en el clúster EKS “cluster-test-hybrid”
[ℹ] creado perfil de Fargate “fp-serverless-profile” en el clúster EKS “cluster-test-hybrid”

Si ahora verificamos el número de perfiles que tenemos disponibles deberíamos obtener dos perfiles manejando tres espacios de nombres (los que son gestionados por el perfil por defecto — default y kube-system — y el que — serverless — es gestionado por el que acabamos de crear ahora)

Solo usaremos el siguiente comando para eliminar el perfil por defecto:

eksctl delete fargateprofile --cluster cluster-test-hybrid fp-default

Y la salida de ese comando debería ser similar a esta:

[ℹ] eliminado perfil de Fargate “fp-default” en el clúster EKS “cluster-test-hybrid”

Y después de eso, ahora tenemos listo nuestro clúster con un alcance limitado para despliegues sin servidor. En el próximo post de la serie, solo desplegaremos cargas de trabajo en ambas modalidades para ver la diferencia entre ellas. Así que, no te pierdas las actualizaciones sobre esta serie asegurándote de seguir mis publicaciones, y si te gustó el artículo, o tienes algunas dudas o comentarios, por favor deja tus comentarios usando los comentarios a continuación!

¿Cómo analizar y mejorar el tamaño de tus imágenes de Docker?

¿Cómo analizar y mejorar el tamaño de tus imágenes de Docker?

Descubre cómo puedes mejorar el tamaño de tus imágenes de Docker para una mejor experiencia y ahorros dentro de tu organización.

¿Cómo analizar y mejorar el tamaño de tus imágenes de Docker?
Foto de Charles Deluvio en Unsplash

La contenedorización es la nueva normalidad. Todos somos conscientes de eso. Todas las nuevas versiones del software corporativo y todos los proyectos de código abierto están incluyendo las opciones para usar una imagen de Docker para ejecutar su software.

Probablemente ya hayas estado haciendo tus pruebas o incluso ejecutando cargas de trabajo en producción basadas en imágenes de Docker que has construido tú mismo. Si ese es el caso, probablemente conozcas uno de los grandes desafíos cuando realizas este tipo de tarea: ¿Cómo optimizar el tamaño de la imagen que generas?

Una de las principales razones por las que la imagen de Docker puede ser tan grande es porque se construyen siguiendo un concepto de capas. Y eso significa que cada una de las imágenes se crea como la adición de capas, cada una asociada con los diferentes comandos que tienes en tu Dockerfile.

¿Cómo analizar y mejorar el tamaño de tus imágenes de Docker?
Explicación gráfica de cómo se compone una imagen de Docker.

Usa dive para analizar el tamaño de tus imágenes

dive es un proyecto de código abierto que proporciona una vista detallada de la composición de tus imágenes de Docker. Funciona como una aplicación de interfaz de línea de comandos que tiene una gran vista del contenido de las capas, como puedes ver en la imagen a continuación:

¿Cómo analizar y mejorar el tamaño de tus imágenes de Docker?
Ejecución de Dive de una imagen de BusinessWorks Container Edition 

La herramienta sigue una interfaz n-curses (si eres lo suficientemente mayor para recordar cómo eran las herramientas antes de que las interfaces gráficas de usuario fueran algo; debería parecerte familiar) y tiene estas características principales:

  • Esta herramienta proporcionará la lista de capas en la parte superior izquierda de la pantalla y el tamaño asociado con cada una de ellas.
  • Proporciona estadísticas generales sobre la eficiencia de la imagen (un valor porcentual), una vista potencial del tamaño desperdiciado y el tamaño total de la imagen.
  • Para cada una de las capas seleccionadas, obtienes una vista en el sistema de archivos para esta vista con los datos del tamaño de cada carpeta.
  • También obtén una vista de los elementos más grandes y el número de replicaciones de estos objetos.

Ahora, tienes una herramienta que te ayudará primero a saber cómo se construye tu imagen y obtener datos de rendimiento de cada uno de los ajustes que haces para mejorar ese tamaño. Así que, comencemos con los trucos.

1.- ¡Limpia tu imagen!

Este primero es bastante obvio, pero eso no significa que no sea importante. Por lo general, cuando creas una imagen de Docker, sigues el mismo patrón:

  • Declaras una imagen base para aprovechar.
  • Agregas recursos para hacer algún trabajo.
  • Haces algún trabajo.

Por lo general, olvidamos un paso adicional: ¡Limpiar los recursos agregados cuando ya no son necesarios! Por lo tanto, es importante asegurarse de eliminar cada uno de los archivos que ya no necesitamos.

Esto también se aplica a otros componentes como la caché de apt cuando estamos instalando un nuevo paquete que necesitamos o cualquier carpeta temporal que necesitemos para realizar una instalación o algún trabajo para construir la imagen.

2.- Ten cuidado con cómo creas tu Dockerfile

Como ya mencionamos, cada uno de los comandos que declaramos en nuestro Dockerfile genera una nueva capa. Por lo tanto, es importante tener mucho cuidado con las líneas que tenemos en el Dockerfile. Incluso si esto es un compromiso con respecto a la legibilidad del Dockerfile, es importante intentar fusionar comandos en la misma primitiva RUN para asegurarnos de no estar creando capas adicionales.

¿Cómo analizar y mejorar el tamaño de tus imágenes de Docker?
Ejemplo de un Dockerfile con comandos fusionados

También puedes usar linters de Docker como Hadolint que te ayudarán con esto y otros anti-patrones que deberías evitar cuando estás creando un Dockerfile.

3.- Opta por docker build — squash

Las últimas versiones del motor de Docker proporcionan una nueva opción cuando construyes tus imágenes para crear con el tamaño minimizado squashing de las capas intermedias que pueden ser creadas como parte del proceso de creación del Dockerfile.

Eso funciona, proporcionando una nueva bandera cuando estás haciendo la construcción de tu imagen. Así que, en lugar de hacer esto:

docker build -t <your-image-name>:<tag> <Dockerfile location>

Deberías usar una bandera adicional:

docker build --squash -t <your-image-name>:<tag> <Dockerfile location>

Para poder usar esta opción, debes habilitar las características experimentales en tu Docker Engine. Para hacer eso, necesitas habilitarlo en tu archivo daemon.json y reiniciar el motor. Si estás usando Docker para Windows o Docker para Mac, puedes hacerlo usando la interfaz de usuario como se muestra a continuación:

¿Cómo analizar y mejorar el tamaño de tus imágenes de Docker?

Resumen

Estos ajustes te ayudarán a hacer tus imágenes de Docker más delgadas y mucho más agradable el proceso de extraer y empujar y, al mismo tiempo, incluso ahorrar algo de dinero con respecto al almacenamiento de las imágenes en el repositorio de tu elección. Y no solo para ti, sino para muchos otros que pueden aprovechar el trabajo que estás haciendo. Así que piensa en ti mismo, pero también piensa en la comunidad.

Cómo escanear imágenes de Docker en tu máquina local

Cómo escanear imágenes de Docker en tu máquina local

Aprende cómo puedes aprovechar el uso de Snyk dentro de tu instalación del motor Docker

Vista desde una montaña
Foto de Alberto Restifo en Unsplash.

La seguridad es el tema más relevante en la arquitectura moderna. Necesita ser manejado desde todas las diferentes perspectivas. Tener un solo equipo auditando las plataformas y los desarrollos que construimos no es suficiente.

La introducción de DevSecOps como la nueva normalidad, incluyendo a los equipos de seguridad y las políticas como parte del proceso de desarrollo para evitar que la seguridad se convierta en un obstáculo para la innovación y asegurar que los artefactos que desplegamos estén seguros, ha dejado esto claro.

El escaneo de imágenes Docker es uno de los temas más importantes que podemos cubrir respecto a las imágenes de contenedores para saber que todos los componentes internos que forman parte de la imagen están a salvo de vulnerabilidades. Usualmente confiamos en algunos sistemas para hacerlo.

Escribí un artículo sobre el uso de una de las opciones más relevantes (Harbor) del mundo de código abierto para hacer este trabajo.

Y esto también está siendo realizado por diferentes repositorios Docker de proveedores de nube como Amazon ECR a partir de este año. Pero, ¿por qué necesitamos esperar hasta que subamos las imágenes a un registro Docker externo? ¿Por qué no podemos hacerlo en nuestro entorno local?

Ahora podemos. La versión 2.5.0.1 del motor Docker también incluye los componentes de Snyk necesarios para inspeccionar las imágenes Docker directamente desde la línea de comandos:

https://www.docker.com/blog/combining-snyk-scans-in-docker-desktop-and-docker-hub-to-deploy-secure-containers/


Escaneando Tus Imágenes Locales

Entonces, comencemos. Abramos un nuevo terminal y escribamos el siguiente comando:

docker scan <nombre-de-imagen>

Tan pronto como escribamos esto, el comando nos dirá que este proceso de escaneo usará Snyk para hacerlo y necesitamos autorizar el acceso a esos servicios para realizar el proceso de escaneo.

Después de eso, obtenemos una lista de todas las vulnerabilidades detectadas, como puedes ver en la imagen a continuación:

Escaneo de vulnerabilidades
Escaneo de vulnerabilidades usando tu cliente Docker local

Para cada una de las vulnerabilidades, puedes ver los siguientes datos:

Información de vulnerabilidad
Información detallada proporcionada para cada una de las vulnerabilidades detectadas

Obtenemos la biblioteca con la vulnerabilidad, el nivel de severidad y una breve descripción de la misma. Si necesitas más detalles, también puedes consultar la URL proporcionada que está vinculada a una página de descripción para esa vulnerabilidad:

Página de vulnerabilidades
Página detallada de vulnerabilidades de snyk

Finalmente, también proporciona las fuentes que introducen esta biblioteca en tu imagen para que esto pueda resolverse rápidamente.

También proporciona una vista de alto nivel de toda la imagen, como puedes ver aquí:

Visión general de imágenes Docker
Visión general de tus imágenes Docker con todas las vulnerabilidades detectadas

Así que ahora no tienes excusa para no tener todas tus imágenes seguras antes de subirlas a tu repositorio local. ¡Hagámoslo!

Almacenamiento Prometheus: ¿Cómo funciona y por qué es importante?

Almacenamiento Prometheus: ¿Cómo funciona y por qué es importante?

Aprende las bases que hacen de Prometheus una gran solución para monitorear tus cargas de trabajo y úsalo para tu propio beneficio.

Almacenamiento de Prometheus: ¿Cómo funciona y por qué es importante?
Foto de Vincent Botta en Unsplash

Prometheus es uno de los sistemas clave en las arquitecturas de nube actuales. El segundo proyecto graduado de la Cloud Native Computing Foundation (CNCF) después de Kubernetes, y es la solución de monitoreo por excelencia en la mayoría de las cargas de trabajo que se ejecutan en Kubernetes.

Si ya has usado Prometheus por algún tiempo, sabes que se basa en una base de datos de series temporales, por lo que el almacenamiento de Prometheus es uno de los elementos clave. Basado en sus propias palabras de la página oficial de Prometheus:

Cada serie temporal se identifica de manera única por su nombre de métrica y pares clave-valor opcionales llamados etiquetas, y esa serie es similar a las tablas en un modelo relacional. Y dentro de cada una de esas series, tenemos muestras que son similares a las tuplas. Y cada una de las muestras contiene un valor flotante y una marca de tiempo con precisión de milisegundos.

Enfoque predeterminado en disco

Por defecto, Prometheus utiliza un enfoque de almacenamiento local guardando todas esas muestras en disco. Estos datos se distribuyen en diferentes archivos y carpetas para agrupar diferentes fragmentos de datos.

Así que, tenemos carpetas para crear esos grupos, y por defecto, son un bloque de dos horas y pueden contener uno o más archivos dependiendo de la cantidad de datos ingeridos en ese período de tiempo, ya que cada carpeta contiene todas las muestras para esa línea de tiempo específica.

Además, cada carpeta también tiene algún tipo de archivos de metadatos que ayudan a localizar las métricas de cada uno de los archivos de datos.

Un archivo es persistente de manera completa cuando el bloque ha terminado, y antes de eso, se mantiene en memoria y utiliza una técnica de registro de escritura anticipada para recuperar los datos en caso de un fallo del servidor de Prometheus.

Así que, a un nivel alto, la estructura de directorios del directorio de datos de un servidor de Prometheus se verá algo así:

Integración de Almacenamiento Remoto

El almacenamiento predeterminado en disco es bueno y tiene algunas limitaciones en términos de escalabilidad y durabilidad, incluso considerando la mejora de rendimiento de la última versión de TSDB. Así que, si queremos explorar otras opciones para almacenar estos datos, Prometheus proporciona una manera de integrarse con ubicaciones de almacenamiento remoto.

Proporciona una API que permite escribir muestras que se están ingiriendo en una URL remota y, al mismo tiempo, poder leer datos de muestra para esa URL remota como se muestra en la imagen a continuación:

Como siempre en cualquier cosa relacionada con Prometheus, el número de adaptadores creados usando este patrón es enorme, y se puede ver en el siguiente enlace en detalle:

Resumen

Saber cómo funciona el almacenamiento de Prometheus es crítico para entender cómo podemos optimizar su uso para mejorar el rendimiento de nuestra solución de monitoreo y proporcionar un despliegue rentable.

En las siguientes publicaciones, vamos a cubrir cómo podemos optimizar el uso de esta capa de almacenamiento, asegurándonos de que solo se almacenen las métricas y muestras que son importantes para usar, y también cómo analizar cuáles métricas son las más utilizadas de la base de datos de series temporales para poder tomar buenas decisiones sobre qué métricas deben ser eliminadas y cuáles deben mantenerse.

Así que, mantente atento a la próxima publicación sobre cómo podemos tener una mejor vida con Prometheus y no morir en el intento.

Prometheus Storage: ¿Cómo optimizar el uso del disco?

Prometheus Storage: ¿Cómo optimizar el uso del disco?

Aprende algunos trucos para analizar y optimizar el uso que estás haciendo de la TSDB y ahorrar dinero en tu implementación en la nube.

Prometheus Storage: ¿Cómo optimizar el uso del disco?
Foto de Markus Spiske en Unsplash

En publicaciones anteriores, discutimos cómo funcionaba la capa de almacenamiento para Prometheus y cuán efectiva era. Pero en los tiempos actuales, estamos en la era de la computación en la nube y sabemos que cada optimización técnica es también una optimización de costos, y es por eso que necesitamos ser muy diligentes con cualquier opción que usemos en cuanto a optimización.

Sabemos que usualmente cuando monitoreamos usando Prometheus tenemos muchos exportadores disponibles a nuestra disposición y también que cada uno de ellos expone muchas métricas muy relevantes que necesitamos para rastrear todo lo que necesitamos. Pero también, debemos ser conscientes de que hay métricas que no necesitamos en este momento o que no planeamos usar. Entonces, si no planeamos usarlas, ¿por qué queremos desperdiciar espacio en disco almacenándolas?

Así que, comencemos echando un vistazo a uno de los exportadores que tenemos en nuestro sistema. En mi caso, me gustaría usar una Aplicación de Contenedor BusinessWorks que expone métricas sobre su utilización. Si revisas su punto de acceso de métricas podrías ver algo como esto:

# HELP jvm_info Información de la versión de JVM
# TYPE jvm_info gauge
jvm_info{version="1.8.0_221-b27",vendor="Oracle Corporation",runtime="Java(TM) SE Runtime Environment",} 1.0
# HELP jvm_memory_bytes_used Bytes usados de un área de memoria JVM dada.
# TYPE jvm_memory_bytes_used gauge
jvm_memory_bytes_used{area="heap",} 1.0318492E8
jvm_memory_bytes_used{area="nonheap",} 1.52094712E8
# HELP jvm_memory_bytes_committed Bytes comprometidos de un área de memoria JVM dada.
# TYPE jvm_memory_bytes_committed gauge
jvm_memory_bytes_committed{area="heap",} 1.35266304E8
jvm_memory_bytes_committed{area="nonheap",} 1.71302912E8
# HELP jvm_memory_bytes_max Máximo de bytes de un área de memoria JVM dada.
# TYPE jvm_memory_bytes_max gauge
jvm_memory_bytes_max{area="heap",} 1.073741824E9
jvm_memory_bytes_max{area="nonheap",} -1.0
# HELP jvm_memory_bytes_init Bytes iniciales de un área de memoria JVM dada.
# TYPE jvm_memory_bytes_init gauge
jvm_memory_bytes_init{area="heap",} 1.34217728E8
jvm_memory_bytes_init{area="nonheap",} 2555904.0
# HELP jvm_memory_pool_bytes_used Bytes usados de un pool de memoria JVM dado.
# TYPE jvm_memory_pool_bytes_used gauge
jvm_memory_pool_bytes_used{pool="Code Cache",} 3.3337536E7
jvm_memory_pool_bytes_used{pool="Metaspace",} 1.04914136E8
jvm_memory_pool_bytes_used{pool="Compressed Class Space",} 1.384304E7
jvm_memory_pool_bytes_used{pool="G1 Eden Space",} 3.3554432E7
jvm_memory_pool_bytes_used{pool="G1 Survivor Space",} 1048576.0
jvm_memory_pool_bytes_used{pool="G1 Old Gen",} 6.8581912E7
# HELP jvm_memory_pool_bytes_committed Bytes comprometidos de un pool de memoria JVM dado.
# TYPE jvm_memory_pool_bytes_committed gauge
jvm_memory_pool_bytes_committed{pool="Code Cache",} 3.3619968E7
jvm_memory_pool_bytes_committed{pool="Metaspace",} 1.19697408E8
jvm_memory_pool_bytes_committed{pool="Compressed Class Space",} 1.7985536E7
jvm_memory_pool_bytes_committed{pool="G1 Eden Space",} 4.6137344E7
jvm_memory_pool_bytes_committed{pool="G1 Survivor Space",} 1048576.0
jvm_memory_pool_bytes_committed{pool="G1 Old Gen",} 8.8080384E7
# HELP jvm_memory_pool_bytes_max Máximo de bytes de un pool de memoria JVM dado.
# TYPE jvm_memory_pool_bytes_max gauge
jvm_memory_pool_bytes_max{pool="Code Cache",} 2.5165824E8
jvm_memory_pool_bytes_max{pool="Metaspace",} -1.0
jvm_memory_pool_bytes_max{pool="Compressed Class Space",} 1.073741824E9
jvm_memory_pool_bytes_max{pool="G1 Eden Space",} -1.0
jvm_memory_pool_bytes_max{pool="G1 Survivor Space",} -1.0
jvm_memory_pool_bytes_max{pool="G1 Old Gen",} 1.073741824E9
# HELP jvm_memory_pool_bytes_init Bytes iniciales de un pool de memoria JVM dado.
# TYPE jvm_memory_pool_bytes_init gauge
jvm_memory_pool_bytes_init{pool="Code Cache",} 2555904.0
jvm_memory_pool_bytes_init{pool="Metaspace",} 0.0
jvm_memory_pool_bytes_init{pool="Compressed Class Space",} 0.0
jvm_memory_pool_bytes_init{pool="G1 Eden Space",} 7340032.0
jvm_memory_pool_bytes_init{pool="G1 Survivor Space",} 0.0
jvm_memory_pool_bytes_init{pool="G1 Old Gen",} 1.26877696E8
# HELP jvm_buffer_pool_used_bytes Bytes usados de un pool de buffer JVM dado.
# TYPE jvm_buffer_pool_used_bytes gauge
jvm_buffer_pool_used_bytes{pool="direct",} 148590.0
jvm_buffer_pool_used_bytes{pool="mapped",} 0.0
# HELP jvm_buffer_pool_capacity_bytes Capacidad en bytes de un pool de buffer JVM dado.
# TYPE jvm_buffer_pool_capacity_bytes gauge
jvm_buffer_pool_capacity_bytes{pool="direct",} 148590.0
jvm_buffer_pool_capacity_bytes{pool="mapped",} 0.0
# HELP jvm_buffer_pool_used_buffers Buffers usados de un pool de buffer JVM dado.
# TYPE jvm_buffer_pool_used_buffers gauge
jvm_buffer_pool_used_buffers{pool="direct",} 19.0
jvm_buffer_pool_used_buffers{pool="mapped",} 0.0
# HELP jvm_classes_loaded El número de clases que están actualmente cargadas en la JVM
# TYPE jvm_classes_loaded gauge
jvm_classes_loaded 16993.0
# HELP jvm_classes_loaded_total El número total de clases que han sido cargadas desde que la JVM comenzó a ejecutarse
# TYPE jvm_classes_loaded_total counter
jvm_classes_loaded_total 17041.0
# HELP jvm_classes_unloaded_total El número total de clases que han sido descargadas desde que la JVM comenzó a ejecutarse
# TYPE jvm_classes_unloaded_total counter
jvm_classes_unloaded_total 48.0
# HELP bwce_activity_stats_list Lista de estadísticas de actividad BWCE
# TYPE bwce_activity_stats_list gauge
# HELP bwce_activity_counter_list Lista de contadores relacionados con la actividad BWCE
# TYPE bwce_activity_counter_list gauge
# HELP all_activity_events_count Conteo de todos los eventos de actividad BWCE por estado
# TYPE all_activity_events_count counter
all_activity_events_count{StateName="CANCELLED",} 0.0
all_activity_events_count{StateName="COMPLETED",} 0.0
all_activity_events_count{StateName="STARTED",} 0.0
all_activity_events_count{StateName="FAULTED",} 0.0
# HELP activity_events_count Conteo de todos los eventos de actividad BWCE por proceso, estado de actividad
# TYPE activity_events_count counter
# HELP activity_total_evaltime_count Tiempo total de evaluación de actividad BWCE por proceso y actividad
# TYPE activity_total_evaltime_count counter
# HELP activity_total_duration_count Tiempo total de duración de actividad BWCE por proceso y actividad
# TYPE activity_total_duration_count counter
# HELP bwpartner_instance:total_request Solicitud total para la invocación del socio que se mapeó desde las actividades
# TYPE bwpartner_instance:total_request counter
# HELP bwpartner_instance:total_duration_ms Duración total para la invocación del socio que se mapeó desde las actividades (ejecución o latencia)
# TYPE bwpartner_instance:total_duration_ms counter
# HELP bwce_process_stats Lista de estadísticas de proceso BWCE
# TYPE bwce_process_stats gauge
# HELP bwce_process_counter_list Lista de contadores relacionados con el proceso BWCE
# TYPE bwce_process_counter_list gauge
# HELP all_process_events_count Conteo de todos los eventos de proceso BWCE por estado
# TYPE all_process_events_count counter
all_process_events_count{StateName="CANCELLED",} 0.0
all_process_events_count{StateName="COMPLETED",} 0.0
all_process_events_count{StateName="STARTED",} 0.0
all_process_events_count{StateName="FAULTED",} 0.0
# HELP process_events_count Conteo de eventos de proceso BWCE por operación
# TYPE process_events_count counter
# HELP process_duration_seconds_total Duración de eventos de proceso BWCE por operación en segundos
# TYPE process_duration_seconds_total counter
# HELP process_duration_milliseconds_total Duración de eventos de proceso BWCE por operación en milisegundos
# TYPE process_duration_milliseconds_total counter
# HELP bwdefinitions:partner Conteo de eventos de proceso BWCE por operación
# TYPE bwdefinitions:partner counter
bwdefinitions:partner{ProcessName="t1.module.item.getTransactionData",ActivityName="FTLPublisher",ServiceName="GetCustomer360",OperationName="GetDataOperation",PartnerService="TransactionService",PartnerOperation="GetTransactionsOperation",Location="internal",PartnerMiddleware="MW",} 1.0
bwdefinitions:partner{ProcessName=" t1.module.item.auditProcess",ActivityName="KafkaSendMessage",ServiceName="GetCustomer360",OperationName="GetDataOperation",PartnerService="AuditService",PartnerOperation="AuditOperation",Location="internal",PartnerMiddleware="MW",} 1.0
bwdefinitions:partner{ProcessName="t1.module.item.getCustomerData",ActivityName="JMSRequestReply",ServiceName="GetCustomer360",OperationName="GetDataOperation",PartnerService="CustomerService",PartnerOperation="GetCustomerDetailsOperation",Location="internal",PartnerMiddleware="MW",} 1.0
# HELP bwdefinitions:binding Repositorio de tiempo de diseño BW - definición de enlace/transporte
# TYPE bwdefinitions:binding counter
bwdefinitions:binding{ServiceName="GetCustomer360",OperationName="GetDataOperation",ServiceInterface="GetCustomer360:GetDataOperation",Binding="/customer",Transport="HTTP",} 1.0
# HELP bwdefinitions:service Repositorio de tiempo de diseño BW - definición de servicio
# TYPE bwdefinitions:service counter
bwdefinitions:service{ProcessName="t1.module.sub.item.getCustomerData",ServiceName="GetCustomer360",OperationName="GetDataOperation",ServiceInstance="GetCustomer360:GetDataOperation",} 1.0
bwdefinitions:service{ProcessName="t1.module.sub.item.auditProcess",ServiceName="GetCustomer360",OperationName="GetDataOperation",ServiceInstance="GetCustomer360:GetDataOperation",} 1.0
bwdefinitions:service{ProcessName="t1.module.sub.orchestratorSubFlow",ServiceName="GetCustomer360",OperationName="GetDataOperation",ServiceInstance="GetCustomer360:GetDataOperation",} 1.0
bwdefinitions:service{ProcessName="t1.module.Process",ServiceName="GetCustomer360",OperationName="GetDataOperation",ServiceInstance="GetCustomer360:GetDataOperation",} 1.0
# HELP bwdefinitions:gateway Repositorio de tiempo de diseño BW - definición de puerta de enlace
# TYPE bwdefinitions:gateway counter
bwdefinitions:gateway{ServiceName="GetCustomer360",OperationName="GetDataOperation",ServiceInstance="GetCustomer360:GetDataOperation",Endpoint="bwce-demo-mon-orchestrator-bwce",InteractionType="ISTIO",} 1.0
# HELP process_cpu_seconds_total Tiempo total de usuario y sistema de CPU gastado en segundos.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 1956.86
# HELP process_start_time_seconds Hora de inicio del proceso desde la época de Unix en segundos.
# TYPE process_start_time_seconds gauge
process_start_time_seconds 1.604712447107E9
# HELP process_open_fds Número de descriptores de archivo abiertos.
# TYPE process_open_fds gauge
process_open_fds 763.0
# HELP process_max_fds Número máximo de descriptores de archivo abiertos.
# TYPE process_max_fds gauge
process_max_fds 1048576.0
# HELP process_virtual_memory_bytes Tamaño de la memoria virtual en bytes.
# TYPE process_virtual_memory_bytes gauge
process_virtual_memory_bytes 3.046207488E9
# HELP process_resident_memory_bytes Tamaño de la memoria residente en bytes.
# TYPE process_resident_memory_bytes gauge
process_resident_memory_bytes 4.2151936E8
# HELP jvm_gc_collection_seconds Tiempo gastado en un recolector de basura JVM dado en segundos.
# TYPE jvm_gc_collection_seconds summary
jvm_gc_collection_seconds_count{gc="G1 Young Generation",} 540.0
jvm_gc_collection_seconds_sum{gc="G1 Young Generation",} 4.754
jvm_gc_collection_seconds_count{gc="G1 Old Generation",} 2.0
jvm_gc_collection_seconds_sum{gc="G1 Old Generation",} 0.563
# HELP jvm_threads_current Conteo actual de hilos de una JVM
# TYPE jvm_threads_current gauge
jvm_threads_current 98.0
# HELP jvm_threads_daemon Conteo de hilos daemon de una JVM
# TYPE jvm_threads_daemon gauge
jvm_threads_daemon 43.0
# HELP jvm_threads_peak Conteo máximo de hilos de una JVM
# TYPE jvm_threads_peak gauge
jvm_threads_peak 98.0
# HELP jvm_threads_started_total Conteo de hilos iniciados de una JVM
# TYPE jvm_threads_started_total counter
jvm_threads_started_total 109.0
# HELP jvm_threads_deadlocked Ciclos de hilos JVM que están en deadlock esperando adquirir monitores de objetos o sincronizadores propios
# TYPE jvm_threads_deadlocked gauge
jvm_threads_deadlocked 0.0
# HELP jvm_threads_deadlocked_monitor Ciclos de hilos JVM que están en deadlock esperando adquirir monitores de objetos
# TYPE jvm_threads_deadlocked_monitor gauge
jvm_threads_deadlocked_monitor 0.0

Como puedes ver, muchas métricas, pero tengo que ser honesto, no estoy usando la mayoría de ellas en mis paneles y para generar mis alertas. Puedo usar las métricas relacionadas con el rendimiento de la aplicación para cada uno de los procesos de BusinessWorks y sus actividades, también el rendimiento de la memoria JVM y el número de hilos, pero cosas como cómo está funcionando el GC de la JVM para cada una de las capas de la JVM (G1 Young Generation, G1 Old Generation) no las estoy usando en absoluto.

Entonces, si muestro el mismo punto de acceso de métricas destacando las cosas que no estoy usando, sería algo como esto:

# HELP jvm_info Información de la versión de JVM
# TYPE jvm_info gauge
jvm_info{version="1.8.0_221-b27",vendor="Oracle Corporation",runtime="Java(TM) SE Runtime Environment",} 1.0

# HELP jvm_memory_bytes_used Bytes usados de un área de memoria JVM dada.
# TYPE jvm_memory_bytes_used gauge
jvm_memory_bytes_used{area="heap",} 1.0318492E8
jvm_memory_bytes_used{area="nonheap",} 1.52094712E8
# HELP jvm_memory_bytes_committed Bytes comprometidos de un área de memoria JVM dada.
# TYPE jvm_memory_bytes_committed gauge
jvm_memory_bytes_committed{area="heap",} 1.35266304E8
jvm_memory_bytes_committed{area="nonheap",} 1.71302912E8
# HELP jvm_memory_bytes_max Máximo de bytes de un área de memoria JVM dada.
# TYPE jvm_memory_bytes_max gauge
jvm_memory_bytes_max{area="heap",} 1.073741824E9
jvm_memory_bytes_max{area="nonheap",} -1.0
# HELP jvm_memory_bytes_init Bytes iniciales de un área de memoria JVM dada.
# TYPE jvm_memory_bytes_init gauge
jvm_memory_bytes_init{area="heap",} 1.34217728E8
jvm_memory_bytes_init{area="nonheap",} 2555904.0

# HELP jvm_memory_pool_bytes_used Bytes usados de un pool de memoria JVM dado.
# TYPE jvm_memory_pool_bytes_used gauge
jvm_memory_pool_bytes_used{pool="Code Cache",} 3.3337536E7
jvm_memory_pool_bytes_used{pool="Metaspace",} 1.04914136E8
jvm_memory_pool_bytes_used{pool="Compressed Class Space",} 1.384304E7
jvm_memory_pool_bytes_used{pool="G1 Eden Space",} 3.3554432E7
jvm_memory_pool_bytes_used{pool="G1 Survivor Space",} 1048576.0
jvm_memory_pool_bytes_used{pool="G1 Old Gen",} 6.8581912E7
# HELP jvm_memory_pool_bytes_committed Bytes comprometidos de un pool de memoria JVM dado.
# TYPE jvm_memory_pool_bytes_committed gauge
jvm_memory_pool_bytes_committed{pool="Code Cache",} 3.3619968E7
jvm_memory_pool_bytes_committed{pool="Metaspace",} 1.19697408E8
jvm_memory_pool_bytes_committed{pool="Compressed Class Space",} 1.7985536E7
jvm_memory_pool_bytes_committed{pool="G1 Eden Space",} 4.6137344E7
jvm_memory_pool_bytes_committed{pool="G1 Survivor Space",} 1048576.0
jvm_memory_pool_bytes_committed{pool="G1 Old Gen",} 8.8080384E7
# HELP jvm_memory_pool_bytes_max Máximo de bytes de un pool de memoria JVM dado.
# TYPE jvm_memory_pool_bytes_max gauge
jvm_memory_pool_bytes_max{pool="Code Cache",} 2.5165824E8
jvm_memory_pool_bytes_max{pool="Metaspace",} -1.0
jvm_memory_pool_bytes_max{pool="Compressed Class Space",} 1.073741824E9
jvm_memory_pool_bytes_max{pool="G1 Eden Space",} -1.0
jvm_memory_pool_bytes_max{pool="G1 Survivor Space",} -1.0
jvm_memory_pool_bytes_max{pool="G1 Old Gen",} 1.073741824E9
# HELP jvm_memory_pool_bytes_init Bytes iniciales de un pool de memoria JVM dado.
# TYPE jvm_memory_pool_bytes_init gauge
jvm_memory_pool_bytes_init{pool="Code Cache",} 2555904.0
jvm_memory_pool_bytes_init{pool="Metaspace",} 0.0
jvm_memory_pool_bytes_init{pool="Compressed Class Space",} 0.0
jvm_memory_pool_bytes_init{pool="G1 Eden Space",} 7340032.0
jvm_memory_pool_bytes_init{pool="G1 Survivor Space",} 0.0
jvm_memory_pool_bytes_init{pool="G1 Old Gen",} 1.26877696E8
# HELP jvm_buffer_pool_used_bytes Bytes usados de un pool de buffer JVM dado.
# TYPE jvm_buffer_pool_used_bytes gauge
jvm_buffer_pool_used_bytes{pool="direct",} 148590.0
jvm_buffer_pool_used_bytes{pool="mapped",} 0.0
# HELP jvm_buffer_pool_capacity_bytes Capacidad en bytes de un pool de buffer JVM dado.
# TYPE jvm_buffer_pool_capacity_bytes gauge
jvm_buffer_pool_capacity_bytes{pool="direct",} 148590.0
jvm_buffer_pool_capacity_bytes{pool="mapped",} 0.0
# HELP jvm_buffer_pool_used_buffers Buffers usados de un pool de buffer JVM dado.
# TYPE jvm_buffer_pool_used_buffers gauge
jvm_buffer_pool_used_buffers{pool="direct",} 19.0
jvm_buffer_pool_used_buffers{pool="mapped",} 0.0
# HELP jvm_classes_loaded El número de clases que están actualmente cargadas en la JVM
# TYPE jvm_classes_loaded gauge
jvm_classes_loaded 16993.0
# HELP jvm_classes_loaded_total El número total de clases que han sido cargadas desde que la JVM comenzó a ejecutarse
# TYPE jvm_classes_loaded_total counter
jvm_classes_loaded_total 17041.0
# HELP jvm_classes_unloaded_total El número total de clases que han sido descargadas desde que la JVM comenzó a ejecutarse
# TYPE jvm_classes_unloaded_total counter
jvm_classes_unloaded_total 48.0

# HELP bwce_activity_stats_list Lista de estadísticas de actividad BWCE
# TYPE bwce_activity_stats_list gauge
# HELP bwce_activity_counter_list Lista de contadores relacionados con la actividad BWCE
# TYPE bwce_activity_counter_list gauge
# HELP all_activity_events_count Conteo de todos los eventos de actividad BWCE por estado
# TYPE all_activity_events_count counter
all_activity_events_count{StateName="CANCELLED",} 0.0
all_activity_events_count{StateName="COMPLETED",} 0.0
all_activity_events_count{StateName="STARTED",} 0.0
all_activity_events_count{StateName="FAULTED",} 0.0
# HELP activity_events_count Conteo de todos los eventos de actividad BWCE por proceso, estado de actividad
# TYPE activity_events_count counter
# HELP activity_total_evaltime_count Tiempo total de evaluación de actividad BWCE por proceso y actividad
# TYPE activity_total_evaltime_count counter
# HELP activity_total_duration_count Tiempo total de duración de actividad BWCE por proceso y actividad
# TYPE activity_total_duration_count counter
# HELP bwpartner_instance:total_request Solicitud total para la invocación del socio que se mapeó desde las actividades
# TYPE bwpartner_instance:total_request counter
# HELP bwpartner_instance:total_duration_ms Duración total para la invocación del socio que se mapeó desde las actividades (ejecución o latencia)
# TYPE bwpartner_instance:total_duration_ms counter
# HELP bwce_process_stats Lista de estadísticas de proceso BWCE
# TYPE bwce_process_stats gauge
# HELP bwce_process_counter_list Lista de contadores relacionados con el proceso BWCE
# TYPE bwce_process_counter_list gauge
# HELP all_process_events_count Conteo de todos los eventos de proceso BWCE por estado
# TYPE all_process_events_count counter
all_process_events_count{StateName="CANCELLED",} 0.0
all_process_events_count{StateName="COMPLETED",} 0.0
all_process_events_count{StateName="STARTED",} 0.0
all_process_events_count{StateName="FAULTED",} 0.0
# HELP process_events_count Conteo de eventos de proceso BWCE por operación
# TYPE process_events_count counter
# HELP process_duration_seconds_total Duración de eventos de proceso BWCE por operación en segundos
# TYPE process_duration_seconds_total counter
# HELP process_duration_milliseconds_total Duración de eventos de proceso BWCE por operación en milisegundos
# TYPE process_duration_milliseconds_total counter
# HELP bwdefinitions:partner Conteo de eventos de proceso BWCE por operación
# TYPE bwdefinitions:partner counter
bwdefinitions:partner{ProcessName="t1.module.item.getTransactionData",ActivityName="FTLPublisher",ServiceName="GetCustomer360",OperationName="GetDataOperation",PartnerService="TransactionService",PartnerOperation="GetTransactionsOperation",Location="internal",PartnerMiddleware="MW",} 1.0
bwdefinitions:partner{ProcessName=" t1.module.item.auditProcess",ActivityName="KafkaSendMessage",ServiceName="GetCustomer360",OperationName="GetDataOperation",PartnerService="AuditService",PartnerOperation="AuditOperation",Location="internal",PartnerMiddleware="MW",} 1.0
bwdefinitions:partner{ProcessName="t1.module.item.getCustomerData",ActivityName="JMSRequestReply",ServiceName="GetCustomer360",OperationName="GetDataOperation",PartnerService="CustomerService",PartnerOperation="GetCustomerDetailsOperation",Location="internal",PartnerMiddleware="MW",} 1.0
# HELP bwdefinitions:binding Repositorio de tiempo de diseño BW - definición de enlace/transporte
# TYPE bwdefinitions:binding counter
bwdefinitions:binding{ServiceName="GetCustomer360",OperationName="GetDataOperation",ServiceInterface="GetCustomer360:GetDataOperation",Binding="/customer",Transport="HTTP",} 1.0
# HELP bwdefinitions:service Repositorio de tiempo de diseño BW - definición de servicio
# TYPE bwdefinitions:service counter
bwdefinitions:service{ProcessName="t1.module.sub.item.getCustomerData",ServiceName="GetCustomer360",OperationName="GetDataOperation",ServiceInstance="GetCustomer360:GetDataOperation",} 1.0
bwdefinitions:service{ProcessName="t1.module.sub.item.auditProcess",ServiceName="GetCustomer360",OperationName="GetDataOperation",ServiceInstance="GetCustomer360:GetDataOperation",} 1.0
bwdefinitions:service{ProcessName="t1.module.sub.orchestratorSubFlow",ServiceName="GetCustomer360",OperationName="GetDataOperation",ServiceInstance="GetCustomer360:GetDataOperation",} 1.0
bwdefinitions:service{ProcessName="t1.module.Process",ServiceName="GetCustomer360",OperationName="GetDataOperation",ServiceInstance="GetCustomer360:GetDataOperation",} 1.0
# HELP bwdefinitions:gateway Repositorio de tiempo de diseño BW - definición de puerta de enlace
# TYPE bwdefinitions:gateway counter
bwdefinitions:gateway{ServiceName="GetCustomer360",OperationName="GetDataOperation",ServiceInstance="GetCustomer360:GetDataOperation",Endpoint="bwce-demo-mon-orchestrator-bwce",InteractionType="ISTIO",} 1.0
# HELP process_cpu_seconds_total Tiempo total de usuario y sistema de CPU gastado en segundos.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 1956.86
# HELP process_start_time_seconds Hora de inicio del proceso desde la época de Unix en segundos.
# TYPE process_start_time_seconds gauge
process_start_time_seconds 1.604712447107E9
# HELP process_open_fds Número de descriptores de archivo abiertos.
# TYPE process_open_fds gauge
process_open_fds 763.0
# HELP process_max_fds Número máximo de descriptores de archivo abiertos.
# TYPE process_max_fds gauge
process_max_fds 1048576.0
# HELP process_virtual_memory_bytes Tamaño de la memoria virtual en bytes.
# TYPE process_virtual_memory_bytes gauge
process_virtual_memory_bytes 3.046207488E9
# HELP process_resident_memory_bytes Tamaño de la memoria residente en bytes.
# TYPE process_resident_memory_bytes gauge
process_resident_memory_bytes 4.2151936E8
# HELP jvm_gc_collection_seconds Tiempo gastado en un recolector de basura JVM dado en segundos.
# TYPE jvm_gc_collection_seconds summary
jvm_gc_collection_seconds_count{gc="G1 Young Generation",} 540.0
jvm_gc_collection_seconds_sum{gc="G1 Young Generation",} 4.754
jvm_gc_collection_seconds_count{gc="G1 Old Generation",} 2.0
jvm_gc_collection_seconds_sum{gc="G1 Old Generation",} 0.563

# HELP jvm_threads_current Conteo actual de hilos de una JVM
# TYPE jvm_threads_current gauge
jvm_threads_current 98.0
# HELP jvm_threads_daemon Conteo de hilos daemon de una JVM
# TYPE jvm_threads_daemon gauge
jvm_threads_daemon 43.0
# HELP jvm_threads_peak Conteo máximo de hilos de una JVM
# TYPE jvm_threads_peak gauge
jvm_threads_peak 98.0
# HELP jvm_threads_started_total Conteo de hilos iniciados de una JVM
# TYPE jvm_threads_started_total counter
jvm_threads_started_total 109.0
# HELP jvm_threads_deadlocked Ciclos de hilos JVM que están en deadlock esperando adquirir monitores de objetos o sincronizadores propios
# TYPE jvm_threads_deadlocked gauge
jvm_threads_deadlocked 0.0
# HELP jvm_threads_deadlocked_monitor Ciclos de hilos JVM que están en deadlock esperando adquirir monitores de objetos
# TYPE jvm_threads_deadlocked_monitor gauge
jvm_threads_deadlocked_monitor 0.0

Entonces, puede ser un 50% de la respuesta del punto de acceso de métricas la parte que no estoy usando, entonces, ¿por qué estoy usando espacio en disco que estoy pagando para almacenarlo? Y esto es solo para un «exportador crítico», uno que trato de usar tanta información como sea posible, pero piensa en cuántos exportadores tienes y cuánta información usas para cada uno de ellos.

Ok, entonces ahora el propósito y la motivación de esta publicación están claros, pero ¿qué podemos hacer al respecto?

Descubriendo la API REST

Prometheus tiene una increíble API REST para exponer toda la información que puedas desear. Si alguna vez has usado la Interfaz Gráfica para Prometheus (mostrada a continuación) estás usando la API REST porque esto es lo que está detrás de ella.

Prometheus Storage: ¿Cómo optimizar el uso del disco?
Vista de destino de la Interfaz Gráfica de Prometheus

Tenemos toda la documentación sobre la API REST en la documentación oficial de Prometheus:

https://prometheus.io/docs/prometheus/latest/querying/api/

Pero, ¿qué nos proporciona esta API en términos de la base de datos de series temporales TSDB que utiliza Prometheus?

APIs de Administración de TSDB

Tenemos una API específica para gestionar el rendimiento de la base de datos TSDB, pero para poder usarla, necesitamos habilitar la API de Administración. Y eso se hace proporcionando la siguiente bandera donde estamos lanzando el servidor Prometheus --web.enable-admin-api.

Si estamos usando el Prometheus Operator Helm Chart para desplegar esto, necesitamos usar el siguiente elemento en nuestro values.yaml

## EnableAdminAPI habilita la API HTTP administrativa de Prometheus, que incluye funcionalidades como eliminar series temporales.    
## Esto está deshabilitado por defecto.
## ref: https://prometheus.io/docs/prometheus/latest/querying/api/#tsdb-admin-apis
## enableAdminAPI: true

Tenemos muchas opciones habilitadas cuando habilitamos esta API administrativa, pero hoy nos vamos a centrar en una sola operación REST que es el «stats». Este es el único método relacionado con TSDB que no requiere habilitar la API de Administración. Esta operación, como podemos leer en la documentación de Prometheus, devuelve los siguientes elementos:

headStats: Esto proporciona los siguientes datos sobre el bloque principal de la TSDB:

  • numSeries: El número de series.
  • chunkCount: El número de fragmentos.
  • minTime: La marca de tiempo mínima actual en milisegundos.
  • maxTime: La marca de tiempo máxima actual en milisegundos.

seriesCountByMetricName: Esto proporcionará una lista de nombres de métricas y su conteo de series.

labelValueCountByLabelName: Esto proporcionará una lista de nombres de etiquetas y su conteo de valores.

memoryInBytesByLabelName Esto proporcionará una lista de nombres de etiquetas y la memoria utilizada en bytes. El uso de memoria se calcula sumando la longitud de todos los valores para un nombre de etiqueta dado.

seriesCountByLabelPair Esto proporcionará una lista de pares de valores de etiquetas y su conteo de series.

Para acceder a esa API, necesitamos acceder al siguiente punto de acceso:

GET /api/v1/status/tsdb

Entonces, cuando hago eso en mi implementación de Prometheus, obtengo algo similar a esto:

{
"status":"success",
"data":{
"seriesCountByMetricName":[
{
"name":"apiserver_request_duration_seconds_bucket",
"value":34884
},
{
"name":"apiserver_request_latencies_bucket",
"value":7344
},
{
"name":"etcd_request_duration_seconds_bucket",
"value":6000
},
{
"name":"apiserver_response_sizes_bucket",
"value":3888
},
{
"name":"apiserver_request_latencies_summary",
"value":2754
},
{
"name":"etcd_request_latencies_summary",
"value":1500
},
{
"name":"apiserver_request_count",
"value":1216
},
{
"name":"apiserver_request_total",
"value":1216
},
{
"name":"container_tasks_state",
"value":1140
},
{
"name":"apiserver_request_latencies_count",
"value":918
}
],
"labelValueCountByLabelName":[
{
"name":"__name__",
"value":2374
},
{
"name":"id",
"value":210
},
{
"name":"mountpoint",
"value":208
},
{
"name":"le",
"value":195
},
{
"name":"type",
"value":185
},
{
"name":"name",
"value":181
},
{
"name":"resource",
"value":170
},
{
"name":"secret",
"value":168
},
{
"name":"image",
"value":107
},
{
"name":"container_id",
"value":97
}
],
"memoryInBytesByLabelName":[
{
"name":"__name__",
"value":97729
},
"name":"id",
"value":21450
},
{
"name":"mountpoint",
"value":18123
},
{
"name":"name",
"value":13831
},
{
"name":"image",
"value":8005
},
{
"name":"container_id",
"value":7081
},
{
"name":"image_id",
"value":6872
},
{
"name":"secret",
"value":5054
},
{
"name":"type",
"value":4613
},
{
"name":"resource",
"value":3459
}
],
"seriesCountByLabelValuePair":[
{
"name":"namespace=default",
"value":72064
},
{
"name":"service=kubernetes",
"value":70921
},
{
"name":"endpoint=https",
"value":70917
},
{
"name":"job=apiserver",
"value":70917
},
{
"name":"component=apiserver",
"value":57992
},
{
"name":"instance=192.168.185.199:443",
"value":40343
},
{
"name":"__name__=apiserver_request_duration_seconds_bucket",
"value":34884
},
{
"name":"version=v1",
"value":31152
},
{
"name":"instance=192.168.112.31:443",
"value":30574
},
{
"name":"scope=cluster",
"value":29713
}
]
}
}

También podemos verificar la misma información si usamos la nueva y experimental Interfaz de Usuario React en el siguiente punto de acceso:

/new/tsdb-status
Prometheus Storage: ¿Cómo optimizar el uso del disco?
Visualización gráfica del conteo de las 10 series principales por nombre de métrica en la nueva interfaz de usuario de Prometheus

Entonces, con eso, obtendrás las 10 series y etiquetas principales que están dentro de tu base de datos de series temporales, por lo que en caso de que algunas de ellas no sean útiles, puedes simplemente deshacerte de ellas usando los enfoques normales para eliminar una serie o una etiqueta. Esto es genial, pero ¿qué pasa si todas las que se muestran aquí son relevantes, qué podemos hacer al respecto?

Mmmm, tal vez podamos usar PromQL para monitorear esto (enfoque de dogfooding). Así que si quisiéramos extraer la misma información pero usando PromQL, podemos hacerlo con la siguiente consulta:

topk(10, count by (__name__)({__name__=~".+"}))
Prometheus Storage: ¿Cómo optimizar el uso del disco?
Top 10 de series de métricas generadas y almacenadas en la base de datos de series temporales

Y ahora tenemos todo el poder en mis manos. Por ejemplo, echemos un vistazo no a las 10 más relevantes, sino a las 100 más relevantes o cualquier otro filtro que necesitemos aplicar. Por ejemplo, veamos las métricas relacionadas con la JVM que discutimos al principio. Y lo haremos con la siguiente consulta PromQL:

topk(100, count by (__name__)({__name__=~"jvm.+"}))
Prometheus Storage: ¿Cómo optimizar el uso del disco?
Top 100 de series de métricas relacionadas con métricas de JVM

Así que podemos ver que tenemos al menos 150 series relacionadas con métricas que no estoy usando en absoluto. Pero hagámoslo aún mejor, echemos un vistazo a lo mismo pero agrupado por nombres de trabajos:

topk(10, count by (job,__name__)({__name__=~".+"}))
Prometheus Storage: ¿Cómo optimizar el uso del disco?
Resultado de verificar el conteo de las 10 series de métricas principales con el trabajo que las está generando

La diferencia visual podría ser la pieza que falta en el desarrollo de bajo código

La diferencia visual podría ser la pieza que falta en el desarrollo de bajo código

Ayudándote a sobresalir usando low code en equipos distribuidos y programación paralela

Escalera mecánica
Foto de Tomasz Frankowski en Unsplash.

La mayoría de las empresas están explorando el desarrollo low-code/no-code ahora que lo más importante es lograr agilidad en los artefactos tecnológicos desde diferentes perspectivas (desarrollo, implementación y operación).

Los beneficios de esta forma de trabajar hacen que sea casi una decisión obvia para la mayoría de las empresas. Ya los cubrimos en un artículo anterior. Échale un vistazo si aún no lo has leído.

Pero sabemos que todas las cosas nuevas vienen con sus propios desafíos que necesitamos abordar y dominar para liberar todos los beneficios que estos nuevos paradigmas o tecnologías están proporcionando. Al igual que con la arquitectura nativa de la nube, necesitamos ser capaces de adaptarnos.

A veces no es la cultura lo que necesitamos cambiar. A veces la tecnología y las herramientas también necesitan evolucionar para abordar esos desafíos y ayudarnos en ese viaje. Y así es como Visual Diff cobró vida.

Cuando desarrollas usando un enfoque low-code, todo el proceso de desarrollo es más fácil. Necesitas combinar diferentes bloques que realizan la lógica que necesitas, y todo es más simple que un montón de líneas de código.

Ejemplo de desarrollo low-code
Enfoque de desarrollo low-code usando TIBCO BusinessWorks.

Pero también necesitamos gestionar todos estos artefactos en un repositorio donde todos ellos están enfocados en el desarrollo de código fuente. Eso significa que cuando trabajas con esas herramientas al final, no estás trabajando con un “enfoque low-code” sino más bien con un enfoque de código fuente. Cosas como fusionar diferentes ramas y mirar el historial de versiones para conocer los cambios son complejas.

Y son complejas porque son realizadas por el propio repositorio, que está enfocado en los cambios de archivos y el código fuente que cambia. Pero uno de los grandes beneficios del desarrollo low-code es que el desarrollador no necesita estar al tanto del código fuente generado como parte de la actividad visual, más rápida. Entonces, ¿cómo podemos resolver eso? ¿Qué podemos usar para resolver eso?

Las tecnologías low-code necesitan avanzar para tomar la delantera aquí. Por ejemplo, esto es lo que TIBCO BusinessWorks ha hecho con el lanzamiento de su capacidad Visual Diff.

Entonces, todavía tienes tu integración con tu repositorio de código fuente. Puedes hacer todos los procesos y actividades que usualmente necesitas hacer en este tipo de desarrollo distribuido paralelo. Aún así, también puedes ver todas esas actividades desde una perspectiva “low-code”.

Eso significa que cuando estoy echando un vistazo al historial de versiones, puedo ver los artefactos visuales que han sido modificados. Las actividades añadidas o eliminadas se muestran allí de una manera significativa para el desarrollo low-code. Eso cierra el ciclo sobre cómo los desarrollos low-code pueden aprovechar todas las ventajas de los repositorios de código fuente modernos y sus flujos (GitFlow, GitHub Flow, One Flow, etc.) así como las ventajas de la perspectiva low-code.

Digamos que hay dos opciones con las que puedes ver cómo ha cambiado una aplicación. Una es el enfoque tradicional y la otra usa el Visual Diff:

Visual Diff de tus procesos
Opción A: Visual Diff de tus procesos
Mismos procesos pero con un enfoque de Comparación de Texto
Opción B: Mismos procesos pero con un enfoque de Comparación de Texto

Entonces, basándonos en esta evidencia, ¿qué crees que es más fácil de entender? Incluso si eres un verdadero programador como yo, no podemos negar la facilidad y los beneficios del enfoque low-code para el desarrollo masivo y estándar en el mundo empresarial.


Resumen

No importa qué tan rápido estemos desarrollando con todos los aceleradores y marcos que tenemos, una aplicación low-code bien definida será más rápida que cualquiera de nosotros. Es la misma batalla que tuvimos en el pasado con las Interfaces Gráficas o el control del ratón frente al teclado.

Aceptamos que hay una preferencia personal para elegir una u otra, pero cuando necesitamos decidir qué es más efectivo y necesitamos basarnos en los hechos, no podemos ser ciegos a lo que está frente a nosotros.

Espero que hayas disfrutado este artículo. ¡Que tengas un buen día!

Distribuciones de Kubernetes: ¿Qué son y cuáles son los 5 mejores jugadores disponibles?

Distribuciones de Kubernetes: ¿Qué son y cuáles son los 5 mejores jugadores disponibles?

Aprende qué son las Distribuciones de Kubernetes, por qué te importa a ti y quiénes son los mejores jugadores disponibles en el mercado hoy en día

Distribuciones de Kubernetes — ¿Qué son?
Foto de Drew Beamer en Unsplash

Introducción

Uno de los mayores anuncios de las últimas sesiones de AWS re:Invent 2020 fue el lanzamiento de EKS-D de Amazon. EKS-D es su Distribución de Kubernetes de código abierto que ahora está disponible para que todos la usen en su proveedor de nube o incluso en sus instalaciones.

Está basado en hallazgos anteriores y en todo el proceso que Amazon ha llevado a cabo en la gestión de su plataforma gestionada de Kubernetes, Amazon EKS.

Estos anuncios han hecho que muchas personas se pregunten: “OK, conozco Kubernetes, pero ¿qué es una distribución de Kubernetes? ¿Y por qué debería importarme?”

Así que intentaré responder eso con el conocimiento que tengo, y siempre trato de usar el mismo enfoque: una comparación del modelo de Kubernetes versus Linux.

Kubernetes es un proyecto de código abierto, como sabes, iniciado por Google y ahora gestionado por la comunidad y la Cloud Native Computing Foundation (CNCF), y puedes encontrar todo el código disponible aquí:

Pero seamos honestos: No muchos de nosotros estamos descargando ese repositorio e intentando compilarlo para proporcionar un clúster. No es así como solemos trabajar. Si sigues el camino del código — descargándolo, construyéndolo, etc. — esto generalmente se llama vanilla Kubernetes.

Si comenzamos con la comparación con Linux, es la misma situación que tenemos con el kernel de Linux que la mayoría de las distribuciones de Linux envían, pero esto ya está compilado y disponible con un montón de otras herramientas que trabajan juntas a través del enfoque habitual.

Así que eso es lo que es una distribución de Kubernetes. Construyen Kubernetes. Proporcionan otras herramientas y componentes para mejorar o proporcionar más características y para centrarse en aspectos adicionales como un enfoque en la seguridad, un enfoque en DevOps, u otro enfoque. Otro concepto que generalmente se plantea es la pureza de la distribución, y tratamos de hablar sobre una distribución que sea pura.

Llamamos a una distribución pura cuando está construyendo Kubernetes, y eso es todo. Deja todo lo demás a los desarrolladores o usuarios para decidir qué quieren usar encima de ella.


¿Cuáles son los Componentes Principales que se Incluyen en una Distribución de Kubernetes?

Los componentes principales que pueden diferir cuando hablamos de una distribución de Kubernetes son los siguientes:

Tiempo de ejecución de contenedores y registros

Todos sabemos que hay más de un tiempo de ejecución de contenedores, e incluso si no estabas al tanto de eso, probablemente hayas leído todos los artículos sobre la eliminación del soporte de Docker en Kubernetes v1.20, como puedes leer en este increíble artículo de Edgar Rodriguez.

En este momento, parece que todos los tiempos de ejecución deberían soportar la existente Interfaz de Tiempo de Ejecución de Contenedores, y tiempos de ejecución como CRI-O, Containerd, o Kata parecen ser las opciones predeterminadas ahora.

Redes

Otro tema que a menudo difiere cuando hablamos de distribuciones de Kubernetes es cómo gestionan su red, y este es uno de los aspectos más críticos de toda la plataforma.

Como tenemos con el tiempo de ejecución de contenedores, existe una especificación estándar para cubrir ese tema, y esa es la Interfaz de Red de Contenedores (CNI). Existen varios proyectos sobre este tema, como Flannel, Calico, Canal, y Wave. Además, algunas plataformas proporcionan su propio componente, como el operador Openshift SDN.

Almacenamiento

Cómo manejar el almacenamiento en Kubernetes también es muy importante, especialmente a medida que adoptamos este modelo en implementaciones que requieren modelos con estado. Diferentes plataformas pueden soportar diferentes opciones de almacenamiento, como sistemas de archivos, etc.


¿Quiénes son los Mejores Jugadores?

Lo primero que debemos tener en cuenta es que hay un gran número de distribuciones de Kubernetes por ahí.

Contaremos las que tienen una certificación CNCF, y puedes echar un vistazo a todas ellas aquí. En el momento de escribir este artículo, estamos hablando de 72 distribuciones certificadas.

Logos de las diversas distribuciones certificadas por CNCF.
Imagen a través de la Cloud Native Computing Foundation

Estos son los que me gustaría destacar hoy:

Red Hat OpenShift

Logo de Red Hat OpenShift.

La plataforma Red Hat OpenShift podría ser una de las plataformas más utilizadas, especialmente en un entorno de nube privada. Podría incluir la mayoría de los servicios de Red Hat en cuanto a almacenamiento, como GlusterFS y redes con OpenShift DNS. Tiene OKD como el proyecto de código abierto que respalda y contribuye a la plataforma OpenShift. Consulta este artículo para ver cómo configurar Openshift localmente para probarlo

Mirantis

Logo de Mirantis

La antigua empresa Docker que ha sido adquirida por Mirantis es otra de las opciones habituales cuando hablamos de plataformas soportadas.

VMware Tanzu

Logo de VMware Tanzu

VMware Tanzu, también proveniente de la adquisición de Pivotal por parte de VMware, es una plataforma de Kubernetes.

Canonical

Logo de Canonical

Canonical (código abierto) es una plataforma de la empresa que desarrolla y mantiene Ubuntu. Es otra de las opciones importantes aquí y proporciona una variedad de opciones, centrándose no solo en el modo central común sino también en implementaciones de Kubernetes en el borde con proyectos como MicroK8S y más opciones.

Rancher

Logo de Rancher

Rancher (código abierto) es otro de los grandes jugadores, centrándose en seguir y extender los estándares de CNCF y también ofreciendo un gran impulso para la implementación en el borde con K3S. También ofrece actualizaciones automáticas.


Resumen

Así que, como puedes ver, el número de opciones disponibles es enorme. Todas difieren, por lo que es importante tomarse su tiempo cuando se decide la plataforma objetivo basada en sus criterios para su proyecto o su empresa.

Y eso sin cubrir las plataformas gestionadas disponibles que se están convirtiendo en una de las opciones más preferidas para las empresas para que puedan obtener toda la flexibilidad de Kubernetes sin necesidad de manejar la complejidad de gestionar una plataforma de Kubernetes por sí mismos. Pero ese es un tema para otro artículo — esperemos que pronto.

Este artículo al menos te ha proporcionado más claridad sobre qué es una distribución de Kubernetes, las principales diferencias entre ellas, y un vistazo rápido a algunos de los actores clave en este espectro. Disfruta tu día, y disfruta tu vida.

Escalado automático de Kubernetes: Aprende a escalar tus implementaciones de Kubernetes de manera dinámica

Escalado automático de Kubernetes: Aprende a escalar tus implementaciones de Kubernetes de manera dinámica

Descubre las diferentes opciones para escalar tu plataforma según la carga de tráfico que recibas

Escalado automático de Kubernetes: Aprende a escalar tus implementaciones de Kubernetes de manera dinámica
Foto de SpaceX en Unsplash.

Cuando hablamos de Kubernetes, siempre estamos hablando de las opciones de flexibilidad que proporciona. Normalmente, uno de los temas que surgen en la discusión son las opciones de elasticidad que vienen con la plataforma, especialmente cuando se trabaja con un proveedor de nube pública. Pero, ¿cómo podemos implementarlo realmente?

Antes de comenzar a mostrar cómo escalar nuestra plataforma Kubernetes, necesitamos hacer un breve repaso de las opciones que están disponibles para nosotros:

  • Cluster Autoscaler: Cuando la carga de toda la infraestructura alcanza su pico, podemos mejorarla creando nuevos nodos de trabajo para alojar más instancias de servicio.
  • Horizontal Pod Autoscaling: Cuando la carga para un pod específico o conjunto de pods alcanza su pico, desplegamos una nueva instancia para asegurar que podemos tener la disponibilidad global que necesitamos.

Veamos cómo podemos implementar esto usando uno de los servicios gestionados de Kubernetes más populares, los Servicios de Kubernetes Elásticos (EKS) de Amazon.


Configuración

Lo primero que vamos a hacer es crear un clúster con un solo nodo de trabajo para demostrar fácilmente el comportamiento de escalabilidad. Y para hacerlo, vamos a usar la herramienta de línea de comandos eksctl para gestionar fácilmente un clúster EKS.

Para poder crear el clúster, lo haremos con el siguiente comando:

eksctl create cluster --name=eks-scalability --nodes=1 --region=eu-west-2 --node-type=m5.large --version 1.17 --managed --asg-access

Después de unos minutos, tendremos nuestro propio clúster de Kubernetes con un solo nodo para desplegar aplicaciones sobre él.

Ahora vamos a crear una aplicación de muestra para generar carga. Vamos a usar TIBCO BusinessWorks Application Container Edition para generar una aplicación simple. Será una API REST que ejecutará un bucle de 100,000 iteraciones actuando como un contador y devolverá un resultado.

Aplicación de muestra de BusinessWorks para mostrar las opciones de escalabilidad
Aplicación de muestra de BusinessWorks para mostrar las opciones de escalabilidad

Y utilizaremos los recursos disponibles en este repositorio de GitHub:

Construiremos la imagen del contenedor y la subiremos a un registro de contenedores. En mi caso, voy a usar mi instancia de Amazon ECR para hacerlo, y usaré los siguientes comandos:

docker build -t testeks:1.0 .
docker tag testeks:1.0 938784100097.dkr.ecr.eu-west-2.amazonaws.com/testeks:1.0
docker push 938784100097.dkr.ecr.eu-west-2.amazonaws.com/testeks:1.0

Y una vez que la imagen esté subida al registro, desplegaremos la aplicación sobre el clúster de Kubernetes usando este comando:

kubectl apply -f .testeks.yaml

Después de eso, tendremos nuestra aplicación desplegada allí, como puedes ver en la imagen a continuación:

Imagen desplegada en el clúster de Kubernetes
Imagen desplegada en el clúster de Kubernetes

Entonces, ahora podemos probar la aplicación. Para hacerlo, haré que el puerto 8080 esté disponible usando un comando de reenvío de puerto como este:

kubectl port-forward pod/testeks-v1-869948fbb-j5jh7 8080:8080

Con eso, puedo ver y probar la aplicación de muestra usando el navegador, como se muestra a continuación:

Probador de Swagger UI para la aplicación de muestra de Kubernetes
Probador de Swagger UI para la aplicación de muestra de Kubernetes

Escalado automático de pods horizontales

Ahora, necesitamos comenzar a definir las reglas de escalado automático, y comenzaremos con la regla de Escalado Automático de Pods Horizontales (HPA). Necesitaremos elegir el recurso que nos gustaría usar para escalar nuestro pod. En esta prueba, usaré la utilización de CPU para hacerlo, y usaré el siguiente comando:

kubectl autoscale deployment testeks-v1 --min=1 --max=5 --cpu-percent=80

Ese comando escalará el conjunto de réplicas testeks de una (1) instancia a cinco (5) instancias cuando el porcentaje de utilización de CPU sea superior al 80%.

Si ahora verificamos el estado de los componentes, obtendremos algo similar a la imagen a continuación:

Definición de regla HPA para la aplicación usando la utilización de CPU como la métrica clave
Definición de regla HPA para la aplicación usando la utilización de CPU como la métrica clave

Si verificamos la columna TARGETS, veremos este valor: <unknown>/80%. Eso significa que el 80% es el objetivo para activar las nuevas instancias y el uso actual es <unknown>.

No tenemos nada desplegado en el clúster para obtener las métricas de cada uno de los pods. Para resolver eso, necesitamos desplegar el Servidor de Métricas. Para hacerlo, seguiremos la documentación de Amazon AWS:

Entonces, ejecutando el siguiente comando, tendremos el Servidor de Métricas instalado.

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.7/components.yaml

Y después de hacer eso, si verificamos nuevamente, podemos ver que el uso actual ha reemplazado el <unknown>:

Utilización actual de recursos después de instalar el servidor de métricas en el clúster de Kubernetes
Utilización actual de recursos después de instalar el Servidor de Métricas en el clúster de Kubernetes

Si eso funciona, voy a comenzar a enviar solicitudes usando una Prueba de Carga dentro del clúster. Usaré la aplicación de muestra definida a continuación:

Para desplegar, usaremos un archivo YAML con el siguiente contenido:

https://gist.github.com/BetterProgramming/53181f3aa7bee7b7e3adda7c4ed8ca40#file-deploy-yaml

Y lo desplegaremos usando el siguiente comando:

kubectl apply -f tester.yaml

Después de hacer eso, veremos que la utilización actual está aumentando. Después de unos segundos, comenzará a girar nuevas instancias hasta que alcance el número máximo de pods definido en la regla HPA.

Aumento de pods cuando la carga excede el objetivo definido en pasos anteriores.
Aumento de pods cuando la carga excede el objetivo definido en pasos anteriores.

Luego, tan pronto como la carga también disminuya, el número de instancias será eliminado.

Los pods se eliminan tan pronto como la carga disminuye.
Los pods se eliminan tan pronto como la carga disminuye.

Escalado automático de clústeres

Ahora, necesitamos ver cómo podemos implementar el Escalador Automático de Clústeres usando EKS. Usaremos la información que proporciona Amazon:

https://github.com/alexandrev/testeks

El primer paso es desplegar el escalado automático del clúster, y lo haremos usando el siguiente comando:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/aws/examples/cluster-autoscaler-autodiscover.yaml

Luego ejecutaremos este comando:

kubectl -n kube-system annotate deployment.apps/cluster-autoscaler cluster-autoscaler.kubernetes.io/safe-to-evict=”false”

Y editaremos el despliegue para proporcionar el nombre actual del clúster que estamos gestionando. Para hacer eso, ejecutaremos el siguiente comando:

kubectl -n kube-system edit deployment.apps/cluster-autoscaler

Cuando tu editor de texto predeterminado se abra con el contenido del texto, necesitas hacer los siguientes cambios:

  • Establece el nombre de tu clúster en el marcador de posición disponible.
  • Agrega estas propiedades adicionales:
- --balance-similar-node-groups
- --skip-nodes-with-system-pods=false
Ediciones de despliegue necesarias para configurar el Escalador Automático de Clústeres
Ediciones de despliegue necesarias para configurar el Escalador Automático de Clústeres

Ahora necesitamos ejecutar el siguiente comando:

kubectl -n kube-system set image deployment.apps/cluster-autoscaler cluster-autoscaler=eu.gcr.io/k8s-artifacts-prod/autoscaling/cluster-autoscaler:v1.17.4

Lo único que queda es definir la política de AutoScaling. Para hacerlo, usaremos el portal de Servicios de AWS:

  • Ingresa a la página de servicio EC en la región en la que hemos desplegado el clúster.
  • Selecciona las opciones de Grupo de Auto Scaling.
  • Selecciona el Grupo de Auto Scaling que se ha creado como parte del proceso de creación del clúster EKS.
  • Ve a la pestaña de Escalado Automático y haz clic en el botón Agregar Política disponible.
Opción de política de escalado automático en la consola de servicio EC2
Opción de política de escalado automático en la consola de servicio EC2

Luego deberíamos definir la política. Usaremos la utilización promedio de CPU como la métrica y estableceremos el valor objetivo en 50%:

Diálogo de creación de política de escalado automático
Diálogo de creación de política de escalado automático

Para validar el comportamiento, generaremos carga usando el probador como lo hicimos en la prueba anterior y validaremos la carga del nodo usando el siguiente comando:

kubectl top nodes
Salida de muestra de kubectl top nodes
Salida de muestra de kubectl top nodes

Ahora desplegamos el probador nuevamente. Como ya lo tenemos desplegado en este clúster, necesitamos eliminarlo primero para desplegarlo nuevamente:

kubectl delete -f .tester.yaml
kubectl apply -f .tester.yaml

Tan pronto como comience la carga, se crearán nuevos nodos, como se muestra en la imagen a continuación:

Salida de muestra de kubectl top nodes
kubectl top nodes mostrando cómo los nodos han sido escalados hacia arriba

Después de que la carga termine, volvemos a la situación anterior:

kubectl top nodes mostrando cómo los nodos han sido escalados hacia abajo
kubectl top nodes mostrando cómo los nodos han sido escalados hacia abajo

Resumen

En este artículo, hemos mostrado cómo podemos escalar un clúster de Kubernetes de manera dinámica tanto a nivel de nodo de trabajo usando la capacidad de Escalador Automático de Clústeres como a nivel de pod usando el Escalador Automático de Pods Horizontales. Eso nos da todas las opciones necesarias para crear un entorno verdaderamente elástico y flexible capaz de adaptarse a las necesidades de cada momento con el enfoque más eficiente.

CICD Docker: Las 3 principales razones para usar contenedores en tu pipeline de DevSecOps

CICD Docker: Las 3 principales razones para usar contenedores en tu pipeline de DevSecOps

Mejora el rendimiento y la productividad de tu canalización DevSecOps usando contenedores.

CICD Docker: Las 3 principales razones para usar contenedores en tu pipeline de DevSecOps
Foto de Austin Distel en Unsplash

CICD Docker significa el enfoque que la mayoría de las empresas están utilizando para introducir contenedores también en la fase de construcción y pre-despliegue para implementar una parte de la canalización CICD. Veamos por qué.

DevSecOps es la nueva norma para despliegues a gran escala en grandes empresas para cumplir con el ritmo requerido en los negocios digitales hoy en día. Estos procesos se orquestan utilizando una herramienta de orquestación CICD que actúa como el cerebro de este proceso. Las herramientas habituales para hacer este trabajo son Jenkins, Bamboo, AzureDevOps, GitLab, GitHub.

En el enfoque tradicional, tenemos diferentes servidores de trabajo realizando etapas del proceso DevOps: Código, Construcción, Prueba, Despliegue, y para cada uno de ellos, necesitamos diferentes tipos de herramientas y utilidades para hacer el trabajo. Por ejemplo, para obtener el código, podemos necesitar un git instalado. Para hacer la construcción, podemos confiar en maven o Gradle, y para probar, podemos usar SonarQube, etc.

CICD Docker: 3 Razones para usar Contenedores en tu canalización DevSecOps
Estructura de CICD Docker y la relación entre Orquestador y Trabajadores

Entonces, al final, necesitamos un conjunto de herramientas para desempeñarnos con éxito, y eso también requiere algo de gestión. En los nuevos días, con el auge del desarrollo nativo en la nube y el enfoque de contenedores en la industria, esto también está afectando la forma en que desarrollas tus canalizaciones para introducir contenedores como parte de la etapa.

En la mayoría de los Orquestadores CI, puedes definir una imagen de contenedor para ejecutar como cualquier paso de tu proceso DevSecOps, y déjame decirte que es genial si lo haces porque esto te proporcionará muchos de los beneficios de los que necesitas estar consciente.

1.- Solución mucho más escalable 

Uno de los problemas cuando usas un orquestador como el elemento principal en tu empresa, y que está siendo utilizado por muchas tecnologías diferentes que pueden ser de código abierto, propietario, basado en código, desarrollo visual, etc., significa que necesitas gestionar muchas cosas e instalar el software en los trabajadores.

Usualmente, lo que haces es que defines algunos trabajadores para hacer la construcción de algunos artefactos, como la imagen mostrada a continuación:

CICD Docker: Las 3 principales razones para usar contenedores en tu pipeline de DevSecOps
Distribución de trabajadores basada en sus propias capacidades

Eso es genial porque permite la segmentación del proceso de construcción y no requiere que todo el software esté instalado en todas las máquinas, incluso cuando pueden ser incompatibles.

Pero, ¿qué pasa si necesitamos desplegar muchas aplicaciones de uno de los tipos que tenemos en la imagen a continuación, como aplicaciones de TIBCO BusinessWorks? Que estarás limitado según el número de trabajadores que tengan el software instalado para construirlo y desplegarlo.

Con un enfoque basado en contenedores, tendrás todos los trabajadores disponibles porque no se necesita software, solo necesitas extraer la imagen de docker, y eso es todo, así que solo estás limitado por la infraestructura que usas, y si adoptas una plataforma en la nube como parte del proceso de construcción, estas limitaciones simplemente se eliminan. Tu tiempo de comercialización y ritmo de despliegue mejoran.

2.- Fácil de mantener y extender

Si eliminas la necesidad de instalar y gestionar los trabajadores porque se activan cuando los necesitas y se eliminan cuando no son necesarios y todo lo que necesitas hacer es crear una imagen de contenedor que haga el trabajo, el tiempo y el esfuerzo que los equipos necesitan gastar en mantener y extender la solución disminuirán considerablemente.

También la eliminación de cualquier proceso de actualización para los componentes involucrados en los pasos ya que siguen el proceso habitual de imagen de contenedor.

3.- Evitar el bloqueo del Orquestador

Como confiamos en los contenedores para hacer la mayor parte del trabajo, el trabajo que necesitamos hacer para movernos de una solución DevOps a otra es pequeño, y eso nos da el control para elegir en cualquier momento si la solución que estamos usando es la mejor para nuestro caso de uso y contexto o necesitamos movernos a otra más optimizada sin el problema de justificar grandes inversiones para hacer ese trabajo.

Recuperas el control, y también puedes incluso ir a un enfoque de multi-orquestador si es necesario, como usar la mejor solución para cada caso de uso y obtener todos los beneficios de cada uno de ellos al mismo tiempo sin necesidad de luchar contra cada uno de ellos.

Resumen

Todos los beneficios que todos conocemos de los paradigmas de desarrollo nativo en la nube y los contenedores son relevantes para el desarrollo de aplicaciones y otros procesos que usamos en nuestra organización, siendo uno de esos tu canalización y procesos DevSecOps. Comienza hoy haciendo ese viaje para obtener todas esas ventajas en el proceso de construcción y no esperes hasta que sea demasiado tarde. Disfruta tu día. Disfruta tu vida.