Docs / DevOps / CI/CD Pipeline

CI/CD Pipeline

DiseΓ±o e implementaciΓ³n de un pipeline CI/CD completo con GitHub Actions, Container Registry, ArgoCD y Kubernetes. Cubre desde el commit hasta el despliegue automΓ‘tico en producciΓ³n con estrategias GitOps.

Pipeline Architecture

El pipeline sigue el modelo GitOps: el estado deseado de la infraestructura se define en Git, y ArgoCD se encarga de sincronizar el cluster con el repositorio automaticamente.

text
Flujo completo CI/CD

Developer                    CI (GitHub Actions)              CD (ArgoCD)
    β”‚                              β”‚                                β”‚
    β”‚  git push                    β”‚                                β”‚
    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Άβ”‚                                β”‚
    β”‚                              β”‚  1. Lint + Test                β”‚
    β”‚                              β”‚  2. Build Docker image         β”‚
    β”‚                              β”‚  3. Push to Registry           β”‚
    β”‚                              β”‚  4. Update K8s manifests       β”‚
    β”‚                              β”‚     (image tag)                β”‚
    β”‚                              β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Άβ”‚
    β”‚                              β”‚                                β”‚  5. Detect change
    β”‚                              β”‚                                β”‚  6. Sync to cluster
    β”‚                              β”‚                                β”‚  7. Health check
    β”‚                              β”‚                                β”‚
    │◀─────────────────────────────┼─────────────────────────────────
    β”‚  Deploy notification         β”‚                                β”‚

Repositorios:
  app-repo/      -> codigo de la aplicacion + Dockerfile + CI workflows
  infra-repo/    -> Helm charts / K8s manifests (fuente de verdad para ArgoCD)

Por que GitOps?

Con GitOps, Git es la fuente de verdad. Toda la configuracion del cluster esta versionada. Los rollbacks son un simple git revert. Auditabilidad total de quien cambio que y cuando.

GitHub Actions

El workflow de CI se ejecuta en cada push a main y en cada Pull Request. Incluye testing, build de imagen Docker y push al registry.

yaml
# .github/workflows/ci.yml
name: CI Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          cache: "pip"

      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install -r requirements-dev.txt

      - name: Lint (Ruff)
        run: ruff check . --output-format=github

      - name: Type check (mypy)
        run: mypy app/ --ignore-missing-imports

      - name: Unit tests
        run: pytest tests/ -v --cov=app --cov-report=xml

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          file: coverage.xml

  build-and-push:
    needs: test
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

Container Registry

El registry almacena las imagenes Docker construidas por CI. GitHub Container Registry (ghcr.io) se integra nativamente con GitHub Actions.

RegistryURLVentaja
GitHub (GHCR)ghcr.ioIntegracion nativa con Actions + permisos GITHUB_TOKEN
Docker Hubdocker.ioRegistry publico mas grande, rate limits en gratuito
AWS ECR*.ecr.*.amazonaws.comIntegracion IAM, scanning de vulnerabilidades
Google AR*-docker.pkg.devIntegracion GKE, vulnerability scanning

Estrategia de tagging

bash
# Tags recomendados por imagen:
ghcr.io/org/backend:abc123f   # SHA del commit (inmutable)
ghcr.io/org/backend:1.4.2     # Semver (releases)
ghcr.io/org/backend:latest    # Ultimo build de main

# NUNCA usar :latest en produccion -> siempre SHA o semver

ArgoCD & GitOps

ArgoCD monitorea el repositorio de infraestructura y sincroniza automaticamente los manifiestos con el cluster de Kubernetes. Si detecta drift entre el estado del cluster y Git, puede corregirlo automaticamente.

yaml
# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: backend-production
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/org/infra-repo.git
    targetRevision: main
    path: charts/backend
    helm:
      valueFiles:
        - values-production.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true          # Eliminar recursos huerfanos
      selfHeal: true       # Corregir drift automaticamente
    syncOptions:
      - CreateNamespace=true

Auto-update de image tag

Usa GitHub Actions para hacer un commit automatico al infra-repo actualizando el image tag despues de cada build exitoso. ArgoCD detecta el cambio y despliega automaticamente.

Step de CI que actualiza infra-repo

