¿En tu organización se han topado con este escenario?
Un científico terminó de desarrollar una herramienta o pipeline analítico, pero nadie en el equipo sabe a ciencia cierta cómo llevarlo a producción de una manera que simplifique el despliegue automatizado, que permita que los recursos de cómputo detrás del pipeline escalen (hacia arriba y hacia abajo) con un uso optimizado en costos, y que garantice resiliencia ante fallas inesperadas de hardware o software, todo ello facilitando el despliegue de actualizaciones y la baja de versiones obsoletas.
Con tantas preguntas críticas de ingeniería sin resolver, ¿cómo puede tu equipo científico pasar de una mentalidad de pruebas y desarrollo en un entorno académico a la de construir herramientas científicas verdaderamente confiables y a escala de producción, minimizando además las horas-hombre necesarias para montar y mantener un sistema así?
¿Te interesaría correr este tipo de workloads en GCP o AWS para aprovechar la escala global, la altísima confiabilidad de la infraestructura y la rentabilidad que ofrecen estas plataformas frente a los clusters de cómputo on-prem?
¿Quizá también te gustaría una explicación de los principios fundamentales de DevOps detrás de cómo correr este tipo de workload en la nube?
Voy a apoyarme en una demo detallada para explicarte cómo aprovechar un ejemplo funcional, tanto para Google Cloud Platform (GCP) como para Amazon Web Services (AWS), que muestra la ejecución de un workload real de cómputo científico (bioinformática) aplicando varios principios modernos de DevOps. Sin más preámbulo, manos a la obra.
A grandes rasgos, el código de ejemplo:
- Muestra cómo empaquetar herramientas comunes de bioinformática (FastQC y BWA) en imágenes de contenedor y subirlas al repositorio de imágenes de cada nube
- Levanta (y permite desmontar rápidamente) toda la infraestructura cloud necesaria para correr workloads mediante Terraform, una herramienta de Infrastructure as Code
- Despliega un pipeline de workflow común basado en estas imágenes y en la infraestructura cloud levantada, sobre el servicio totalmente administrado de Kubernetes de cada nube, un sistema de gestión y orquestación para la ejecución de contenedores. Se utilizará Argo para orquestar un workflow / pipeline de tareas en un cluster de Kubernetes
Al usar Docker, Terraform, Kubernetes y Argo, aprenderás a:
- Levantar, actualizar y desmontar la infraestructura necesaria para correr tus workloads con comandos sencillos
- Ejecutar workloads analíticos end-to-end dirigidos por DAGs, con reintentos automáticos de pasos individuales si se topan con errores inesperados o fallas de infraestructura
- Centralizar el logging y el monitoreo de métricas de los workloads en herramientas nativas de la nube para explorar logs y métricas
- Correr workloads sobre una infraestructura que escala automáticamente hacia arriba (con capacidad a escala global) y hacia abajo (con muy pocos o ningún recurso de cómputo) según haga falta, de modo que solo pagas por los recursos de CPU/RAM/almacenamiento que tus tareas realmente necesitan. Atrás quedaron los días de dejar servidores en idle e inflar la factura sin necesidad
- Desplegar nuevas versiones de software sin fricción y retirar progresivamente el uso de versiones antiguas
El código base que ilustra estos principios está disponible aquí, y la Parte 2 explica cómo aprovecharlo. Si quieres saltarte el repaso de principios de DevOps e ir directo al código, puedes dejar de leer esta Parte 1.
Ahora bien, si lo que quieres es un recorrido guiado por las tecnologías que hacen posibles estos objetivos de DevOps, quédate conmigo. Te recomiendo prepararte un café cargado: lo vas a necesitar para llegar al final de este artículo detallado.
A continuación te presento un curso intensivo sobre DevOps, contenedores, Kubernetes y Terraform, y por qué su uso es clave para el uso real del software, sea bioinformático o de cualquier otro tipo.
Contenedores: despliegue escalable de herramientas
El problema central que resuelven este artículo y su código base es el de DevOps, o Development Operations. Dockerización, Kubernetes y Terraform trabajan en conjunto hacia el mismo objetivo: simplificar el despliegue confiable de programas a escala. Para entender los fundamentos del DevOps moderno hace falta una comprensión básica de los contenedores. Empecemos por ahí.
La mayoría de los bioinformáticos conoce muy bien este problema: quieres usar un programa open-source, probablemente creado en el ámbito académico, con dependencias desactualizadas. La instalación se convierte en una pesadilla de gestión de paquetes, ya que los requisitos de versión para Python, Perl y otras dependencias entran en conflicto con las versiones actualizadas que tienes instaladas localmente.
Seguramente le has dado la vuelta a esto creando entornos virtuales con Anaconda o con el comando venv de Python. Pero, lamentablemente, ese enfoque tiene sus puntos débiles. En algún momento, esa forma de trabajar se vuelve insostenible y más difícil de mantener de lo que vale la pena. Los entornos virtuales pueden ser aceptables para desarrollo y pruebas, pero a escala simplemente no son sostenibles.
He visto a muchas empresas, pequeñas y grandes, descubrirlo por las malas. Aguantaron demasiado tiempo antes de tirar la toalla.
Llega la contenerización
Un contenedor es un proceso de software aislado que incluye todo el código y las dependencias necesarios para ejecutar la aplicación rápidamente, con un rendimiento cercano al bare-metal, en cualquier entorno de cómputo.
Una imagen de contenedor/Docker es la definición de cómo debe construirse y ejecutarse un contenedor. Los contenedores son procesos que ejecutan las instrucciones empaquetadas dentro de las imágenes de contenedor.
Una imagen de contenedor se crea con un sencillo Dockerfile que define:
- El SO base sobre el que se construye la imagen (esto no impide que un contenedor basado en esa imagen se ejecute sobre otros SO host)
- Los paquetes a instalar
- Los comandos de shell que se ejecutan cuando se inicia un contenedor basado en la imagen
Este Dockerfile lo usa docker build para crear una imagen inmutable.
¿Cómo se logra redundancia y escalado horizontal para el servicio empaquetado en la imagen?
Se consigue desplegando instancias ejecutables de una imagen de contenedor a través de múltiples contenedores corriendo en múltiples hosts ubicados en distintos data centers, y balanceando la carga entre ellos. Dada la escala global de los proveedores cloud, el potencial de escalabilidad horizontal y de resiliencia ante fallas con contenedores es prácticamente ilimitado.
Podrías, por ejemplo, crear una imagen de contenedor que empaquete una herramienta como FastQC junto con todas sus dependencias. Los contenedores lanzados a partir de esa imagen de FastQC pueden desplegarse en prácticamente cualquier entorno de cómputo, a la escala que sea, sin importar el hardware, el software ni el SO de la máquina host. Una verdadera bendición.
Ese contenedor se ejecuta de forma muy similar a como se ejecuta FastQC por sí solo. Por ejemplo, es muy directo pasarle archivos de entrada a un contenedor de FastQC y obtener los archivos de salida.
Cada proveedor cloud tiene su propio container registry para hacer push y pull de imágenes. Funciona de manera similar al push y pull de ramas en un repositorio git. Por ejemplo, ejecutas docker pull <image_name> para descargar una imagen de FastQC alojada en el repositorio de contenedores de tu nube. Esto te permite descargar la imagen en distintos recursos de cómputo de tu entorno cloud, dejando a FastQC listo para correr de inmediato con docker run <image_name>, sin tener que recurrir a métodos enrevesados para gestionar la instalación de dependencias.
La similitud con los repos de git no termina ahí. A los contenedores también se les pueden aplicar tags, como latest o v1.0.1, para llevar registro de qué imagen corresponde a cada versión específica del código base. docker run <image_name>:<tag_name> te permite descargar y ejecutar un contenedor basado en una versión concreta de la imagen.
Contenedores vs. Máquinas Virtuales (VMs)
Si te parece que los contenedores se parecen mucho a las máquinas virtuales —que también empaquetan software y lo hacen ejecutable en una amplia variedad de entornos de cómputo—, quizá te preguntes en qué se diferencian unos de otras y por qué los contenedores son los preferidos para DevOps escalable.
- Tanto los contenedores como las VMs pueden correr en un mismo host, pero los contenedores lo hacen sin el fuerte golpe al rendimiento que sufren las VMs por ejecutar su propio sistema operativo además del software empaquetado. Los contenedores, en cambio, comparten el SO del host, lo que los hace ligeros; típicamente verás una degradación de rendimiento cercana al 0,5 %.
Con una máquina virtual sobre un hipervisor moderno, tendrás suerte si la degradación de rendimiento se queda en un 1-3 %. A mayor escala, esas diferencias tienen un impacto significativo y tangible en el costo.
- Los contenedores son mucho más rápidos de construir y desplegar. Habilitan un proceso de desarrollo Agile que no existe cuando se trabaja con VMs, que pueden tardar minutos u horas en construirse.
- Los contenedores son inmutables y se definen en un único archivo, lo que vuelve más confiable la integración continua del software. Por ejemplo: puedes hacer rollback a una versión anterior y conocida del software cuando lo necesites, con la tranquilidad de saber que no pudo haber sido alterada desde el último despliegue.
- Los contenedores fomentan una práctica probada de la arquitectura de software: favorecer microservicios distribuidos y débilmente acoplados frente a aplicaciones monolíticas. Esto agiliza el despliegue cuando hay que sacar un fix o una nueva funcionalidad, y evita que un único problema en una aplicación monolítica tumbe a la vez todos los demás aspectos de la aplicación que no tenían por qué estar fuertemente acoplados.
Lo ideal es que cada programa desplegado a escala viva en su propia imagen de contenedor, de modo que el número de contenedores desplegados de ese programa pueda escalar hacia arriba y hacia abajo de manera independiente para responder a la demanda de recursos.
- Los contenedores facilitan el logging y el monitoreo de métricas tanto a nivel de SO como de aplicación, ya que comparten los recursos del SO del host y fueron diseñados pensando en una captura detallada de logs y métricas.
Gestión de la ejecución de contenedores
Supongamos que ya contenerizaste un programa como FastQC y lo etiquetaste con los tags adecuados, como latest y v0.11.9. ¿Qué hacemos con eso, no solo para habilitar, sino para simplificar enormemente la ejecución tolerante a fallas y escalable de FastQC?
Llega Kubernetes
Kubernetes (a menudo abreviado como K8s) es una plataforma open-source creada por Google en 2014 como un planificador de jobs y sistema de gestión de clusters open-source, pensado para facilitar la adopción comunitaria de la gestión automatizada de workloads contenerizados. Ha ganado una popularidad enorme por la relativa facilidad con la que habilita operaciones a escala global.
Kubernetes es hoy el segundo repo de GitHub más popular en términos de autores e issues, solo por detrás del kernel de Linux.
La documentación de K8s ofrece una explicación excelente, aunque extensa, de por qué Kubernetes es tan útil. Para resumir lo mejor de K8s en pocas palabras, K8s permite:
- Bin packing automático: le entregas a Kubernetes un cluster de nodos (servidores cloud) que puede usar para correr tareas contenerizadas. Le indicas a Kubernetes cuánto CPU, memoria y almacenamiento necesita cada contenedor, y Kubernetes los acomoda en tus nodos para optimizar el uso de recursos.
- Auto-recuperación: Kubernetes reinicia los contenedores que fallan (por errores de software o fallas de hardware), reemplaza contenedores, mata aquellos que no responden al health check definido por el usuario y no los anuncia a los clientes hasta que estén listos para servir.
- Service discovery y balanceo de carga: Kubernetes puede exponer un contenedor mediante un nombre DNS o su propia dirección IP. Si la carga de tráfico es alta, puede balancear y distribuir el tráfico de red entre múltiples instancias de ese contenedor para que el deployment se mantenga estable a escala.
- Rollouts y rollbacks automatizados: puedes describir el estado deseado de tus contenedores desplegados con Kubernetes, y este lleva el estado real al estado deseado a un ritmo controlado. Por ejemplo, puedes automatizar a Kubernetes para que cree nuevos contenedores en tu deployment (p. ej., contenedores de tu software etiquetados como ‘v2’), elimine los existentes (p. ej., los etiquetados como ‘v1’) y traspase todos sus recursos de cómputo al nuevo contenedor. Esa migración puede hacerse de una sola vez o personalizarse —por ejemplo, reemplazando un 5 % de los contenedores existentes cada cinco minutos. Si notas un aumento en la tasa de errores mientras se está desplegando el nuevo contenedor, hacer rollback del nuevo deployment es tan sencillo como ejecutar un único comando.
Kubernetes totalmente administrado
Te alegrará saber que hoy Kubernetes se ofrece como un servicio totalmente administrado por los principales proveedores cloud. Entre ellos están:
- GKE (Google Kubernetes Engine) en Google Cloud
- EKS (Elastic Kubernetes Service) en Amazon Web Services
Estas ofertas simplifican la instalación de Kubernetes y el aprovisionamiento de hardware, abstrayendo la creación del cluster. Te permiten concentrarte en lanzar workloads escalables sobre un cluster de K8s que se crea en cuestión de minutos y con unos pocos clics en la consola.
Tanto GKE como EKS cuentan con una máquina cluster control plane serverless. Funciona como una especie de nodo maestro, junto con varias máquinas worker llamadas nodes. Tú envías contenedores para ejecución al control plane, que luego los planifica para correr en los nodes. Cuando se ejecutan en los nodes, esas tareas se conocen como pods.
En resumen: los pods (típicamente un solo contenedor) corren en los nodes, y el escalado del número de pods lo controla la máquina del control plane. Las ofertas totalmente administradas de K8s también automatizan la configuración del auto-scaling de nodes (el auto-scaling viene incorporado en GKE; en AWS, EKS crea las plantillas de EC2 Auto Scaling). En definitiva, con GKE y EKS se logra el auto-scaling tanto de pods como de nodes con un esfuerzo mínimo.
Al configurar un cluster de K8s, conviene también definir node groups opcionales, es decir, la familia de recursos de hardware que se usará en tu cluster. Es habitual especificar node groups al enviar workloads de cómputo científico.
Por ejemplo, podrías crear un node group ‘high cpu’ que use hardware de familias de máquinas con mucha CPU u optimizadas en costo de CPU, como la familia c2 en GCP o la c5 en AWS, y asignar a ese node group los workloads CPU-intensivos. Podrías tener un node group ‘GPU’ aparte que use la familia a2-highgpu en GCP o la p3 en AWS, al cual se envíen los workloads que utilizan GPU. Operando así, los distintos tipos de máquina pueden escalar de manera independiente entre sí, en función de los workloads que deben correr en cada uno.
Eso sí, si usas el modo Autopilot de GKE en lugar del modo Standard, no hace falta que especifiques node groups para tu cluster. El aprovisionamiento de hardware queda abstraído, ya que GKE Autopilot lidera la industria DevOps al llevar el ecosistema de K8s hacia un enfoque más serverless para la computación a escala global. Con GKE Autopilot, simplemente envías tu job de K8s —en el que se definen los requisitos de CPU/RAM/almacenamiento del contenedor— y GKE escala los recursos de cómputo entre bambalinas según haga falta para ejecutar esa tarea según tus especificaciones. ECS Fargate es el equivalente de AWS a GKE Autopilot.
Infraestructura reproducible y automatizada
El nexo entre la contenerización y la ejecución robusta de contenedores en Kubernetes es Terraform, una herramienta de software open-source de infrastructure as code que permite definir, con texto YAML fácil de leer, el estado deseado de tu infraestructura cloud. Con un único comando puedes crear, actualizar o desmontar tu infraestructura cloud.
Mientras los contenedores hacen que las versiones de software sean inmutables, fáciles de reproducir y fáciles de escalar, Terraform hace que la infraestructura cloud sobre la que corren esos contenedores sea fácil de replicar, actualizar o eliminar, además de totalmente versionable.
Al mover tu software a contenedores, tu sistema de gestión de workloads a Kubernetes y codificar tu infraestructura cloud con Terraform, sentarás las bases de una empresa capaz de escalar desde el modo stealth hasta operaciones globales, y de recuperarse rápidamente de fuentes habituales de fallas —como releases con bugs e incidencias de hardware— con muy pocos cambios fundamentales en tu ecosistema DevOps.
Pon en práctica lo aprendido
Gracias por tu tiempo: espero de verdad que este artículo te haya ayudado a entender mejor el DevOps. Si quieres empezar a aplicar de forma práctica las habilidades que vimos siguiendo un código base de demo funcional, continúa con la última entrega de esta serie de dos partes.
¡Gracias por leer! Para mantenerte al tanto, síguenos en el DoiT Engineering Blog , en el canal de DoiT en LinkedIn y en el canal de DoiT en Twitter . Para conocer las oportunidades laborales, visita https://careers.doit-intl.com .