certmundo.
es‑mx

7 min de lectura

¿Cómo armar tu primer proyecto DevOps completo de principio a fin?

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 Hub
  • DOCKER_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:

  1. Entendiste qué es DevOps y por qué cambia la forma de trabajar en equipo.
  2. Aprendiste a versionar código con Git y a colaborar con pull requests.
  3. Configuraste pipelines de integración continua con GitHub Actions.
  4. Automatizaste pruebas para detectar errores antes de que lleguen a producción.
  5. Empaquetaste aplicaciones con Docker para que corran igual en cualquier ambiente.
  6. Implementaste entrega continua para desplegar sin intervención manual.
  7. Configuraste monitoreo con Prometheus y Grafana para ver el estado de tu sistema.
  8. Aplicaste DevSecOps para mover la seguridad al inicio del ciclo.
  9. 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.

Puntos clave

  • El marco CADENA DevOps (Código, Automatización, Docker, Entrega, No vulnerabilidades, Alertas) te da una estructura repetible para cualquier proyecto, sin importar el tamaño.
  • Separar el pipeline en jobs distintos (pruebas → seguridad → Docker → despliegue) te da visibilidad exacta de dónde falla cada parte, lo que reduce el tiempo de diagnóstico.
  • Nunca uses `:latest` como único tag de tu imagen Docker. Versiona con el SHA del commit para poder revertir cambios cuando algo se rompe en producción.
  • Un proyecto en GitHub con README, badge de CI y pipeline funcional vale más en una entrevista técnica que cualquier certificación sin evidencia práctica.
  • DevOps no es un puesto, es una forma de trabajar. Cualquier desarrollador que automatiza pruebas, empaqueta con Docker y asegura su código ya está practicando DevOps, sin importar su título.

Comparte esta lección: