Kitabı oku: «Guía práctica de Kubernetes», sayfa 5
Servicios a nivel de clúster
Además de las herramientas para asignar y gestionar espacios de nombres, también hay servicios útiles a nivel de clúster, y es una buena idea habilitarlos en nuestro clúster de desarrollo. El primero es la agregación de registros a un sistema central de Logging as a Service (administración de registros como servicio) (LaaS). Una de las cosas más fáciles de hacer para que los desarrolladores entiendan el funcionamiento de sus aplicaciones es escribir algo en STDOUT. Aunque podemos acceder a estos registros a través de kubectl logs, estos tienen una longitud limitada y no son localizables particularmente. Si, en cambio, enviamos automáticamente esos registros a un sistema LaaS —como puede ser un servicio en la nube o un clúster de búsqueda elástica—, los desarrolladores pueden buscar fácilmente información relevante en los registros, así como información de registros añadida a través de varios contenedores a su servicio.
Habilitación de flujos de trabajo para desarrolladores
Ahora que hemos tenido éxito en la configuración de un clúster compartido y que podemos registrar nuevos desarrolladores en el clúster, necesitamos conseguir que estos desarrollen sus aplicaciones. Recordemos que uno de los KPI clave que medimos es el tiempo que transcurre desde la incorporación hasta la aplicación inicial que se ejecuta en el clúster. Está claro que, a través de lo que acabamos de describir, podemos autenticar con rapidez a un usuario en un clúster y asignarle un espacio de nombres. Pero, ¿qué hay de empezar con la aplicación? Desafortunadamente, aunque hay algunas técnicas que ayudan en este proceso, por lo general poner en marcha la aplicación inicial requiere más de convención que de automatización. En las siguientes secciones, describimos un enfoque para lograrlo. De ninguna manera se trata de un único enfoque o una única solución. Opcionalmente, podemos aplicar el enfoque tal cual o inspirarnos en ideas para llegar a nuestra propia solución.
Instalación inicial
Uno de los principales retos para implementar una aplicación es la instalación de todas las dependencias. En muchos casos, especialmente en las arquitecturas modernas de microservicios, incluso para empezar la labor de desarrollo en uno de los microservicios se requiere el despliegue de múltiples dependencias, ya sean bases de datos u otros microservicios. Aunque el despliegue de la aplicación en sí es relativamente sencillo, la tarea de identificar e implementar todas las dependencias para crear la aplicación completa es a menudo una tarea frustrante de prueba y error combinada con instrucciones incompletas o desactualizadas.
Para abordar este tema, a menudo es útil introducir una convención para describir e instalar dependencias. Esto se puede ver como el equivalente de algo como npm install, que instala todas las dependencias JavaScript necesarias. Con el tiempo, es probable que exista una herramienta similar a npm que proporcione este servicio para los usuarios de Kubernetes, pero hasta entonces la mejor práctica es confiar en la convención dentro de nuestro equipo de trabajo.
Una de las opciones a adoptar como convención es la creación del script setup.sh dentro del directorio raíz de todos los repositorios del proyecto. Es responsabilidad de este script crear todas las dependencias dentro de un espacio de nombres en particular para asegurar que todas las dependencias de la aplicación se crean correctamente. Por ejemplo, un script de instalación puede parecerse a lo siguiente:
kubectl create my-service/database-stateful-set-yaml kubectl create my-service/middle-tier.yaml kubectl create my-service/configs.yaml
A continuación, podríamos integrar este script con npm añadiendo lo siguiente a nuestro package.json:
{ ... "scripts": { "setup": "./setup.sh", ... } }
Con esta configuración, un desarrollador nuevo tiene simplemente que ejecutar npm run setup y se instalarán las dependencias en el clúster. Obviamente, esta integración particular es específica de Node.js/npm. Con otros lenguajes de programación, tendrá más sentido integrar las herramientas específicas del correspondiente lenguaje. Por ejemplo, en Java podemos integrar en su lugar el archivo Maven pom.xml.
Preparación de la fase de desarrollo activo
Una vez configurado el espacio de trabajo del desarrollador con las dependencias necesarias, la siguiente tarea es permitirle que pueda iterar de forma inmediata su aplicación. La primera condición previa para ello es que exista la posibilidad de crear y transmitir una imagen del contenedor. Supongamos que ya la tenemos configurada. Si no es así, puedes leer cómo hacerlo en otros libros y recursos en línea.
Después de que hayamos hecho built (crear) y push (subir) una imagen del contenedor, la tarea es extenderla al clúster. A diferencia de los despliegues tradicionales, en el caso de las iteraciones de los desarrolladores mantener la disponibilidad no es realmente una preocupación. Por lo tanto, la manera más fácil de desplegar un nuevo código es simplemente eliminar el objeto Deployment asociado con el Deployment anterior y, luego, crear un nuevo despliegue que apunte a la imagen recién creada. También es posible actualizar un Deployment existente, pero esto desencadenará la lógica de despliegue en el recurso Deployment. Aunque es posible configurar un Deployment para poner en marcha el código de forma inmediata, hacerlo así introduce una diferencia entre el entorno de desarrollo y el entorno de producción, lo cual puede ser peligroso o desestabilizador. Imaginemos, por ejemplo, que accidentalmente hacemos push (subir) sobre la configuración de desarrollo de Deployment a producción. De repente, y de forma accidental, desplegaremos nuevas versiones en producción sin las pruebas y retardos adecuados entre las fases de despliegue. Debido a este riesgo, y como hay una alternativa, la mejor práctica es eliminar y volver a crear el Deployment.
Al igual que sucede en la instalación de dependencias, aquí también es una buena práctica crear un script para ejecutar este despliegue. Un ejemplo de script deploy.sh podría ser el siguiente:
kubectl delete -f ./my-service/deployment.yaml perl -pi -e 's/${old_version}/${new_version}/' ./my-service/deployment.yaml kubectl create -f ./my-service/deployment.yaml
Como antes, podemos integrarlo con las herramientas del lenguaje de programación para que, por ejemplo, un desarrollador pueda simplemente ejecutar npm run deploy para desplegar su nuevo código en el clúster.
Preparación de pruebas y depuración
Después de que un usuario haya implementado con éxito la versión de desarrollo de su aplicación, necesita probarla y, si hay problemas, depurar cualquier inconveniente que aparezca con la aplicación. Esto también puede ser un obstáculo a la hora del desarrollo en Kubernetes, porque no siempre está claro cómo interactuamos con el clúster. La línea de comandos de kubectl, como herramienta para conseguirlo, es una verdadera navaja suiza, desde kubectl logs a kubectl exe y kubectl port-forward. Pero aprender a usar las diferentes opciones y conseguir familiarizarse con la herramienta puede requerir una considerable experiencia. Además, debido a que la herramienta se ejecuta en el terminal, a menudo requiere la composición de varias ventanas para examinar simultáneamente el código fuente de la aplicación y la propia aplicación en ejecución.
Para agilizar la experiencia de pruebas y depuración, las herramientas de Kubernetes se integran cada vez más en entornos de desarrollo, como por ejemplo la extensión de código abierto de Visual Studio (VS) Code en Kubernetes. La extensión se instala fácilmente de forma gratuita desde el marketplace de VS Code. Cuando se instala, descubre automáticamente cualquier clúster que ya tengamos en nuestro archivo kubeconfig, y proporciona un panel de navegación en forma de árbol para que podamos ver el contenido de nuestro clúster de un vistazo.
Además de poder ver rápidamente el estado de nuestro clúster, la integración permite al desarrollador utilizar las herramientas disponibles mediante kubectl de una manera intuitiva y reconocible. Desde la vista en forma de árbol, si hacemos clic con el botón derecho del ratón en una cápsula de Kubernetes, podemos utilizar de forma inmediata el enrutamiento de puertos para establecer una conexión de red de la cápsula directamente con la máquina local. Del mismo modo, podemos acceder a los registros de la cápsula o incluso obtener un terminal en el contenedor en ejecución.
La integración de estos comandos con las expectativas de la prototípica interfaz de usuario (por ejemplo, si hacemos clic con el botón derecho del ratón, se muestra un menú contextual), así como la integración de estas experiencias además del código de la propia aplicación, permiten a los desarrolladores con una mínima experiencia en Kubernetes ser productivos con el clúster de desarrollo en un tiempo record.
Por supuesto, esta extensión de VS Code no es la única integración entre Kubernetes y un entorno de desarrollo. Hay otras que podemos instalar dependiendo de la elección del entorno y del estilo de programación (vi, emacs, etc.).
Mejores prácticas en el establecimiento de un entorno de desarrollo
Establecer flujos de trabajo que tengan éxito es clave para ser productivos y estar satisfechos. Seguir estas mejores prácticas ayudará a asegurar que los desarrolladores estén operativos de forma inmediata:
• Podemos pensar en la experiencia del desarrollador en tres fases: incorporación, desarrollo y pruebas. Debemos tener la seguridad de que el entorno de desarrollo que creamos es compatible con estas tres fases.
• Cuando se crea un clúster de desarrollo, se puede elegir entre un clúster grande y un clúster para cada desarrollador. Hay ventajas y desventajas en cada uno de ellos, pero en general un único clúster grande es el mejor enfoque.
• Cuando añadimos usuarios a un clúster, los añadimos con su propia identidad y acceso a su propio espacio de nombres. Usamos las limitaciones de recursos para restringir la porción de clúster que pueden usar.
• Cuando administramos espacios de nombres, debemos pensar en cómo podemos recoger recursos antiguos y no utilizados. Los desarrolladores pueden tener la mala costumbre de no eliminar las cosas que no usan. Utilizamos la automatización para eliminarlas si ellos no lo hacen.
• Pensemos en los servicios a nivel de clúster como son los registros y la supervisión, que podemos configurar para todos los usuarios. A veces, las dependencias a nivel de clúster, como las bases de datos, también es útil configurarlas en nombre de todos los usuarios utilizando plantillas como los gráficos de Helm.
Resumen
Hemos llegado al punto en el que crear un clúster de Kubernetes, especialmente en la nube, es relativamente sencillo, pero conseguir que los desarrolladores utilicen de forma productiva el clúster es considerablemente menos obvio y menos fácil. Al pensar en habilitar a los desarrolladores para crear aplicaciones en Kubernetes y que estas que tengan éxito, es importante pensar en los objetivos clave entorno a la incorporación, iteración, prueba y depuración de aplicaciones. De la misma manera, vale la pena invertir en algunas herramientas básicas específicas para la incorporación de usuarios, provisión de espacio de nombres y servicios de clúster, como la agregación básica de registros. Ver el clúster de desarrollo y los repositorios de código como una oportunidad para estandarizar y aplicar de la mejor manera posible las mejores prácticas nos asegurará desarrolladores satisfechos y productivos, que creen código de forma eficaz para desplegarlo en nuestros clústeres de producción de Kubernetes.
CAPÍTULO 3
Monitorización y recopilación de registros en Kubernetes
En este capítulo, presentaremos las mejores prácticas para la monitorización y recopilación de registros en Kubernetes. Nos adentraremos en los detalles sobre las diferentes formas de monitorización y las métricas importantes a recopilar, y hablaremos de la creación de paneles a partir de estas métricas en bruto. A continuación, terminaremos con ejemplos en los que se implementa la monitorización para nuestro clúster de Kubernetes.
Métricas versus registros
En primer lugar, necesitamos entender la diferencia entre colección de registros y colección de métricas. Se complementan entre sí, pero sirven para fines diferentes.
Métricas
Series de valores numéricos medidos durante un período de tiempo.
Registros
Se utilizan para el análisis exploratorio de un sistema.
Un ejemplo en el que necesitamos utilizar tanto métricas como registros es cuando una aplicación tiene un rendimiento deficiente. Nuestra primera indicación del problema podría ser una alerta de alta latencia en las cápsulas que albergan la aplicación, pero las métricas podrían no dar una buena indicación del problema. A continuación, podríamos buscar en los registros para realizar una investigación de los errores que arroja la aplicación.
Técnicas de monitorización
La monitorización Black-box se centra en la monitorización de una aplicación desde el exterior y es el procedimiento que se ha utilizado tradicionalmente cuando se monitorizan sistemas en relación con componentes como CPU, memoria, almacenamiento, etc. La monitorización Black-box puede seguir siendo útil para la monitorización a nivel de infraestructura, pero carece de información y contexto sobre cómo está funcionando la aplicación. Por ejemplo, para probar si un clúster funciona correctamente, podríamos planificar una cápsula y, si tenemos éxito, sabemos que el scheduler (planificador) y la detección de servicios funcionan correctamente en nuestro clúster, así que podemos suponer que los componentes del clúster también funcionan bien.
La monitorización White-box se centra en los detalles, en el contexto del estado de la aplicación, como pueden ser el total de peticiones HTTP, el número de errores 500, la latencia de las peticiones, etc. Con la monitorización White-box, podemos empezar a entender el por qué del estado de nuestro sistema. Nos permite preguntar: «¿Por qué se ha llenado el disco?» y no solo «El disco se ha llenado».
Formas de monitorización
Podríamos ver la monitorización y decir: «¿Qué dificultad puede tener esto? Siempre hemos monitorizado nuestros sistemas». Sí, algunas de las formas de monitorización típicas que se aplican hoy en día también encajan con la forma en que monitoriza Kubernetes. La diferencia está en que las plataformas como Kubernetes son mucho más dinámicas y transitorias, y tendremos que cambiar nuestra forma de pensar sobre cómo monitorizar estos entornos. Por ejemplo, cuando se monitoriza un sistema virtual (VM), esperamos que VM se mantenga activo las 24 horas del día, los 7 días de la semana y que, además, preserve su estado. En Kubernetes, las cápsulas pueden ser muy dinámicas y de corta duración, por lo que necesitamos tener una monitorización que pueda manejar esta naturaleza dinámica y transitoria.
Hay un par de formas de monitorización distintas que tendremos en cuenta cuando se monitorizan sistemas distribuidos.
El método USE, popularizado por Brendan Gregg, se centra en lo siguiente:
• U: utilización
• S: saturación
• E: errores
Este método se centra en la monitorización de la infraestructura porque presenta limitaciones cuando se utiliza en la monitorización a nivel de aplicación. El método USE se describe así: «Para cada recurso, verificar la utilización, saturación y tasas de error». Este método nos permite identificar rápidamente las limitaciones de recursos y las tasas de error de los sistemas. Por ejemplo, para verificar el funcionamiento de la red en relación con los nodos en el clúster, tendremos interés en monitorizar la utilización, saturación y tasa de errores para poder identificar fácilmente cualquier cuello de botella de la red o errores que se produzcan en la pila de la red. El método USE es una herramienta que forma parte de una caja de herramientas más grande y no es el único método que utilizaremos para monitorizar nuestros sistemas.
Otro enfoque en lo que respecta a la monitorización es el llamado método RED, popularizado por Tom Wilkie. El enfoque del método RED se centra en lo siguiente:
• R: rate (tasa)
• E: errores
• D: duración
La filosofía fue adoptada de las Four Golden Signals (cuatro señales doradas) de Google:
• Latencia (cuánto tiempo se tarda en atender una solicitud)
• Tráfico (cuánta demanda hay en nuestro sistema)
• Errores (tasa de solicitudes que fallan)
• Saturación (cómo se utiliza nuestro servicio)
Por ejemplo, podríamos utilizar este método para monitorizar un servicio de frontend que se ejecuta en Kubernetes y calcular lo siguiente:
• ¿Cuántas solicitudes está procesando el servicio de frontend?
• ¿Cuántos errores 500 están recibiendo los usuarios del servicio?
• ¿El servicio está sobreexplotado por las solicitudes?
Como se puede ver en el ejemplo anterior, este método se centra más en lo experimentados que sean los usuarios y en su experiencia con el servicio.
Los métodos USE y RED son complementarios, ya que el método USE se centra en los componentes de la infraestructura y el método RED se centra en monitorizar la experiencia del usuario final de la aplicación.
Visión general de las métricas en Kubernetes
Ahora que conocemos las diferentes técnicas y formas de monitorización, veamos qué componentes deberíamos monitorizar en nuestro clúster de Kubernetes. El clúster de Kubernetes consta de los componentes del plano de control y los componentes del nodo worker (esclavo). Los componentes del plano de control son el servidor de la API, etcd, el scheduler (planificador) y el controller manager (administrador de controladores). Los nodos worker están formados por kubelet, container runtime (software de tiempo de ejecución del contenedor), kubeproxy, kube-dns y las cápsulas. Es necesario monitorizar todos estos componentes para garantizar un correcto funcionamiento del clúster y de la aplicación.
Kubernetes presenta estas métricas de varias maneras, así que echemos un vistazo a los diferentes componentes que podemos utilizar para recopilar métricas en el clúster.
cAdvisor
Container Advisor, o cAdvisor, es un proyecto de código abierto que recopila recursos y métricas de los contenedores que se ejecutan en un nodo. cAdvisor está integrado en kubelet de Kubernetes, que se ejecuta en todos los nodos del clúster. Recoge métricas de memoria y CPU a través del árbol de grupos de control (cgroup) de Linux. Si no estás familiarizado con los cgroups, es una característica del kernel (núcleo) de Linux que permite el aislamiento de recursos de CPU, E/S de disco, o E/S de red. cAdvisor también recopilará las métricas del disco a través del sistema statfs, que está integrado en el kernel (núcleo) de Linux. Estos son los detalles de la aplicación de los que realmente no tenemos que preocuparnos, pero debemos entender cómo se presentan estas métricas y el tipo de información que podemos recopilar.
Debemos considerar a cAdvisor como fuente de verdad para todas las métricas de los contenedores.
Servidor de métricas
El servidor de métricas de Kubernetes y la API de Metrics Server (servidor de métricas) sustituyen al servidor obsoleto Heapster. Heapster tenía algunas desventajas arquitectónicas derivadas de la forma en que se implementó el receptor de datos, que dio lugar a muchas soluciones comercializadas en el código base del núcleo de Heapster. Este problema se resolvió mediante la aplicación de un recurso y la API de Custom Metrics (métricas personalizadas) como una API agregada a Kubernetes. Esto permite cambiar las aplicaciones sin cambiar la API.
Hay dos aspectos que debemos entender de la API de Metrics Server y del servidor de métricas.
En primer lugar, la aplicación clásica de la API de Resource Metrics (métricas de recursos) es el servidor de métricas. El servidor de métricas recoge métricas de recursos como la CPU y la memoria. Recoge estas métricas de la API de kubelet y, luego, las almacena en la memoria. Kubernetes utiliza estas métricas de recursos en el scheduler (planificador), en el Horizontal Pod Autoscaler (autoescalador horizontal de cápsulas) (HPA) y en el Vertical Pod Autoscaler (autoescalador vertical de cápsulas) (VPA).
En segundo lugar, la API de Custom Metrics permite a los sistemas de monitorización recopilar métricas de forma arbitraria. Esto hace posible que las soluciones de monitorización puedan crear adaptadores a medida que permiten extender la monitorización más allá de las métricas de recursos básicos. Por ejemplo, Prometheus creó uno de los primeros adaptadores de métricas personalizadas, que permite usar HPA basado en una métrica personalizada. Esto abre la posibilidad a una mejor escalabilidad basada en nuestro caso de uso, ya que ahora podemos incorporar métricas, como el tamaño de la cola y la escala, basadas en una métrica que podría ser externa a Kubernetes.
Ahora que existe una API de Metrics (métricas) estandarizada, se abren muchas posibilidades que van más allá de las antiguas métricas de CPU y memoria.