Saltar a contenido

Regla 16: Despliegue Continuo (CD portable)

Premisa: Premisa

El CD debe ser reproducible desde local y portable entre proveedores de CI. make construye artefactos; just orquesta operativas (desplegar a server/k8s, publicar release, conectar vía ssh). El CD no depende de secrets del proveedor, excepto la clave privada age para descifrar. Los secretos de la aplicación viven cifrados en .secrets/secrets.<env>.enc.yaml (sops+age, regla 13). Los pipelines externos solo parametrizan/inyectan variables que las tareas locales esperan. Cambiar de proveedor (GitHub → GitLab → Jenkins) no rompe el CD.

tags: [obligatorio]

Estructura: Estructura

Componentes del CD portable

proyecto/
├── .secrets/
│   └── secrets.<env>.enc.yaml           # secretos de app/servicio destino (sops+age)
├── .sops.yaml                          # age recipients públicos
├── ~/.age/<proyecto>-key.txt           # local: clave privada age
│                                        # pipeline: env del proveedor (AGE_KEY)
├── Makefile → helpers/mk/*.mk          # construcción: build, test, package
├── Justfile → helpers/just/*.just      # operativas: deploy, k8s-push, ssh, release
│   ├── cd.just                         # @deploy, @k8s-push, @ssh-deploy
│   └── github.just                     # @release (→ github.sh → gh)
├── helpers/shell/secrets.sh            # edit, env, verify, keygen
├── helpers/shell/deploy.sh             # descifra secretos + despliega
└── .github/workflows/*.yml             # wrapper: inyecta AGE_KEY, dispara make/just

Flujo de despliegue

Local:
  just edit-secrets prod  → .secrets/secrets.prod.enc.yaml (cifrado)
  just env prod           → descifra → .env.prod (no versionado)
  just deploy prod        → make package + secrets env prod + deploy.sh → server/k8s

Pipeline (wrapper):
  AGE_KEY inyectado por proveedor
  → just env prod (descifra con AGE_KEY) → .env.prod
  → make package + just deploy prod → CD ejecutado localmente

Secretos en el CD

Fuente Secretos de app/servicio destino Secrets de plataforma/proveedor
.secrets/*.enc.yaml (sops+age) Sí — tokens de API, passwords DB, claves de registry destino, SSH keys destino No — GH_TOKEN, GITLAB_TOKEN, etc.
Pipeline/proveedor No — no debe depender Sí — GH_TOKEN, GITLAB_TOKEN (cada proveedor tiene su variable)
Clave privada age .secrets/ no la versiona; local en ~/.age/ El proveedor la inyecta (env AGE_KEY) para descifrar en tiempo de ejecución

Responsabilidades Makefile vs Justfile

  • Makefile (construcción/artefactos): build, test, package, image, release-build — genera el artefacto desplegable.
  • Justfile (task manager — operativas): deploy <env>, k8s-push, ssh-deploy, release — orquesta make package + despliegue al destino. just release → make package + github.just → gh release create.

tags: [opcional]

Nombre Sugerido: Nombres Sugeridos

  • Secretos de deploy: .secrets/secrets.<env>.enc.yaml (prod, dev, int, staging...).
  • Clave age: local ~/.age/<proyecto>-key.txt (nombre del repo en kebab-case); en pipeline: AGE_KEY (env del proveedor).
  • Scripts: deploy.sh (operativa de deploy), secrets.sh (cifrado/descifrado).
  • Módulos Just: cd.just (deploy, k8s, ssh), github.just (release).
  • Targets Makefile: package (construir artefacto), release-build (construir todo lo necesario para el release).

tags: [opcional]

Comando: Comandos

Construcción (Makefile)

make build               # Compilar
make test                # Ejecutar tests
make package             # Generar artefacto (wheel, jar, tar)

Operativas (Justfile)

just deploy prod         # → secrets.sh env prod + make package + deploy.sh → server
just deploy staging      # → secrets.sh env staging + make package + deploy.sh → staging server

just k8s-push            # → make package + kubectl apply / docker push
just ssh-deploy host     # → secrets.sh env prod + rsync + ssh restart

just release             # → make package + github.just → gh release create

Secretos del deploy

just edit-secrets prod   # → sops edit .secrets/secrets.prod.enc.yaml
just env prod            # → descifra → .env.prod
just env prod --export   # → exporta variables al entorno (para uso en deploy)

Pipeline (wrapper parametrizador)

- name: Deploy to production
  env:
    AGE_KEY: ${{ secrets.AGE_KEY }}      # único secret del proveedor
  run: |
    echo "$AGE_KEY" > ~/.age/proyecto-key.txt
    just deploy prod

tags: [opcional]

Ejemplo: Ejemplos

Desplegar a un servidor vía SSH

# Local: cifrar credenciales
just edit-secrets prod
# .secrets/secrets.prod.enc.yaml contiene:
#   SSH_HOST=mi-server.com
#   SSH_USER=deployer
#   SSH_KEY_PATH=~/.ssh/deploy_key
#   APP_PORT=8080

# Local: desplegar
just ssh-deploy mi-server.com
# → just env prod (descifra)
# → make package
# → rsync + ssh restart

Pipeline de GitHub Actions (wrapper parametrizador)

name: Deploy

on:
  push:
    branches: [main]

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

      - name: Decrypt and deploy
        env:
          AGE_KEY: ${{ secrets.AGE_KEY }}
        run: |
          echo "$AGE_KEY" > ~/.age/proyecto-key.txt
          just deploy prod

Publicar release en GitHub

# Local: publicar release
just release
# → make package (construye artefacto)
# → github.just → github.sh → gh release create

El pipeline externo solo necesita inyectar AGE_KEY para descifrar. Si el proveedor no tiene secrets, el desarrollador ejecuta el CD directamente desde local (sin pipeline).

tags: [obligatorio]

Restriccion: Restricciones

  • El CD no depende de secrets del proveedor, excepto la clave privada age (AGE_KEY) para descifrar. Todos los demás secretos viven en .secrets/*.enc.yaml.
  • Excepción deliberada: los secrets de plataforma/proveedor (GH_TOKEN, GITLAB_TOKEN, DOCKER_REGISTRY_TOKEN, etc.) no van en .secrets/. Cada proveedor tiene su propia variable; viven en el proveedor. Si se migra de GitHub a GitLab, esos secrets se recrean en el nuevo proveedor. Los secrets de la aplicación destino NO cambian.
  • Cambiar de proveedor no rompe el CD. Las tareas locales (make package, just deploy prod) son la fuente de verdad. Los pipelines solo las parametrizan.
  • Los pipelines externos son wrappers/parametrizadores. Inyectan variables (AGE_KEY, envs del proveedor) y disparan make/just. Nunca reimplementan lógica de deploy.
  • El CD es local-first. just deploy prod debe funcionar en cualquier máquina del equipo sin necesidad de CI remoto.
  • sops, age, y las herramientas destino (docker/podman, kubectl, gh, ssh) deben estar instaladas localmente (documentado en la instalación de hooks).
  • El deploy.sh nunca hardcodea credenciales ni hosts. Recibe todo por flags o variables de entorno descifradas de .secrets/.
  • just release es operativa → Justfile, no Makefile (publicar no es construir, es orquestar make package + gh).

tags: [obligatorio]

Referencia: Referencias

tags: [obligatorio]

Plantilla: Plantilla

tags: [opcional]