yaml
# Paso adicional en CI despues del build
  update-manifest:
    needs: build-and-push
    runs-on: ubuntu-latest
    steps:
      - name: Checkout infra repo
        uses: actions/checkout@v4
        with:
          repository: org/infra-repo
          token: ${{ secrets.INFRA_REPO_TOKEN }}

      - name: Update image tag
        run: |
          cd charts/backend
          sed -i "s|tag:.*|tag: ${{ github.sha }}|" values-production.yaml
          git config user.name "CI Bot"
          git config user.email "ci@org.com"
          git add .
          git commit -m "chore: update backend to ${{ github.sha }}"
          git push

Environments

La separacion de entornos asegura que los cambios se validen progresivamente antes de llegar a produccion. Cada entorno tiene su propio namespace y configuracion en K8s.

EntornoTriggerNamespaceReplicasProposito
DevPush a branchdev1Testing rapido de features
StagingPR -> mainstaging2Validacion pre-produccion
ProductionMerge a mainproduction3-10Trafico real
text
Flujo de promocion entre environments

  feature-branch          main
       β”‚                     β”‚
       β”‚  PR created         β”‚
       β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Άβ”‚
       β”‚                     β”‚
   [ Dev ]              [ Staging ]         [ Production ]
   Auto-deploy          Auto-deploy          Auto-deploy
   on push              on PR merge          on manifest update
       β”‚                     β”‚                     β”‚
       β–Ό                     β–Ό                     β–Ό
   1 replica             2 replicas           3-10 replicas
   No monitoring         Alertas test         Full observability

Rollback Strategies

Cuando un deploy falla, necesitas volver al estado anterior de forma rapida y confiable.

GitOps rollback (recomendado)

bash
# En el infra-repo: revertir el commit que actualizo el tag
git revert HEAD
git push
# ArgoCD detecta el cambio y sincroniza al tag anterior

K8s rollback directo

bash
# Ver historial de rollouts
kubectl rollout history deployment/backend -n production

# Rollback a la revision anterior
kubectl rollout undo deployment/backend -n production

# Rollback a una revision especifica
kubectl rollout undo deployment/backend --to-revision=3 -n production

# Verificar estado
kubectl rollout status deployment/backend -n production

Rollback K8s vs GitOps

kubectl rollout undo causa drift entre el cluster y Git. ArgoCD intentara re-sincronizar al estado de Git. Siempre prefiere el rollback via Git para mantener la consistencia.

Branch Protection & Releases

Una estrategia solida de proteccion de ramas y releases automatizados garantiza que el codigo que llega a produccion sea estable, revisado y versionado correctamente. Combinar branch protection rules con semantic versioning y releases automatizados reduce errores humanos y acelera el ciclo de entrega.

Branch protection rules en GitHub

text
Configuracion recomendada para la rama main

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 Branch Protection: main                      β”‚
β”‚                                                             β”‚
β”‚  βœ“  Require pull request before merging                     β”‚
β”‚     β”œβ”€β”€ Required approvals: 1 (minimo)                      β”‚
β”‚     β”œβ”€β”€ Dismiss stale reviews on new commits                β”‚
β”‚     └── Require review from code owners                     β”‚
β”‚                                                             β”‚
β”‚  βœ“  Require status checks to pass                           β”‚
β”‚     β”œβ”€β”€ CI Pipeline / test                                  β”‚
β”‚     β”œβ”€β”€ CI Pipeline / lint                                  β”‚
β”‚     └── Require branches to be up to date                   β”‚
β”‚                                                             β”‚
β”‚  βœ“  Require signed commits                                  β”‚
β”‚  βœ“  Require linear history (squash or rebase)               β”‚
β”‚  βœ—  Allow force pushes: NEVER                               β”‚
β”‚  βœ—  Allow deletions: NEVER                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
bash
# Configurar branch protection via GitHub CLI
gh api repos/{owner}/{repo}/branches/main/protection \
  --method PUT \
  --field required_status_checks='{"strict":true,"contexts":["test","lint"]}' \
  --field enforce_admins=true \
  --field required_pull_request_reviews='{"required_approving_review_count":1,"dismiss_stale_reviews":true}' \
  --field restrictions=null

Conventional Commits y Semantic Versioning

El versionado semantico (SemVer) sigue el formato MAJOR.MINOR.PATCH. Combinado con Conventional Commits, las herramientas pueden determinar automaticamente el tipo de release.

