La Sinfonía DevOps: Orquestando una Aplicación Completa
- Fecha: 21 de septiembre de 2025
- Versión: 1.0
- Autor: Rubén Darío Cabrera García
¿Alguna vez ha sentido que gestionar una aplicación moderna es como intentar dirigir una orquesta sin partitura? Entre un backend en Python, un frontend en React, una base de datos PostgreSQL y una cola de mensajes RabbitMQ, el caos potencial es inmenso. Multiplique esa complejidad por los distintos entornos que debe gestionar —local para desarrollo, lab para pruebas y prod para producción— y la cacofonía puede volverse ensordecedora. Sin una dirección clara, cada componente toca a su propio ritmo, llevando a inconsistencias, errores y despliegues fallidos.
El objetivo de esta entrega es darle esa partitura. Le guiaremos, paso a paso, en la construcción de un flujo de trabajo de despliegue completamente automatizado, consistente y fiable. Adoptaremos los principios de GitOps y un ecosistema de herramientas de primer nivel para transformar el caos en una sinfonía perfectamente coordinada. • Parte 1: La Fundación - Construiremos el escenario con Infraestructura como Código (IaC) usando Terraform y Terragrunt, garantizando que cada entorno sea idéntico y reproducible. • Parte 2: Domando el Caos - Con el escenario listo, subiremos a los tramoyistas. Usaremos Ansible para configurar nuestros servicios, asegurando que cada base de datos y cola de mensajes esté perfectamente preparada para la aplicación. • Parte 3: El Gran Final - Es la hora del espectáculo. Desplegaremos nuestra aplicación con un flujo GitOps completo, donde ArgoCD, Helm y GitLab CI se aseguran de que el estado en producción sea siempre un reflejo exacto de lo definido en Git.
Nota: Todo el código fuente está disponible en el repositorio todo-aws.
¿Qué es GitOps?
GitOps es una metodología DevOps que utiliza Git como la única fuente de la verdad para todo el estado de su sistema. No es simplemente una herramienta, sino un enfoque fundamental que transforma la manera en que gestionamos infraestructura, configuración y aplicaciones. En lugar de comandos manuales o scripts ad-hoc, GitOps establece que:
- Todo cambio al sistema debe ser propuesto a través de Git
- Git contiene la descripción declarativa de cómo debe ser el sistema
- Un agente automatizado (como ArgoCD) se encarga de hacer que la realidad coincida con esa descripción
Este enfoque resuelve los problemas clásicos de los flujos de trabajo tradicionales:
Problema tradicional | Solución GitOps |
---|---|
Despliegues manuales → Error humano, inconsistencias | Despliegues automatizados → Solo Git decide qué se despliega |
Deriva de configuración → Entornos divergen con el tiempo | Reconciliación continua → Sistema se auto-repara constantemente |
Rollbacks complejos → Requerir intervención manual experta | Rollbacks instantáneos → Solo un git revert |
Falta de auditoría → No se sabe quién hizo qué cambios | Auditoría completa → Todo en el historial de Git |
Accesos privilegiados → Credenciales en múltiples sistemas | Seguridad mejorada → Solo Git necesita acceso al clúster |
flowchart LR A[Desarrollador hace cambios] --> B[Commit en Git] B --> C[GitLab CI ejecuta tests y builds] C --> D[Push a repositorio Git] D --> E[ArgoCD detecta cambio en Git] F[ArgoCD reconcilia clúster con estado deseado] F --> G[Clúster actualizado automáticamente] G --> H[Si alguien modifica manualmente el clúster<br>ArgoCD lo restaura al estado deseado]
Los 5 pilares fundamentales de GitOps
-
Declarativo y Versionado: Todo el estado del sistema (infraestructura, configuración, aplicaciones) se describe de forma declarativa y se almacena en Git. Cada cambio es un commit auditable.
-
Automatizado y Deseado: Los cambios que se aprueban y fusionan en Git se aplican automáticamente al sistema en vivo. No hay despliegues manuales.
-
Reconciliación Continua: Un agente automatizado (como ArgoCD) se ejecuta en el clúster y compara continuamente el estado real con el estado deseado en Git, corrigiendo cualquier desviación o “deriva” de forma automática.
-
Cerrado por Prueba: Todos los cambios al estado deseado se proponen a través de pull/merge requests. Esto impone una revisión por pares y permite la ejecución de validaciones automáticas antes de que un cambio sea aprobado.
-
Pull-Based: Los cambios son “tirados” por el clúster desde Git, no “empujados” desde un sistema externo. Esto reduce riesgos de seguridad al eliminar credenciales de despliegue en los sistemas de CI/CD.
Introducción General a la Serie
En la era del desarrollo ágil y la nube, la complejidad de las aplicaciones modernas ha crecido exponencialmente. Hoy en día, una aplicación típica incluye elementos similares al de este proyecto:
- Backend: Servicios en Python/Node.js con microservicios
- Frontend: Aplicaciones React/Vue con SPA
- Base de datos: PostgreSQL/MySQL con replicación
- Cola de mensajes: RabbitMQ/Kafka para comunicación asíncrona
- Infraestructura: Kubernetes en AWS/GCP con múltiples clústeres
La verdadera magia no está en las piezas individuales, sino en cómo se orquestan juntas de manera coherente. Vamos a dividir esta entrada en 3 partes importantes, transformaremos este caos en una sinfonía perfectamente coordinada usando:
- Infraestructura como Código (IaC) con Terraform y Terragrunt
- Automatización de configuración con Ansible y Molecule
- GitOps completo con ArgoCD, Helm y GitLab CI
La aplicación que exponemos en este blog es una aplicación de ejemplo que utiliza las tecnologías mencionadas anteriormente. Esta aplicación está diseñada para demostrar cómo se pueden implementar estos principios en una aplicación real.
Nota: Normalmente la infraestructura, configuración y código fuente se gestionan en repositorios separados, pero para este ejemplo, se han combinado en un único repositorio para simplificar la demostración.
Este enfoque no solo garantiza consistencia entre entornos, sino que crea un sistema autoreparable, auditado por diseño y listo para escalar. Pero lo que realmente hace la diferencia es la integración de herramientas de testing en cada etapa del flujo de trabajo, permitiendo validar cambios antes de que lleguen a producción y asegurando que Git sea siempre una fuente de verdad confiable.
Testing en cada etapa: La clave del GitOps iterativo
GitOps no es solo sobre despliegue sino también desarrollo iterativo, es construir confianza en cada paso del ciclo de vida. Para lograr esto, cada herramienta tiene un compañero de testing especializado que permite validar cambios antes de que lleguen a producción. Este enfoque iterativo asegura que Git sea siempre una fuente de verdad confiable.
Nota: No con todas las herramientas que escogemos para nuestros flujos DevOps podremos hacer testing fácilmente, por suerte con las que hemos escogido si podemos
Terraform + LocalStack: Pruebas de infraestructura sin costo
- ¿Qué hace? LocalStack emula servicios AWS en Docker, permitiendo probar Terraform sin gastar en la nube
- Beneficio para GitOps: Validar cambios de infraestructura localmente antes de commit, asegurando que el código de IaC sea correcto
- Flujo iterativo:
- Modificar módulo Terraform
- Probar con LocalStack revisa Anexo: Implementación de Terraform, Terragrunt y LocalStack
- Confirmar cambios en Git
- CI ejecuta validación en pipeline
- Por qué es esencial: Evita errores costosos en la nube real y permite desarrollo offline
Ansible + Molecule: Configuración probada en contenedores
- ¿Qué hace? Molecule ejecuta roles de Ansible en contenedores Docker aislados
- Beneficio para GitOps: Cada cambio en configuración se prueba en entornos idénticos a producción antes de commit
- Flujo iterativo:
- Actualizar rol Ansible
- Ejecutar
molecule test
localmente Anexo: Uso de Ansible y Molecule para Configuración - Validar en CI con pipeline config-ci.yml
- Solo cambios probados llegan a Git
- Por qué es esencial: Garantiza idempotencia y evita errores de configuración en producción
Helm + kind: Despliegues de aplicaciones en Kubernetes local
- ¿Qué hace? kind crea clústeres Kubernetes en Docker para probar Helm charts
- Beneficio para GitOps: Validar charts antes de sincronizar con ArgoCD, evitando despliegues fallidos
- Flujo iterativo:
- Modificar Helm chart
- Desplegar en kind local
- Verificar funcionalidad
- Commit a Git
- ArgoCD sincroniza solo cambios validados
- Por qué es esencial: Permite probar despliegues completos sin afectar clústeres reales
GitLab CI + gitlab-ci-local: Pipelines probadas antes del push
- ¿Qué hace? gitlab-ci-local ejecuta pipelines de GitLab en tu máquina
- Beneficio para GitOps: Evitar errores en CI antes de hacer push, manteniendo la calidad del repositorio
- Flujo iterativo:
- Modificar .gitlab-ci.yml
- Probar con
gitlab-ci-local --file ci/pipelines.yml
- Confirmar cambios solo si pasan pruebas
- GitLab CI ejecuta validaciones en remoto
- Por qué es esencial: Reduce tiempo de espera en CI y asegura que los pipelines sean confiables desde el primer commit
💡 La magia de GitOps iterativo: Estas herramientas crean un ciclo de feedback rápido y seguro donde cada cambio se prueba en múltiples capas antes de convertirse en la fuente de verdad. La confianza en Git como única fuente de la verdad surge precisamente porque cada componente tiene su propio mecanismo de validación, desde el desarrollo local hasta los pipelines en la nube.
Parte 1: La Fundación - Infraestructura como Código que No Falla
1.1. Introducción: Construyendo el Escenario para Nuestra Aplicación
La infraestructura es la base sobre la que se construye toda la aplicación. Sin una fundación sólida, cada despliegue se convierte en un juego de azar. En el pasado, los ingenieros debían:
- Conectarse manualmente a servidores
- Instalar paquetes uno por uno
- Configurar servicios con archivos de texto
- Esperar que no hubiera errores en la configuración
El resultado era deriva de configuración - entornos que divergían con el tiempo, causando “funciona en mi máquina” y problemas impredecibles en producción.
Hoy, la solución es Infraestructura como Código (IaC). Al definir nuestra infraestructura en archivos de código versionables, logramos:
- Reproducibilidad: Crear el mismo entorno en segundos
- Consistencia: Todos los entornos idénticos
- Auditoría: Cada cambio es un commit en Git
- Seguridad: Revisión por pares antes de aplicar cambios
1.2. Los Arquitectos: Terraform, Terragrunt y la Magia de la Infraestructura Declarativa
Terraform es el motor declarativo que permite definir “qué” queremos (clúster EKS, base de datos RDS) en HCL (HashiCorp Configuration Language), y se encarga del “cómo” construirlo. El flujo de trabajo estándar es:
flowchart LR A[terraform init] --> B[terraform plan] B --> C[Revisión del plan] C --> D[terraform apply] D --> E[Infraestructura creada]
Pero a medida que crece la complejidad, Terraform solo no basta. Es aquí donde Terragrunt entra en escena como el sistema de dirección que hace que la infraestructura sea manejable a escala.
Ventajas clave de Terragrunt:
- DRY (Don’t Repeat Yourself): Configuraciones comunes definidas una vez y heredadas
- Gestión de estado remoto: Automatiza el almacenamiento del estado en S3
- Orquestación multi-entorno: Diferencias entre entornos gestionadas de forma limpia
Estructura de directorios optimizada:
graph TD infra/ --> terraform/ terraform/ --> modules/ modules/ --> eks/ modules/ --> rds/ modules/ --> network/ modules/ --> local-k8s/ terraform/ --> environments/ environments/ --> local/ environments/ --> lab/ environments/ --> prod/ terraform/ --> terragrunt.hcl
1.3. Testing con LocalStack: Validación de Infraestructura sin Costo
La verdadera magia de GitOps iterativo surge cuando podemos probar cambios antes de que lleguen a producción. Para Terraform, LocalStack es nuestra herramienta clave para validar infraestructura sin gastar un céntimo en la nube real.
¿Qué hace LocalStack?
- Emula servicios AWS en un contenedor Docker
- Permite probar Terraform contra una versión local de AWS
- Elimina la necesidad de credenciales de AWS reales para pruebas
Nota: Para poder usar comodamente todas las herramientas y flujos de trabajo, he creado un fichero Makefile y un directorio
scripts
con scripts auxiliares. Se hará referencia a estos en todo el documento.
Flujo iterativo de prueba:
- Modificar módulo Terraform
- Ejecutar
make test-infra
(que inicia LocalStack y aplica Terraform) - Validar con
awslocal
comandos - Confirmar cambios en Git solo si pasan las pruebas
- GitLab CI ejecuta validación en pipeline antes de merge
flowchart TD A[Modificación en Terraform] --> B[Iniciar LocalStack] B --> C[Ejecutar terragrunt apply] C --> D[Verificar con awslocal] D --> E{¿Pruebas pasaron?} E -->|Sí| F[Commit en Git] E -->|No| G[Corregir y repetir]
Configuración típica de LocalStack:
# docker-compose.yml
services:
localstack:
container_name: localstack_main
image: localstack/localstack:latest
ports:
- "4566:4566"
- "4510-4559:4510-4559"
environment:
- SERVICES=s3,cloudformation,mq,ec2,eks,iam,rds
- DEFAULT_REGION=eu-central-1
- LOCALSTACK_HOST=localhost.localstack.cloud
volumes:
- "localstack_data:/var/lib/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
volumes:
localstack_data:
Ejemplo de prueba en Makefile:
# Makefile
test-infra: ## Run the full infrastructure test suite against LocalStack
@echo "--- Starting LocalStack for infrastructure tests ---"
docker compose -f docker-compose.localstack.yml up -d
@echo "--- Running infrastructure tests ---"
./scripts/run-infra-tests.sh
@echo "--- Tearing down LocalStack ---"
docker compose -f docker-compose.localstack.yml down
Ventajas clave:
- Costo cero: Pruebas sin facturas de AWS
- Velocidad: Ciclos de feedback en segundos (no minutos)
- Offline: Desarrollo sin conexión a internet
- Consistencia: Comportamiento idéntico a AWS
- Seguridad: No se usan credenciales reales en desarrollo
💡 Consejo del Arquitecto: La variable
SERVICES
en LocalStack es crucial. Solo los servicios listados se iniciarán. Si necesitas Lambda, añadelambda
a la lista. Siempre verifica que los servicios necesarios para tus módulos Terraform estén incluidos.
1.4. El Entorno Local: Orquestando un Clúster de Kubernetes con kind
Para desarrollo local, usamos kind (Kubernetes in Docker) con Terraform. Con un simple make infra-up
:
flowchart LR A[make infra-up] --> B[Terraform crea clúster kind] B --> C[Generar KUBECONFIG] C --> D[kubectl listo para usar]
Ejemplo de configuración Terraform para kind:
# modules/local-k8s/main.tf
resource "kind_cluster" "main" {
name = "dev-cluster"
node {
image = "kindest/node:v1.27.3"
roles = ["control-plane", "worker"]
}
}
1.5. GitLab CI pipeline modular y orquestada
En un sistema tan completo como el nuestro, un único pipeline monolítico sería un desastre. Por ello, hemos adoptado una arquitectura de CI/CD modular y orquestada, donde cada componente del sistema tiene su propio ciclo de vida, pero todos trabajan en armonía. El cerebro de esta operación es un fichero orquestador (ci/pipelines.yml
) que incluye pipelines especializados para cada dominio: la aplicación (backend y frontend), la infraestructura (Terraform), la configuración (Ansible) y el despliegue (Helm/GitOps).
La clave de la eficiencia es la directiva rules:changes
, que asegura que un pipeline solo se ejecute cuando se modifican los ficheros de su dominio. Esto evita ejecuciones innecesarias, ahorrando tiempo y recursos.
Nota: La arquitectura modular y orquestada nos permite desplegar y actualizar cada componente del sistema de forma independiente, sin embargo esto se hace asi aqui porque tenemos un monorepo con todo, para casos reales seria mas inteligente separar cada componente en su propio repositorio con su pipeline independiente.
A continuación, desglosamos cada uno de estos pipelines.
1.5.1. Pipelines de Aplicación: El Corazón del Código
Estos pipelines se centran exclusivamente en la calidad y el empaquetado del código de la aplicación. Su objetivo es validar, probar, construir y escanear el backend y el frontend de forma independiente.
a) Pipeline de Backend (Python/Flask)
- Fichero de definición:
ci/backend-ci.yml
. - Se enfoca en: Validar la calidad del código Python, ejecutar pruebas unitarias y de integración, y construir la imagen Docker final.
- Activación: Se dispara automáticamente solo si se detectan cambios en la ruta
apps/backend/**/*
. - Etapas y Trabajos Clave:
validate-be
: Ejecutalint-backend
con herramientas como Flake8 para asegurar que el código sigue las guías de estilo.test-be
: Ejecutatest-backend
usandopytest
para validar la lógica de negocio.build-be
: El trabajobuild-backend-kaniko
construye la imagen Docker sin necesidad de un demonio Docker, lo que es más seguro y eficiente en CI.scan-be
: El trabajoscan-backend-image
utilizaTrivy
para escanear la imagen recién construida en busca de vulnerabilidades de alta criticidad.
b) Pipeline de Frontend (React)
- Fichero de definición:
ci/frontend-ci.yml
. - Se enfoca en: Asegurar la calidad del código JavaScript/TypeScript, ejecutar pruebas de componentes y construir los artefactos estáticos para servirlos.
- Activación: Se dispara solo con cambios en
apps/frontend/**/*
. - Etapas y Trabajos Clave:
validate-fe
: Ejecutalint-frontend
para verificar el formato y estilo del código.test-fe
: Lanzatest-frontend
con el conjunto de pruebas de la aplicación React.build-fe
: El trabajobuild-frontend-kaniko
empaqueta la aplicación en una imagen Docker lista para ser servida.scan-fe
: Al igual que el backend,scan-frontend-image
escanea la imagen conTrivy
en busca de vulnerabilidades.
Cómo ejecutar los pipelines de aplicación en local
Aquí viene la magia. No necesitas hacer un push
para ver si tu pipeline funciona. Gracias a gitlab-ci-local
y nuestro Makefile
, puedes simular todo el flujo de CI en tu máquina.
-
Prerrequisitos:
- Tener
gitlab-ci-local
instalado. - Configurar tus credenciales de Docker Hub en un fichero
.gitlab-ci-local-variables.yml
(que está ignorado por Git) para que Kaniko pueda subir las imágenes. El fichero debería contener tuDOCKER_AUTH_CONFIG
.
- Tener
-
Comandos Mágicos:
-
Para probar el pipeline completo del backend (linting + tests + build):
make ci-backend
Este comando orquesta la ejecución de los diferentes jobs del backend localmente.
-
Para probar el pipeline completo del frontend:
make ci-frontend
De forma análoga, este comando ejecuta todo el ciclo de CI para la aplicación React.
-
Estos comandos utilizan internamente gitlab-ci-local
con el parámetro --file
para apuntar al archivo YAML específico de cada pipeline, simulando las rules:changes
para que se ejecuten aunque no haya habido un cambio real en Git.
5.1.2. Pipelines de Plataforma: Los Cimientos y la Orquesta
Estos pipelines gestionan la infraestructura, configuración y despliegue de la plataforma - todo lo que no es código de aplicación.
Pipeline de Infraestructura (IaC)
Aspecto | Detalles |
---|---|
Archivo | ci/iac-pipeline.yml |
Propósito | Validar, probar y aplicar infraestructura con Terraform/Terragrunt |
Activación | Cambios en infra/terraform/**/* |
Etapas principales:
validate-infra
- Valida sintaxis HCL y mejores prácticas conterraform-validate
ytflint
test-infra
- Prueba infraestructura contra LocalStack (simulación AWS sin costo)plan-infra
- Genera plan de ejecución conterragrunt-plan
para revisiónapply-infra
- Aplica cambios (trabajo manual por seguridad)
Pipeline de Configuración (Ansible)
Aspecto | Detalles |
---|---|
Archivo | ci/config-ci.yml |
Propósito | Configurar servicios (BD, colas) post-aprovisionamiento |
Activación | Cambios en config/ansible/**/* |
Etapas principales:
validate-config
- Ejecutaansible-lint
ymolecule-test
en contenedores Dockerconfigure
- Aplica playbooks conansible-configure
(esquemas PostgreSQL, topología RabbitMQ)
Pipeline de Entrega Continua (GitOps)
Aspecto | Detalles |
---|---|
Archivo | ci/cd-pipeline.yml |
Propósito | Actualizar estado deseado en Git (no desplegar directamente) |
Activación | Cambios en helm-charts/**/* o nuevas imágenes |
Etapas principales:
validate-cd
- Valida Helm Umbrella Chart conhelm-lint-platform
deploy
-cd-gitops-update
actualizavalues.yaml
y hace push; ArgoCD sincroniza automáticamente
Ejecución Local
# Probar infraestructura contra LocalStack
make test-infra
# Validar sintaxis de toda la plataforma
make ci-infra
Resumen de Comandos Locales
Comando make |
Propósito | Pipelines Relacionados |
---|---|---|
make ci-backend |
Ejecuta el CI completo (lint, test, build) para el backend. | Aplicación (Backend) |
make ci-frontend |
Ejecuta el CI completo (lint, test, build) para el frontend. | Aplicación (Frontend) |
make ci-app |
Ejecuta los dos pipelines de aplicación anteriores. | Aplicación (Ambos) |
make test-infra |
Prueba los módulos de Terraform contra una simulación de AWS (LocalStack). | Plataforma (IaC) |
make ci-infra |
Valida estáticamente (linting) todo el código de la plataforma: Terraform, Helm y Ansible. | Plataforma (IaC, Config, CD) |
make ci-all |
Ejecuta absolutamente todo: ci-infra seguido de ci-app . |
Todos |
Con esta arquitectura y los atajos del Makefile
, tienes un sistema de CI/CD extremadamente potente, modular y, lo más importante, que te permite desarrollar de forma rápida e iterativa con la confianza de que lo que funciona en tu máquina se comportará de la misma manera en el pipeline real.
1.6. Conclusión de la Parte 1 y Avance
Hemos construido una fundación sólida:
- Infraestructura versionada en Git
- Reproducible en múltiples entornos
- Pruebas rápidas con LocalStack
- Clúster Kubernetes local para desarrollo
- Pipelines validados en GitLab CI
Pero, ¿de qué sirve un escenario vacío? En la Parte 2, subiremos a los tramoyistas: Ansible y Molecule, para preparar las bases de datos y colas de mensajes para la aplicación.
Parte 2: Domando el Caos - Configuración con Ansible y Pruebas Rigurosas
2.1. Introducción: De la Infraestructura Vacía a los Servicios Listos
Tras crear la infraestructura en la Parte 1, tenemos cajas vacías:
- PostgreSQL sin esquemas ni usuarios
- RabbitMQ sin colas ni exchanges
- Kubernetes sin aplicaciones
Aquí es donde entra Ansible, nuestro maestro de la configuración. Sus principios clave:
- Sin agentes: Se conecta por SSH
- Idempotencia: Ejecutar múltiples veces = mismo resultado
- YAML legible: Configuración fácil de entender
2.2. Anatomía de Nuestra Configuración Automatizada
La estructura de Ansible es clave para la escalabilidad:
graph TD config/ansible/ --> inventory/ inventory/ --> local.ini inventory/ --> lab.ini config/ansible/ --> group_vars/ group_vars/ --> all/ group_vars/ --> local/ config/ansible/ --> playbooks/ playbooks/ --> configure_local.yml config/ansible/ --> roles/ roles/ --> postgresql_config/ roles/ --> rabbitmq_config/
Detalles clave de cada directorio:
- inventory: Define dónde se ejecutan las tareas
# inventory/local.ini [k8s_cluster] localhost ansible_connection=local
- group_vars: Variables por entorno
# group_vars/local/db.yml db_name: "dev_db" db_user: "dev_user" db_password: "dev_password"
- roles: Lógica reutilizable
# roles/postgresql_config/tasks/main.yml - name: Create database postgresql_db: name: "{{ db_name }}" state: present
2.3. Testing con Molecule: Validación de Roles en Contenedores
Molecule es el guardián de calidad para Ansible. Permite ejecutar roles de Ansible en contenedores Docker aislados, asegurando que cada cambio en configuración se pruebe en entornos idénticos a producción antes de commit.
Flujo iterativo de prueba:
- Actualizar rol Ansible
- Ejecutar
molecule test
localmente - Validar en CI con pipeline config-ci.yml
- Solo cambios probados llegan a Git
flowchart TD A[Modificación en rol Ansible] --> B[Molecule crea contenedor] B --> C[Aplica rol] C --> D[Verifica resultados] D --> E{¿Pruebas pasaron?} E -->|Sí| F[Commit en Git] E -->|No| G[Corregir y repetir]
Ejemplo de configuración Molecule:
# molecule/postgresql_config/molecule.yml
driver:
name: docker
provisioner:
name: ansible
platforms:
- name: postgres
image: "postgres:14"
verifier:
name: testinfra
options:
sudo: yes
lint:
name: yamllint
Ejemplo de prueba para PostgreSQL:
# molecule/postgresql_config/tests/test_default.py
import pytest
import psycopg2
def test_database_created(host):
# Verificar que la base de datos existe
conn = psycopg2.connect(
host="localhost",
database="dev_db",
user="dev_user",
password="dev_password"
)
cur = conn.cursor()
cur.execute("SELECT 1 FROM tasks")
assert cur.fetchone() is not None
Ventajas clave:
- Aislamiento: Cada prueba en contenedor independiente
- Idempotencia verificada: Ejecutar múltiples veces = mismo resultado
- Rápido feedback: No necesitas esperar a CI remoto
- Confianza: Sabes que los cambios funcionarán en producción
💡 Consejo del Arquitecto: Molecule permite probar en múltiples plataformas (Docker, Vagrant, etc.). Para pruebas de PostgreSQL, usa la imagen oficial de PostgreSQL; para RabbitMQ, usa la imagen oficial de RabbitMQ.
2.4. Un Vistazo a los Roles: Configurando PostgreSQL y RabbitMQ
Rol PostgreSQL Config
flowchart TD A[Crear usuario] --> B[Crear base de datos] B --> C[Crear esquema] C --> D[Crear tablas] D --> E[Crear índices] E --> F[Opcional: poblar datos]
Ejemplo de tarea para crear tablas:
- name: Create tasks table
postgresql_query:
db: "{{ db_name }}"
query: |
CREATE TABLE IF NOT EXISTS tasks (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
status VARCHAR(50) CHECK (status IN ('pending', 'in_progress', 'completed')),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Rol RabbitMQ Config
flowchart TD A[Crear exchanges] --> B[Crear DLX] B --> C[Crear colas] C --> D[Crear bindings]
Configuración típica de RabbitMQ:
- name: Declare task_events_exchange
rabbitmq_exchange:
name: "task_events_exchange"
type: "topic"
durable: yes
state: present
vhost: "/"
- name: Declare task.created.queue
rabbitmq_queue:
name: "task.created.queue"
durable: yes
vhost: "/"
- name: Bind queue to exchange
rabbitmq_binding:
exchange: "task_events_exchange"
queue: "task.created.queue"
routing_key: "task.created"
vhost: "/"
2.5. Conclusión de la Parte 2 y Avance
Hemos logrado:
- Configuración idempotente y reutilizable
- Pruebas automatizadas en contenedores aislados
- Separación clara entre infraestructura (Parte 1) y configuración (Parte 2)
- Pipelines de CI/CD validados antes de merge
Con el escenario listo y los servicios configurados, solo falta la aplicación. En la Parte 3, daremos vida a nuestra sinfonía con un flujo GitOps completo.
Parte 3: El Gran Final - Despliegue GitOps con ArgoCD, Helm y GitLab CI
3.1. Introducción: ¡Es la Hora del Espectáculo!
Ahora que tenemos:
- Infraestructura definida en Terraform
- Servicios configurados con Ansible
- Clúster Kubernetes listo
Es hora de desplegar la aplicación. Pero no cualquier despliegue… GitOps.
GitOps es una filosofía donde:
- Git es la única fuente de la verdad
- Los cambios se aplican automáticamente
- El sistema se auto-repara
- Los rollbacks son triviales
Comparación: Enfoque Tradicional vs GitOps
Característica | Enfoque Tradicional | GitOps |
---|---|---|
Fuente de Verdad | Dispersa (documentos, scripts) | Centralizada: repositorio Git |
Acción de Despliegue | Imperativa (kubectl apply) | Declarativa (ArgoCD reconcilia) |
Detección de Deriva | Manual | Automática y continua |
Reversión | Compleja | git revert |
Impulsor del Cambio | Humano “empuja” cambios | Agente “tira” cambios desde Git |
3.2. Las Herramientas del Oficio GitOps
Helm: El Gestor de Paquetes de Kubernetes
Helm empaqueta aplicaciones en charts reutilizables. En este proyecto, usamos un Umbrella Chart:
graph TD todo-platform/ --> backend/ todo-platform/ --> frontend/ todo-platform/ --> common/ backend/ --> Dockerfile backend/ --> requirements.txt frontend/ --> package.json frontend/ --> src/
Ejemplo de values-prod.yaml:
backend:
image:
repository: my-registry/backend
tag: a1b2c3d
frontend:
image:
repository: my-registry/frontend
tag: v1.2.3
ArgoCD: El Cerebro de GitOps
ArgoCD es el operador que reconcilia el estado deseado con el estado real:
flowchart LR A[Repositorio Git] --> B[ArgoCD] B --> C[Comparar estado real vs deseado] C --> D{¿Hay diferencias?} D -->|Sí| E[Aplicar cambios] D -->|No| F[No hacer nada] E --> B
Beneficios clave:
- Auto-reparación: Si alguien borra un recurso, ArgoCD lo recrea
- Historial completo: Todos los cambios auditados en Git
- Rollbacks instantáneos:
git revert
+ sincronización
GitLab CI: El Motor de Automatización
GitLab CI gestiona la integración continua antes del despliegue:
flowchart LR A[Push a main] --> B[Detectar cambios en apps/backend/] B --> C[Build imagen con Kaniko] C --> D[Actualizar values-prod.yaml] D --> E[Commit y push a Git] E --> F[ArgoCD detecta cambio] F --> G[Despliegue en producción]
3.3. Testing con kind: Despliegues en Kubernetes Local
Para validar Helm charts antes de sincronizar con ArgoCD, usamos kind (Kubernetes in Docker) para crear clústeres Kubernetes locales.
Flujo iterativo de prueba:
- Modificar Helm chart
- Desplegar en kind local
- Verificar funcionalidad
- Commit a Git
- ArgoCD sincroniza solo cambios validados
flowchart TD A[Modificación en Helm chart] --> B[Crear clúster kind] B --> C[helm install --dry-run] C --> D[Verificar recursos] D --> E{¿Funciona?} E -->|Sí| F[Commit en Git] E -->|No| G[Corregir y repetir]
Ejemplo de prueba con kind:
# Crear clúster kind
kind create cluster --name test-cluster
# Instalar Helm chart en dry-run
helm install --dry-run --debug ./helm/todo-platform
# Desplegar en clúster
helm install todo-app ./helm/todo-platform
# Verificar aplicaciones
kubectl get pods
kubectl get services
# Limpiar
helm uninstall todo-app
kind delete cluster --name test-cluster
Ventajas clave:
- Seguridad: No afecta clústeres reales
- Rapidez: Clústeres se crean y destruyen en segundos
- Consistencia: Mismo comportamiento que en producción
- Validación temprana: Detecta errores antes de sincronizar con ArgoCD
💡 Consejo del Arquitecto: Usa
--dry-run
para validar sin desplegar, y--debug
para ver detalles de Helm. Esto es crucial para evitar despliegues fallidos en producción.
3.4. Testing con gitlab-ci-local: Pipelines probadas antes del push
La herramienta gitlab-ci-local permite probar pipelines de GitLab en tu máquina antes de hacer push, asegurando que los cambios en .gitlab-ci.yml funcionarán correctamente en remoto.
Flujo iterativo de prueba:
- Modificar .gitlab-ci.yml
- Probar con
gitlab-ci-local --file ci/pipelines.yml
- Confirmar cambios solo si pasan pruebas
- GitLab CI ejecuta validaciones en remoto
flowchart TD A[Modificación en .gitlab-ci.yml] --> B[gitlab-ci-local] B --> C[Ejecutar pipeline local] C --> D{¿Pruebas pasaron?} D -->|Sí| E[Commit en Git] D -->|No| F[Corregir y repetir]
Ejecución local con gitlab-ci-local:
# Probar todos los pipelines en local
gitlab-ci-local --file ci/pipelines.yml \
--variable CI_PIPELINE_SOURCE=merge_request_event \
--evaluate-rule-changes=false
# Probar solo la infraestructura
gitlab-ci-local --file ci/iac-pipeline.yml \
--variable CI_PIPELINE_SOURCE=merge_request_event \
--evaluate-rule-changes=false \
validate plan
# Probar solo la configuración de Ansible
gitlab-ci-local --file ci/config-ci.yml \
--variable CI_PIPELINE_SOURCE=merge_request_event \
--evaluate-rule-changes=false \
ansible-lint molecule-test
# Probar solo el backend
gitlab-ci-local --file ci/backend-ci.yml \
--variable CI_PIPELINE_SOURCE=merge_request_event \
--evaluate-rule-changes=false \
pytest flake8 docker-build
# Probar solo el frontend
gitlab-ci-local --file ci/frontend-ci.yml \
--variable CI_PIPELINE_SOURCE=merge_request_event \
--evaluate-rule-changes=false \
npm-test npm-build
# Probar solo el CD pipeline
gitlab-ci-local --file ci/cd-pipeline.yml \
--variable CI_PIPELINE_SOURCE=merge_request_event \
--evaluate-rule-changes=false \
update-values sync-argocd
Ventajas clave:
- Ciclos de feedback rápidos: No necesitas esperar a runners de CI remoto
- Reducción de costos: Menos minutos de CI consumidos
- Mayor confianza: Sabes que los cambios funcionarán en remoto
- Seguridad: Evitas romper el pipeline principal
💡 Consejo del Arquitecto: Usa
--evaluate-rule-changes=false
para probar el pipeline completo sin depender de cambios en archivos específicos. Esto es esencial para probar reglas complejas de CI/CD.
3.5. La Sinfonía Completa: El Flujo End-to-End
Sigamos el viaje de un cambio desde el portátil hasta producción:
flowchart TD A[Desarrollador hace push] --> B[GitLab CI detecta cambios] B --> C[Ejecutar pipelines específicos] C --> D[backend-ci.yml: tests y build] C --> E[frontend-ci.yml: tests y build] C --> F[iac-pipeline.yml: validar infraestructura] C --> G[config-ci.yml: validar configuración] C --> H[test-helm: validar Helm charts] D & E & F & G & H --> I[cd-pipeline.yml: actualizar valores y sincronizar] I --> J[ArgoCD detecta cambio en Git] J --> K[ArgoCD reconcilia clúster con estado deseado] K --> L[Clúster actualizado automáticamente]
3.6. Pipeline Maestro: pipelines.yml
Para organizar todos los pipelines en un solo lugar, creamos un archivo maestro ci/pipelines.yml
:
# ci/pipelines.yml
include:
- local: ci/iac-pipeline.yml
- local: ci/config-ci.yml
- local: ci/backend-ci.yml
- local: ci/frontend-ci.yml
- local: ci/cd-pipeline.yml
Este archivo permite ejecutar todos los pipelines en una sola ejecución, facilitando la gestión y el mantenimiento.
3.7. Conclusión Final: La Orquesta en Perfecta Armonía
Hemos construido una sinfonía DevOps completa:
Componente | Rol | Herramientas |
---|---|---|
Escenario | Infraestructura | Terraform, Terragrunt, LocalStack |
Tramoyistas | Configuración | Ansible, Molecule |
Músicos | Aplicación | Helm, kind, ArgoCD |
Director | Orquestación | GitLab CI, gitlab-ci-local |
Partitura | Fuente de verdad | Git |
Beneficios transformadores:
- 🚀 Entrega acelerada: Despliegues en minutos
- 🔒 Seguridad mejorada: Revisión por pares en todos los cambios
- 🛡️ Resiliencia: Auto-reparación ante derivas
- 📜 Auditoría completa: Historial Git de todos los cambios
- 💡 Innovación sin miedo: Confianza para implementar cambios
Al final, no se trata de las herramientas, sino de la partitura. Con GitOps, cada componente interpreta la misma melodía desde la única fuente de la verdad: Git. El resultado es una aplicación que no solo funciona, sino que se auto-repara, es auditable por diseño y, lo más importante, le devuelve al equipo de ingeniería la tranquilidad para innovar sin miedo.
La verdadera sinfonía no es solo la música que se escucha, sino la tranquilidad de saber que la música nunca se detendrá.
¿Listo para comenzar?
-
📁 Clona el repositorio de ejemplo: `git clone https://gitlab.com/rubentxu74/todo-aws
-
🚀 Ejecuta la fundación:
make infra-up
(Terraform + LocalStack) -
🛠️ Configura los servicios:
ansible-playbook config/ansible/playbooks/configure_local.yml
-
🎶 Despliega la aplicación:
git push origin main
(GitLab CI + ArgoCD) -
🔍 Prueba los pipelines en local antes de push:
gitlab-ci-local --file ci/pipelines.yml \ --variable CI_PIPELINE_SOURCE=merge_request_event \ --evaluate-rule-changes=false
¡Y observa cómo la sinfonía DevOps comienza a sonar!
Nota: Para que todo funciones correctamente tendrás que instalar muchas herramientas diversas que no cubrimos en este documento, como:
- terraform
- terragrunt
- kubectl
- helm
- gitlab-ci-local
- ansible
- awscli (opcional)
- yq / jq (utilidades)
- direnv (opcional)
Si tienes linux o sistema unix talvez asdf-vm te pueda ayudar bastante. https://asdf-vm.com/
Anexo A. Estructura del Repositorio de Código Unificado
Se propone una estructura de repositorio monorepo que contiene el código de la aplicación y toda la definición de su infraestructura y despliegue, promoviendo la coherencia y la facilidad de gestión. En casos mas avanzados se podria separar el codigo de la aplicacion, la infraestructura y el de configuracion y despliegue en repositorios diferentes y que cada uno de ellos tenga su pipeline de gitlab centrado en su objetivo. Pero como en este caso es un proyecto pequeño y de ejemplo, se opta por la estructura monorepo.
project-root/
├── Makefile
├── infra/
│ ├── terraform/
│ │ ├── modules/
│ │ │ ├── network/
│ │ │ ├── eks/
│ │ │ ├── rds/
│ │ │ ├── rabbitmq/
│ │ │ ├── s3/
│ │ │ └── local-k8s/
│ │ ├── environments/
│ │ │ ├── dev/ (#1)
│ │ │ ├── lab/ (#2)
│ │ │ └── prod/ (#3)
│ │ └── terragrunt.hcl
├── config/
│ ├── ansible/
│ │ ├── group_vars/
│ │ │ ├── all/
│ │ │ ├── local/
│ │ │ ├── lab/
│ │ │ └── prod/
│ │ ├── playbooks/
│ │ │ ├── postgresql/
│ │ │ ├── rabbitmq/
│ │ │ └── kubernetes/
│ │ ├── inventory/
│ │ │ ├── local.ini
│ │ │ ├── lab.ini
│ │ │ └── prod.ini
│ │ └── roles/
│ │ ├── postgresql-config/
│ │ ├── rabbitmq-config/
│ │ └── k8s-config/
├── apps/
│ ├── python-app/
│ │ ├── Dockerfile
│ │ ├── src/
│ │ ├── tests/
│ │ └── requirements.txt
├── helm/
│ ├── charts/
│ │ ├── python-app/
│ │ │ ├── Chart.yaml
│ │ │ ├── values.yaml
│ │ │ ├── templates/
│ │ │ └── requirements.yaml
│ │ ├── rabbitmq/
│ │ └── postgresql/
│ └── environments/
│ ├── local/
│ ├── lab/
│ └── prod/
├── argocd/
│ ├── applications/
│ │ ├── base/
│ │ └── overlays/
│ ├── projects/
│ └── config/
├── ci-cd/
│ ├── .gitlab-ci.yml # Pipeline con rules:changes para aplicacion y infraestructura
└── Makefile
- Anexo: Implementación de Terraform, Terragrunt y LocalStack: Este anexo detalla la gestión de infraestructura como código (IaC) usando Terraform y Terragrunt para entornos múltiples, y LocalStack para pruebas locales. Incluye diagramas Mermaid de la estructura de componentes y flujos de trabajo, ejemplos de configuración de Terragrunt, scripts de Makefile para automatización, y guías para integrar con CI/CD.
- Anexo: Uso de Ansible y Molecule para Configuración: Explica cómo usar Ansible para la configuración de recursos (esquemas de BBDD, colas de mensajes) y Molecule para el testing de roles en contenedores.