Cómo modernizamos la infraestructura de Xoxoday
1. Introducción
Echando un vistazo a la infraestructura en mis primeros días de trabajo en Xoxoday, me di cuenta rápidamente de que la infraestructura era relativamente incipiente. Los múltiples cabos sueltos que observé eran indicadores potenciales de deuda técnica, estrictamente hablando en términos de configuración de la infraestructura. Los cabos sueltos incluían el diseño, la arquitectura, las herramientas, la supervisión y el registro, junto con varios procesos y prácticas.
Teniendo en cuenta la naturaleza del negocio, Xoxoday es un servicio web orientado al usuario que normalmente podría clasificarse como Software como Servicio (SaaS). Esto ejerce una gran presión sobre requisitos técnicos específicos como la disponibilidad, el tiempo de actividad, la fiabilidad, la solidez y la escalabilidad.
Un sitio web típico de comercio electrónico como Xoxoday , con un elevado tráfico global, requiere que nos adaptemos a los últimos y exclusivos patrones de diseño, arquitectura, herramientas y prácticas. Esto es para asegurar que nos mantenemos al día con la calidad de servicio esperada por la base de usuarios e idealmente superamos la misma.
Por último, otro requisito crítico que surge por razones de cumplimiento de la privacidad y la seguridad es que a la mayoría de los clientes les importa la ubicación de los datos. La expectativa habitual es que los datos residan y nunca salgan del país o región solicitados.
Esto creó la necesidad de considerar el despliegue de múltiples entornos de producción en paralelo, despliegues atómicos independientes entre sí. A esto lo llamamos policloud, aunque el término puede ser discutible. Este requisito nos obligó a contar con la automatización más avanzada, ya que ahora necesitábamos replicar la gestión y el mantenimiento de nuestra infraestructura en varias regiones.
Por lo tanto, después de una cuidadosa consideración, rápidamente nos dimos cuenta de que necesitábamos tener lo mejor de los procesos y prácticas de automatización de la infraestructura junto con una cartera de modernas tecnologías basadas en la nube para pasar a la nube nativa y proporcionar el mejor servicio y experiencia a nuestros usuarios.
Lo bueno para nosotros fue que los desarrolladores ya habían dividido sus servicios en microservicios. Eso nos ayudó a avanzar rápidamente en la adopción de las últimas tendencias y prácticas de infraestructura.
2. Necesidad de automatización
La automatización de servicios en la nube ha sido un tema candente en los últimos años, que ha dado lugar a la aparición de muchas herramientas y tecnologías excelentes. Nuestras necesidades venían determinadas por la naturaleza de la empresa y sus exigencias. Para nosotros, los siguientes factores eran cruciales:
- Alta disponibilidad
- Autoescalado
- Autocuración
Además, la automatización brinda la oportunidad de conseguir la infraestructura como código, lo que nos permite interactuar con la infraestructura al tiempo que aprovechamos las fantásticas ventajas de los sistemas de control de versiones.
Esto supuso un gran cambio de paradigma, ya que nos permitió examinar la infraestructura de forma proactiva y reducir las interacciones reactivas (imperativas). Facilitó las reversiones y nos ayudó a realizar un seguimiento eficaz de los cambios en toda la infraestructura. Además, la automatización hizo que nuestros equipos de DevOps pudieran descansar en paz a horas intempestivas.
Otra ventaja añadida de la automatización es que abre varias vías para modernizar y mejorar la productividad de los desarrolladores. Profundizaremos en este tema concreto en la siguiente sección sobre entrega continua.
3. Integración, entrega y despliegue continuos
Los lanzamientos pueden convertirse en una dolorosa pesadilla si no se dispone de los canales, herramientas y directrices adecuados. Esto significaba que la columna vertebral de DevOps eran las canalizaciones CI/CD que nos ayudaban a probar, integrar, construir y desplegar nuestro código en producción.
Nuestro objetivo era poder realizar varias implantaciones de producción en un mismo día y sin problemas. Si se estropeaba, queríamos revertir las actualizaciones sin problemas y con rapidez.
Como ya hemos mencionado, nuestros desarrolladores habían hecho un trabajo fantástico al descomponer los servicios monolíticos en microservicios, lo que nos abrió las puertas al mundo de la nube nativa. Sin embargo, esto conllevaba sus propios retos. El mayor reto al que nos enfrentamos fueron las pruebas de integración.
Además, nuestra arquitectura y algunas excelentes herramientas nativas de la nube nos permitieron abordar el desarrollo de software de forma innovadora. Trastocábamos las convenciones tradicionales para adaptarnos al nuevo y apasionante mundo de los microservicios.
Comenzar con la práctica bien establecida de tener múltiples entornos nos permitió probar y escudriñar nuestra cartera de características antes de desplegarla en producción. Una vez que terminaban de trabajar en sus características, errores o mejoras particulares, nuestros desarrolladores abrían un pull request en la rama "dev" de git.
Una vez aprobado el PR, los commits se fusionaron en la rama de desarrollo. Habíamos proporcionado un mecanismo de activación manual para desplegar la rama de desarrollo a los desarrolladores. ( que exploraremos más adelante)
El código base de esta rama se desplegó en el entorno de desarrollo, un entorno dedicado a que los desarrolladores probaran sus funciones más avanzadas. Había dos entornos de desarrollo, uno para que los desarrolladores probaran sus funciones y microservicios y otro para las pruebas de integración.
Una vez probada la base de código y aprobada por el equipo de control de calidad, los commits se fusionaron en la rama "staging o uat". A partir de aquí, la base de código se desplegó (activada manualmente) en el entorno Staging, que nos esforzamos por mantener lo más cerca posible de producción.
Había otro par de ojos en Staging antes de considerar qué características y microservicios se enviarían a producción. Teníamos un entorno diferente, el entorno de demostración, reservado únicamente para hacer demostraciones de nuestros servicios a clientes potenciales interesados en explorar nuestros servicios.
Estudiamos y analizamos las mejores herramientas posibles para CI/CD y, finalmente, nos decidimos por Github Actions. En resumen, Github Actions nos proporcionó un entorno integrado que se integra a la perfección con el flujo de trabajo de GitHub y ofrece una integración mínima para los equipos de desarrolladores.
Junto con la estrecha integración, también nos proporcionó los mecanismos CI/CD más avanzados. Es súper rápido, asequible, fácil de configurar y personalizar. Y tenía una sintaxis basada en YAML súper fácil de definir los trabajos que nos permite confirmar la definición de CI / CD con el código base y el seguimiento con la maravilla de git.
a. Integración continua
Exploramos algunas formas de activar las compilaciones automáticamente, pero rápidamente nos dimos cuenta de que es mejor dejar que los desarrolladores controlen cuándo compilar. Podría haber muchas razones a favor o en contra del despliegue automático y el despliegue manual.
Es mejor alinear las expectativas de los equipos y tomar esta decisión en función de la conveniencia, ya que un exceso de automatización puede acarrear problemas.
Para nuestros entornos no productivos, teníamos un disparador manual para compilar y desplegar las últimas compilaciones en los entornos respectivos. En nuestros entornos de producción, las compilaciones se activaban automáticamente al insertar una etiqueta git. Las etiquetas git nos permitían ceñirnos a un esquema de versiones y, en caso necesario, hacer retroceder las versiones en producción.
Dejando de lado las pruebas unitarias y las pruebas funcionales que los desarrolladores ejecutaron en sus commits, vamos a centrarnos en el lado de la infraestructura de las cosas en nuestro CI/CD. El trabajo de construcción, una vez desencadenado, básicamente se autentica con ECR, gestiona la caché de imágenes y ejecuta el comando docker build para crear una imagen de contenedor.
Esta imagen contenedora se envía al repositorio ECR correspondiente. Se trata de una configuración sencilla pero extremadamente potente y flexible, ya que nos permite ceder el activador a los desarrolladores al tiempo que automatizamos todo el proceso.
b. Despliegues continuos
Teníamos un disparador manual proporcionado a los desarrolladores para desplegar las últimas compilaciones en los entornos respectivos. Utilizamos etiquetas de imagen de Docker para distinguir entre las imágenes de contenedor para la rama git individual y el entorno.
Nuestros microservicios se ejecutan en Kubernetes. Una tarea de despliegue típica hace lo siguiente:
- Actualización de los archivos de configuración y las variables: eliminamos los mapas de configuración existentes y los volvemos a crear, ya que Kubernetes no actualiza un mapa de configuración existente. Nuestros archivos de configuración se envían a un repositorio git privado y la información confidencial se almacena en AWS Secrets Manager. Por lo tanto, git nos permite realizar un seguimiento de los archivos de configuración, lo que nos facilita la eliminación y recreación de los mapas de configuración en Kubernetes.
- Actualizar el despliegue y el servicio en Kubernetes (en caso de que haya cambios en los archivos Yaml).
- Y por último, hacer un despliegue kubernetes rollout porque Kubernetes no actualiza automáticamente los despliegues/pods sólo para los cambios en los archivos de configuración (configmaps).
Estábamos utilizando los siguientes recursos de la comunidad de acciones de GitHub para ayudarnos a conectar, autenticar y comunicarnos fácilmente con EKS/ECR y Docker.
- Cancelar ejecuciones anteriores
- Configurar las credenciales de AWS
- Acceso a AWS ECR
- Configuración de Docker Buildx
- Comprobar otro repositorio
- Kubectl Acceso a AWS EKS
4. Arquitectura
Utilizamos AWS para nuestras necesidades de infraestructura. AWS nos proporciona un rico ecosistema de servicios que ejecuta y administra de manera eficiente nuestra flota de microservicios y el backend y middleware relacionados".
Estos microservicios se ejecutan sobre Kubernetes administrado por AWS EKS. La oferta de AWS EKS nos ahorra mucho tiempo y esfuerzo que, de otro modo, Xoxoday emplearía en administrar el ciclo de vida del propio clúster de Kubernetes. Eso nos permite aprovechar el ecosistema nativo de la nube mientras nos centramos en nuestros servicios y trabajamos en ellos".
Kubernetes aporta una década de experiencia en la gestión de la infraestructura global de Google. Nos proporciona varias características. Por nombrar algunas: Autoreparación, autoescalado, alta disponibilidad al tiempo que consolida y aumenta la eficiencia con la que utilizamos nuestra infraestructura subyacente. Utilizamos la entrada para exponer nuestros servicios al mundo exterior a través de una mezcla de equilibradores de carga clásicos y de aplicaciones y la automatización de la entrada DNS de route53.
Estos servicios se conectan a nuestro backend, que está alojado en una mezcla de servicios administrados de AWS como Kafka administrado, RDS e instancias EC2 autoalojadas. Estamos aprovechando Terraform para la configuración automatizada de EKS, una mezcla única de Terraform y plantillas de lanzamiento de AWS para generar las instancias EC2 y administrarlas automáticamente.
Además, SaltStack se utiliza para maniobras complejas de gestión de la configuración automatizada de la flota de instancias EC2. Nos permite gestionar automáticamente, actualizar y mantener el sistema operativo y los servicios que se ejecutan en la parte superior. SaltStack tiene un conjunto de características robustas y una arquitectura brillantemente diseñada, flexible y conectable. A continuación, aprovisionamos automáticamente los servicios y tomamos la nueva configuración del backend (direcciones IP, etc.) y rellenamos los archivos de configuración de kubernetes, actualizamos los configmaps y los desplegamos en el entorno.
Esto nos permite configurar y reconfigurar los servicios sin estado que se ejecutan en el entorno de Kubernetes para que se aprovisionen y configuren automáticamente con los cambios dinámicos en la configuración del backend. Nuestras imágenes de contenedor se almacenan en AWS ECR, ya que nos proporciona otro servicio fantástico que se integra a la perfección con nuestro conjunto de tecnologías y arquitectura.
5. Registro, supervisión, alerta y APM
Estaríamos ciegos si no dispusiéramos de los mecanismos de retroalimentación adecuados para comprender lo que ocurre en nuestra infraestructura, especialmente si hablamos de un escenario Polycloud.
a. Elasticsearch/Fluentd/Kibana
Utilizamos la exclusiva pila EFK para nuestros problemas relacionados con los registros. Esto nos permite crear y mantener un conjunto dinámico de microservicios que se ejecutan en contenedores y máquinas virtuales, a la vez que conservamos los registros en un canal centralizado.
Esto nos permite acceder a los registros de instancias/contenedores que se destruyen por diversas razones. Y eso hace que sea más fácil para nosotros para acceder con sensatez y hacer frente a múltiples preocupaciones en la producción. Aquí hay otra alternativa elasticsearch para que usted vaya para.
b. Grafana/Prometheus
El panel de control de Grafana, junto con la base de datos de series temporales de Prometheus, nos permite conservar varios detalles sobre nuestra infraestructura como la CPU/RAM/Almacenamiento, etc. y nos mantiene actualizados con el estado actual de la infraestructura casi en tiempo real.
Eso nos permite implementar mecanismos de alerta, facilitando a nuestro equipo de DevOps la gestión de incidentes en nuestra infraestructura. A pesar de todos los preparativos y de la mejor arquitectura y diseño, las cosas pueden romperse. Los sistemas Grafana Prometheus, junto con Alertmanager, permiten mitigar los incidentes en producción.
Además, esta pila se beneficia de la Gestión de Programación de Aplicaciones, que nos proporciona un rico conjunto de métricas que nos da una visión crítica de los productos, su uso y más.
6. Alteración de los entornos de desarrollo
Como caminábamos por la senda de los microservicios, teníamos que gestionar más de 40 microservicios que, orquestados entre sí, formaban nuestro servicio web www.xoxoday.com. Esto implica una gran cantidad de comunicación entre estos microservicios, y el gran número de ellos hace que sea imposible y poco práctico ejecutarlo localmente en la máquina del desarrollador.
De ahí que tuviéramos que ponernos creativos y volver a adentrarnos en las profundas aguas del mundo nativo-nube para encontrar una posible solución. Vimos muchas, por nombrar algunas:
- Skaffold
- Kubefwd
Actualmente, nuestra favorita y la más útil para nuestras necesidades es Kubefwd, pero puede que exploremos otras herramientas en el futuro. ¡Kubefwd nos permite hacer que los servicios de Kubernetes sean accesibles en la estación de trabajo local utilizando el mismo DNS como si la máquina del desarrollador local se encuentra dentro del clúster de Kubernetes!
Esto supone un increíble aumento de la productividad que hace que los desarrolladores sean más eficientes, al tiempo que mejora la experiencia general y reduce el tiempo que se tarda en lanzar una función al mundo.
7. Conclusión
Tras meses de analizar nuestras necesidades de infraestructura y alinearlas con los requisitos de la empresa, el crecimiento futuro y las tendencias actuales en el mundo de la infraestructura y la nube nativa, por fin hemos llegado a un punto en el que podemos esperar con confianza unos días increíbles.
Nuestra infraestructura no solo ha dado un salto evolutivo de una década, sino que también ha conseguido reducir la carga de los equipos de DevOps al tiempo que aumentaba drásticamente la productividad de los desarrolladores. Por fin, nuestra configuración es más barata y hace más, ¡y lo hace mejor!
Esta configuración nos permite replicar fácilmente todo nuestro conjunto de servicios, frontend y backend y configurarse automáticamente en pocos minutos. Esto nos permitirá crear múltiples entornos de producción atómicos e independientes, adaptarlos a nuestras canalizaciones CI/CD y gestionar y mantener automáticamente la compleja configuración con facilidad.
Además, el beneficio más importante de todo esto es que nuestros equipos de DevOps pueden seguir siendo pequeños y escalar linealmente, al tiempo que proporcionan una escala global exponencial para nuestros servicios. Y la guinda del pastel es que el equipo de DevOps puede disfrutar de noches tranquilas y fines de semana fantásticos sin tener que combatir los problemas de producción de forma reactiva, ya que nuestro diseño y arquitectura nos permiten abordar estos retos de forma proactiva.
Una de las cosas fantásticas de nuestra configuración es que la naturaleza automatizada nos permite utilizar instancias puntuales para nuestros sistemas no críticos y de no producción. Esto nos ayuda a optimizar y consolidar aún más los costes, al tiempo que disponemos de una amplia potencia informática. ¡Esto significa configuraciones altamente rentables, y si en caso AWS decide cerrar nuestras instancias spot, no hay problema, en pocos minutos, vamos a auto-escalar nuestras instancias dedicadas y hacer lo mejor de ambos mundos!
Las tendencias actuales en el mundo de las infraestructuras, sobre todo si tenemos en cuenta los desarrollos Cloud Native, hacen que sea una década apasionante para DevOps; parece que estamos cerca de alcanzar el nirvana. El gran número de herramientas, soluciones, servicios y ecosistemas que están apareciendo puede resultar abrumador en ocasiones. Sin embargo, en retrospectiva, nos dirigimos hacia una era emocionante de servicios web sólidos como una roca, robustos y siempre disponibles, al tiempo que proporcionan una experiencia fantástica a los usuarios.