Pipeline de CI/CD: De GitHub a Producción en 5 Minutos

CI/CD Kubernetes GitHub Actions DevOps
6 minutos de lectura

Todos hemos visto el diagrama perfecto: un desarrollador hace push a main, se ejecutan los tests, se construye la imagen Docker, se despliega en Kubernetes y en minutos el cambio está en producción. Limpio. Automático. Elegante.

La realidad es otra.

Antes de que ese pipeline funcione, alguien en tu equipo tuvo que resolver un rompecabezas de accesos, credenciales, configuraciones de red y permisos que fácilmente puede llevar semanas — no minutos. Y lo peor: cada acceso mal configurado es una puerta abierta a tu infraestructura de producción.

// El problema

🧩Lo que ningún tutorial te muestra

Cualquier tutorial te muestra cómo escribir un archivo .github/workflows/deploy.yml con 30 líneas de YAML. Lo que ninguno muestra es todo lo que tiene que existir antes de que ese YAML funcione. Para que tu pipeline pueda desplegar en un cluster de EKS en una VPC privada, necesitás resolver tres accesos críticos:

ACCESO // 01
Acceso a la red privada
Tu cluster vive en subnets privadas. El pipeline corre fuera de tu red. Alguien tiene que tender el puente.
ACCESO // 02
Autenticación en Kubernetes
Una vez dentro de la red, el pipeline necesita autenticarse contra el API server con permisos exactos.
ACCESO // 03
Registry de imágenes
El pipeline pushea la imagen Docker. El cluster tiene que poder pulllearla. Son dos flujos de auth diferentes.

Tres accesos. Tres conjuntos de credenciales. Tres vectores de ataque si se configuran mal.

// 01

🔒Conectar el pipeline a tu red privada

Si seguiste las buenas prácticas, tu cluster vive en subnets privadas — no es accesible desde internet. Correcto desde la perspectiva de seguridad. Problemático para el CI/CD: GitHub Actions corre en runners efímeros fuera de tu red y no puede hablar con tu cluster.

Las opciones y sus costos

Hacer público el API server: terrible para la seguridad. Configurar VPN en el pipeline: complejo y frágil. Self-hosted runners dentro de tu VPC: otro servidor que mantener, parchar y monitorear.

Si elegís la ruta de VPN — la más común — tu pipeline empieza así solo para resolver la conectividad de red:

+15 líneas · 4 secrets · solo para “entrar” a la red
deploy.yml — solo el paso de VPN
Acceso 01
      - name: Install OpenVPN
        run: sudo apt-get update && sudo apt-get install -y openvpn

      - name: Connect to VPN
        run: |
          echo "${{ secrets.VPN_CONFIG }}" > vpn.ovpn
          echo "${{ secrets.VPN_CA_CERT }}" > ca.crt
          echo "${{ secrets.VPN_CLIENT_CERT }}" > client.crt
          echo "${{ secrets.VPN_CLIENT_KEY }}" > client.key
          sudo openvpn --config vpn.ovpn \
            --ca ca.crt --cert client.crt --key client.key \
            --daemon
          sleep 15
El riesgo silencioso

Si los certificados expiran un viernes a las 6 PM, nadie despliega hasta el lunes. Los certificados VPN son un punto de falla que nadie recuerda hasta que producción está frenada.

// 02

🔑Autenticarse contra Kubernetes

Estás dentro de la red. Ahora necesitás que el pipeline pueda ejecutar comandos contra el cluster. La opción más segura es OIDC Federation — el pipeline asume un IAM Role sin Access Keys estáticas. Suena limpio. Para que funcione necesitás coordinar al menos 4 piezas diferentes entre AWS IAM y Kubernetes.

IAM Trust Policy + RBAC Kubernetes
Acceso 02
// Trust policy del IAM Role para GitHub Actions (OIDC)
{
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringLike": {
        "token.actions.githubusercontent.com:sub": "repo:mi-org/mi-repo:ref:refs/heads/main"
      }
    }
  }]
}

# RBAC en Kubernetes - qué puede hacer el pipeline
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: deployer-role
rules:
  - apiGroups: ["apps"]
    resources: ["deployments","replicasets"]
    verbs: ["get","list","watch","update","patch"]
El error más frustrante

Si cualquier pieza no encaja — y hay muchas piezas — el pipeline falla con un error críptico de Unauthorized que puede tardar horas en diagnosticar. No siempre es claro cuál de las 4 capas (OIDC, IAM, aws-auth, RBAC) es la que falló.

// 03

📦Push y Pull de imágenes Docker

Tu pipeline construye la imagen. Esa imagen necesita ir a un registry donde el cluster pueda descargarla. Son dos flujos de autenticación diferentes: el pipeline pushea, el cluster pullea. Si usás Amazon ECR, el token de autenticación dura 12 horas y necesitás renovarlo antes de cada push.

ECR login + build + push + deploy
Acceso 03
      - name: Login to Amazon ECR
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build and Push
        run: |
          docker build -t $REGISTRY/my-app:${{ github.sha }} .
          docker push $REGISTRY/my-app:${{ github.sha }}

      - name: Deploy to Kubernetes
        run: |
          kubectl set image deployment/my-app \
            my-app=$REGISTRY/my-app:${{ github.sha }} \
            -n production
Lo que tiene que existir antes

