Armar un proyecto DevOps completo significa unir CI, CD, contenedores, monitoreo y seguridad en un solo flujo que funciona de forma automática.
El momento en que todo cobra sentido
Imagina que llevas semanas aprendiendo piezas sueltas. Ya sabes qué es un pipeline. Ya configuraste Docker. Ya agregaste escaneo de seguridad con Trivy. Pero sientes que son islas separadas, sin conexión.
Este es el momento en que todo se une. En esta lección vas a construir un proyecto real desde cero. Será pequeño, pero completo. Y al terminar, tendrás algo que puedes mostrar en una entrevista o usar en tu trabajo hoy mismo.
El Sistema CADENA: tu marco de referencia final
Para este proyecto usaremos un marco llamado CADENA DevOps. Cada letra representa una capa del sistema:
- C — Código fuente con control de versiones (Git)
- A — Automatización de pruebas (CI)
- D — Docker: empaqueta tu aplicación
- E — Entrega continua (CD al servidor o nube)
- N — No vulnerabilidades (seguridad en el pipeline)
- A — Alertas y monitoreo (Prometheus + Grafana)
Cada eslabón depende del anterior. Si uno falla, el pipeline se detiene. Eso es exactamente lo que quieres.
El proyecto: API de consulta de precios
Vamos a construir una API sencilla en Python con Flask. Simula un servicio que consulta precios de productos, como los que usa Liverpool o Mercado Libre internamente.
La API tiene un solo endpoint: /precio?producto=laptop. Devuelve un JSON con el nombre del producto y su precio en pesos.
Estructura de archivos
mi-api-precios/
├── app.py
├── test_app.py
├── requirements.txt
├── Dockerfile
├── .github/
│ └── workflows/
│ └── pipeline.yml
└── docker-compose.yml
El código de la aplicación
# app.py
from flask import Flask, jsonify, request
app = Flask(__name__)
precios = {
"laptop": 18500,
"teclado": 850,
"monitor": 6200
}
@app.route("/precio")
def obtener_precio():
producto = request.args.get("producto", "")
if producto in precios:
return jsonify({"producto": producto, "precio": precios[producto]})
return jsonify({"error": "Producto no encontrado"}), 404
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
Nota: los precios en el código son números crudos (18500). Cuando los muestres al usuario, formátealos como $18,500.
Las pruebas automatizadas
# test_app.py
import pytest
from app import app
@pytest.fixture
def cliente():
app.config["TESTING"] = True
with app.test_client() as c:
yield c
def test_precio_existente(cliente):
resp = cliente.get("/precio?producto=laptop")
assert resp.status_code == 200
datos = resp.get_json()
assert datos["precio"] == 18500
def test_producto_no_encontrado(cliente):
resp = cliente.get("/precio?producto=silla")
assert resp.status_code == 404
El Dockerfile: empaqueta tu aplicación
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
Usa python:3.11-slim y no python:3.11. La versión slim pesa menos y tiene menos vulnerabilidades. Trivy lo agradecerá.
El pipeline completo en GitHub Actions
Este es el corazón del proyecto. El archivo pipeline.yml une todos los eslabones de la CADENA:
# .github/workflows/pipeline.yml
name: Pipeline DevOps Completo
on:
push:
branches: [main]
jobs:
pruebas:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Instalar dependencias
run: pip install -r requirements.txt
- name: Ejecutar pruebas
run: pytest test_app.py -v
seguridad:
runs-on: ubuntu-latest
needs: pruebas
steps:
- uses: actions/checkout@v3
- name: Análisis estático con bandit
run: |
pip install bandit
bandit -r app.py
- name: Revisar dependencias con safety
run: |
pip install safety
safety check -r requirements.txt
docker:
runs-on: ubuntu-latest
needs: seguridad
steps:
- uses: actions/checkout@v3
- name: Construir imagen
run: docker build -t mi-api-precios:latest .
- name: Escanear imagen con Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: mi-api-precios:latest
exit-code: '1'
severity: CRITICAL
- name: Subir imagen a Docker Hub
run: |
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USER }} --password-stdin
docker tag mi-api-precios:latest ${{ secrets.DOCKER_USER }}/mi-api-precios:latest
docker push ${{ secrets.DOCKER_USER }}/mi-api-precios:latest
despliegue:
runs-on: ubuntu-latest
needs: docker
steps:
- name: Desplegar en servidor
run: echo "Aquí va tu comando SSH o llamada a Railway/Render"
Cada job depende del anterior gracias a needs. Si las pruebas fallan, la seguridad no corre. Si la seguridad falla, Docker no se construye. Si Docker falla, no hay despliegue.
Configura tus secretos correctamente
Nunca escribas tu contraseña de Docker Hub en el archivo YAML. Ve a tu repositorio en GitHub, entra a Settings → Secrets and variables → Actions y agrega:
DOCKER_USER: tu nombre de usuario en Docker HubDOCKER_PASSWORD: tu contraseña o token de acceso
Esto aplica el principio que vimos en la lección anterior: las credenciales nunca tocan el código fuente. Si alguien clona tu repositorio, no obtiene nada sensible.
Cómo probarlo localmente antes de subir
Antes de hacer push, prueba todo en tu máquina:
# 1. Corre las pruebas
pytest test_app.py -v
# 2. Construye la imagen
docker build -t mi-api-precios:latest .
# 3. Levanta el contenedor
docker run -p 5000:5000 mi-api-precios:latest
# 4. Prueba el endpoint
curl "http://localhost:5000/precio?producto=laptop"
La respuesta esperada es:
{"precio": 18500, "producto": "laptop"}
Cuando lo presentes a alguien, muéstralo formateado: el producto "laptop" cuesta $18,500.
Errores comunes al armar el primer proyecto
Error 1: Poner todo en un solo job. Si mezclas pruebas, seguridad y Docker en un solo bloque, pierdes visibilidad. Si algo falla, no sabes dónde. Separa siempre en jobs distintos.
Error 2: Usar imágenes base pesadas.
Usar python:3.11 en lugar de python:3.11-slim puede triplicar el tiempo de build. Además, Trivy encontrará más vulnerabilidades en imágenes más grandes. Empieza siempre con la versión más ligera.
Error 3: No versionar las imágenes.
Subir siempre como :latest es un problema. Si algo se rompe, no puedes volver atrás fácilmente. Usa el SHA del commit como tag: mi-api-precios:${{ github.sha }}.
Error 4: Ignorar las fallas de Trivy.
Algunos desarrolladores ponen exit-code: '0' para que el pipeline no se detenga aunque haya vulnerabilidades críticas. Eso elimina la protección. Si Trivy falla, investiga y resuelve antes de hacer merge.
Error 5: No documentar el proyecto.
Tu pipeline puede ser perfecto, pero si no tiene un README.md que explique cómo correrlo, nadie más puede usarlo. Escribe al menos cinco líneas explicando qué hace, cómo instalarlo y cómo ejecutar las pruebas.
Siguiente paso: muéstralo en tu portafolio
Este proyecto tiene todo lo que un reclutador técnico en México quiere ver. Tiene pruebas automatizadas. Tiene seguridad. Tiene contenedores. Tiene CI/CD.
Sube el repositorio a GitHub con un README claro. Incluye una imagen del pipeline corriendo exitosamente. Si consigues un badge de GitHub Actions que diga "passing", ponlo al inicio del README.
Empresas como FEMSA o Bimbo tienen equipos de plataforma que buscan exactamente este perfil: alguien que entiende el ciclo completo, no solo una parte.
Lo que aprendiste en este curso
A lo largo de estas nueve lecciones construiste una base sólida:
- Entendiste qué es DevOps y por qué cambia la forma de trabajar en equipo.
- Aprendiste a versionar código con Git y a colaborar con pull requests.
- Configuraste pipelines de integración continua con GitHub Actions.
- Automatizaste pruebas para detectar errores antes de que lleguen a producción.
- Empaquetaste aplicaciones con Docker para que corran igual en cualquier ambiente.
- Implementaste entrega continua para desplegar sin intervención manual.
- Configuraste monitoreo con Prometheus y Grafana para ver el estado de tu sistema.
- Aplicaste DevSecOps para mover la seguridad al inicio del ciclo.
- Y hoy uniste todo en un proyecto funcional y real.
Cada habilidad que practicaste tiene valor directo en el mercado laboral mexicano. Un desarrollador con este perfil puede aspirar a roles que pagan entre $22,000 y $35,000 al mes en empresas de tecnología.
El mejor pipeline DevOps no es el más complejo, sino el que tu equipo realmente usa todos los días.