Prefijo del commitTipo de cambioVersion bumpEjemplo
fix:Correccion de bugPATCH (1.0.0 -> 1.0.1)fix: resolver timeout en conexion DB
feat:Nueva funcionalidadMINOR (1.0.0 -> 1.1.0)feat: agregar endpoint de exportacion CSV
feat!: o BREAKING CHANGE:Cambio incompatibleMAJOR (1.0.0 -> 2.0.0)feat!: migrar auth a OAuth2
chore:MantenimientoSin releasechore: actualizar dependencias
docs:DocumentacionSin releasedocs: actualizar README de la API
ci:Pipeline CI/CDSin releaseci: agregar job de security scanning

Validar commits automaticamente

Usa commitlint con un hook de commit-msg (via Husky o pre-commit) para rechazar commits que no sigan la convencion. En CI, usa la action wagoid/commitlint-github-action para validar los commits del PR.

Release automatizado con release-please

release-please de Google analiza los commits desde la ultima release, genera un PR con el changelog y bump de version, y al mergearlo crea automaticamente el GitHub Release con tag.

yaml
# .github/workflows/release.yml
name: Release

on:
  push:
    branches: [main]

permissions:
  contents: write
  pull-requests: write

jobs:
  release:
    runs-on: ubuntu-latest
    outputs:
      release_created: ${{ steps.release.outputs.release_created }}
      tag_name: ${{ steps.release.outputs.tag_name }}
      version: ${{ steps.release.outputs.version }}
    steps:
      - name: Run release-please
        id: release
        uses: googleapis/release-please-action@v4
        with:
          release-type: python       # o node, go, etc.
          token: ${{ secrets.GITHUB_TOKEN }}

  # ── Build y deploy solo cuando se crea un release ──
  deploy:
    needs: release
    if: needs.release.outputs.release_created == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push release image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ghcr.io/${{ github.repository }}:${{ needs.release.outputs.version }}
            ghcr.io/${{ github.repository }}:latest

      - name: Update infra-repo with release version
        run: |
          git clone https://x-access-token:${{ secrets.INFRA_REPO_TOKEN }}@github.com/org/infra-repo.git
          cd infra-repo/charts/backend
          sed -i "s|tag:.*|tag: ${{ needs.release.outputs.version }}|" values-production.yaml
          git config user.name "Release Bot"
          git config user.email "release@org.com"
          git add .
          git commit -m "release: deploy backend ${{ needs.release.outputs.version }}"
          git push

Generacion de changelog

release-please genera automaticamente un CHANGELOG.md con el siguiente formato basandose en los conventional commits:

text
## [1.5.0](https://github.com/org/backend/compare/v1.4.2...v1.5.0) (2026-02-20)

### Features

* agregar endpoint de exportacion CSV ([#142](https://github.com/org/backend/pull/142))
* soporte para filtros avanzados en busqueda ([#138](https://github.com/org/backend/pull/138))

### Bug Fixes

* resolver timeout en conexion DB bajo carga ([#140](https://github.com/org/backend/pull/140))
* corregir paginacion en listado de usuarios ([#139](https://github.com/org/backend/pull/139))

### Documentation

* actualizar guia de la API con nuevos endpoints ([#141](https://github.com/org/backend/pull/141))

Alternativa: semantic-release

yaml
# .github/workflows/release-semantic.yml β€” Alternativa con semantic-release
name: Semantic Release

on:
  push:
    branches: [main]

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0       # Necesario para analizar historial
          persist-credentials: false

      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install semantic-release
        run: |
          npm install -g semantic-release \
            @semantic-release/changelog \
            @semantic-release/git \
            conventional-changelog-conventionalcommits

      - name: Run semantic-release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: npx semantic-release
json
// .releaserc.json β€” Configuracion de semantic-release
{
  "branches": ["main"],
  "plugins": [
    ["@semantic-release/commit-analyzer", {
      "preset": "conventionalcommits"
    }],
    ["@semantic-release/release-notes-generator", {
      "preset": "conventionalcommits"
    }],
    ["@semantic-release/changelog", {
      "changelogFile": "CHANGELOG.md"
    }],
    ["@semantic-release/git", {
      "assets": ["CHANGELOG.md", "package.json"],
      "message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}"
    }],
    "@semantic-release/github"
  ]
}

release-please vs semantic-release

release-please: crea un PR de release que debes mergear manualmente, dando control sobre cuando publicar. Ideal para equipos que quieren revisar el changelog.
semantic-release: publica automaticamente en cada push a main si hay commits relevantes. Ideal para proyectos con CI/CD completamente automatizado.

END OF DOCUMENT

ΒΏNecesitas mΓ‘s? Volver a la LibrerΓ­a →