El ECR repository tiene que existir con lifecycle policies configuradas para no acumular imágenes infinitas, con permisos de push para el rol del pipeline y permisos de pull para los nodos del cluster. Si usás cuentas AWS separadas por entorno, sumale Cross-Account Policies.

// El resultado

📜El panorama completo: 50+ líneas y semanas de config previa

Cuando juntás los 3 accesos en un solo pipeline, el resultado es esto — en la versión simplificada, sin tests, linting, security scanning, multi-environment ni rollback automático:

50+ líneas · versión simplificada · sin los casos de uso reales
deploy.yml — pipeline completo “simple”
Complejo
name: Deploy to Production
on:
  push:
    branches: [main]

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

      # ACCESO 1: VPN (~15 líneas + 4 secrets)
      - name: Install and Connect VPN
        run: |
          sudo apt-get install -y openvpn
          echo "${{ secrets.VPN_CONFIG }}" > vpn.ovpn
          echo "${{ secrets.VPN_CA_CERT }}" > ca.crt
          echo "${{ secrets.VPN_CLIENT_CERT }}" > client.crt
          echo "${{ secrets.VPN_CLIENT_KEY }}" > client.key
          sudo openvpn --config vpn.ovpn \
            --ca ca.crt --cert client.crt --key client.key --daemon
          sleep 15

      # ACCESO 2: AWS + Kubernetes (~10 líneas + IAM/OIDC/RBAC previo)
      - name: Configure AWS (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
          aws-region: us-east-1

      - name: Connect to EKS
        run: aws eks update-kubeconfig --name my-cluster --region us-east-1

      # ACCESO 3: Registry (~10 líneas + ECR policies previas)
      - name: Login to ECR
        id: ecr
        uses: aws-actions/amazon-ecr-login@v2

      # BUILD + DEPLOY
      - name: Build, Push and Deploy
        run: |
          docker build -t ${{ steps.ecr.outputs.registry }}/my-app:${{ github.sha }} .
          docker push ${{ steps.ecr.outputs.registry }}/my-app:${{ github.sha }}
          kubectl set image deployment/my-app \
            my-app=${{ steps.ecr.outputs.registry }}/my-app:${{ github.sha }} \
            -n production
          kubectl rollout status deployment/my-app -n production --timeout=300s

      - name: Disconnect VPN
        if: always()
        run: sudo killall openvpn || true

Pero el YAML es solo la punta del iceberg. Para que estas líneas funcionen, previamente necesitaste configurar todo esto:

Configuración previa requerida antes del primer deploy
Qué configurar Dónde
Servidor VPN desplegado y configuradoAWS VPC
Certificados VPN generados y distribuidosInfra GitHub Secrets
OIDC Identity ProviderAWS IAM
IAM Role con trust policy específicaAWS IAM
aws-auth ConfigMap mapeadoKubernetes
RBAC — ClusterRole + ClusterRoleBindingKubernetes
ECR Repository con lifecycle policiesAWS ECR
IAM Policy de pull en nodos del clusterAWS IAM
6+ secrets configurados en el repositorioGitHub Settings
El costo real en tiempo

Configurar todo esto correctamente le toma a un equipo experimentado entre 3 y 10 días hábiles. A un equipo que lo hace por primera vez, fácilmente 2 a 4 semanas. Y después viene el mantenimiento: certificados que expiran, tokens que rotan, APIs que se deprecan, versiones de Kubernetes que cambian. Cada incidente son horas de debugging y deploys frenados.

// La alternativa

5 minutos reales con SleakOps

¿Y si toda esa infraestructura previa — la VPN, los roles IAM, el cluster, el registry, los permisos de RBAC — ya existiera configurada y segura? ¿Y si solo tuvieras que copiar un comando en tu pipeline?

1 comando · sin secrets que mantener · sin certificados que expiran
deploy.yml — con SleakOps CLI
Simple
name: Deploy to Production
on:
  push:
    branches: [main]

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

      - name: Deploy with SleakOps
        run: |
          sleakops deploy \
            --app my-app \
            --env production \
            --image-tag ${{ github.sha }}
Lo que hace ese único comando

La CLI se encarga de conectarse a tu red privada, autenticarse contra el cluster, construir y pushear la imagen al registry, desplegar en Kubernetes y verificar que el rollout fue exitoso. Todo con credenciales temporales y menor privilegio por defecto. Y funciona igual en GitHub Actions, GitLab CI, Bitbucket Pipelines o cualquier sistema que pueda ejecutar bash.

Pipeline manual vs. SleakOps
Pipeline manual Con SleakOps
50+ líneas de YAML solo para los 3 accesos1 comando: sleakops deploy
6+ secrets que mantener y rotar manualmenteSin secrets en el pipeline — credenciales temporales
Certificados VPN que expiran en el peor momentoConectividad gestionada automáticamente
3–10 días de config para un equipo experimentadoFunciona desde el primer deploy
Mantenimiento continuo de cada capaActualizaciones gestionadas por SleakOps
Solo GitHub Actions (config específica por plataforma)GitHub, GitLab, Bitbucket o cualquier CLI

Tu pipeline no debería ser un proyecto de infraestructura

La pregunta no es si tu equipo puede configurar todo esto manualmente. Seguramente puede. La pregunta es: ¿debería invertir semanas en plomería de infraestructura, o en construir producto?

Conocé SleakOps →

¿Listo para optimizar tu nube?

Agenda una demo con uno de nuestros expertos para mostrarte cómo Sleakops puede transformar tu operación en la nube.

Artículos relacionados