La Implementación de GitOps Más Simple Que Realmente Funciona


Introducción

En este artículo vamos a reducir GitOps a sus elementos esenciales y construir la implementación más simple que realmente funciona. Sin operadores sofisticados, sin herramientas complejas - solo Git, GitHub Actions y un poco de magia de automatización.


Después de explorar diferentes enfoques de GitOps en mi artículo anterior, me di cuenta de que a veces pensamos demasiado las cosas. A veces, todo lo que necesitás es un pipeline simple y confiable que haga el trabajo. Construyamos exactamente eso.


El objetivo acá es simple:

  • Un repositorio para tu código de aplicación
  • Un repositorio para tus manifiestos de Kubernetes
  • Un workflow que los conecte
  • Cero infraestructura adicional más allá de lo que ya tenés

Solo GitOps puro y simple que podés entender, depurar y mantener sin un doctorado en tecnologías cloud-native.


Lo que vamos a construir

Vamos a implementar un flujo de trabajo GitOps basado en push que:

  1. Construye y prueba tu aplicación
  2. Crea una imagen de contenedor con versionado apropiado
  3. La pushea a GitHub Container Registry (¡gratis con GitHub!)
  4. Actualiza tus manifiestos de Kubernetes automáticamente
  5. Agrega escaneo de seguridad porque, bueno, no somos cowboys

Toda la configuración requiere solo dos repositorios y un workflow de GitHub Actions. Eso es todo. Sin servicios adicionales, sin facturas mensuales, sin configuraciones complejas.


El Repositorio de la Aplicación

Primero, hablemos del repositorio de la aplicación. Acá es donde vive tu código, y donde los desarrolladores pasan la mayor parte del tiempo. Lo único específico de GitOps acá es el workflow de CI/CD.


Esto es lo que pasa cuando pusheás código:

name: CI/CD Pipeline

on:
  push:
    branches: [ master ]
    tags: [ 'v*' ]

Disparador simple - push a master o creá un tag, y la magia comienza. Sin webhooks para configurar, sin servicios externos para integrar. GitHub maneja todo.


Paso 1: Testing (Porque Somos Profesionales)
test:
  runs-on: ubuntu-latest
  steps:
  - uses: actions/checkout@v4
  
  - name: Set up Go
    uses: actions/setup-go@v4
    with:
      go-version: '1.24'
  
  - name: Run tests
    run: go test -v ./...

Nada sofisticado acá. Checkout del código, configurar el entorno, ejecutar las pruebas. Si las pruebas fallan, nada más sucede. Esta es tu primera puerta de calidad, y no es negociable.


Paso 2: Construir y Pushear el Contenedor

Acá es donde las cosas se ponen interesantes. Estamos usando GitHub Container Registry (ghcr.io) porque es gratis, está integrado y simplemente funciona:

build-and-push:
  needs: test
  runs-on: ubuntu-latest
  
  permissions:
    contents: read
    packages: write

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

Estamos usando GITHUB_TOKEN para el registry de github, no necesitás crear y gestionar credenciales del registro. GitHub proporciona este token automáticamente con los permisos justos. Un secreto menos para rotar, una cosa menos de qué preocuparse.


La estrategia de etiquetado de imágenes es donde ocurre la magia:

- name: Extract metadata
  id: meta
  uses: docker/metadata-action@v5
  with:
    images: ghcr.io/${{ github.repository }}
    tags: |
      type=sha,prefix=,suffix=,format=short
      type=ref,event=branch
      type=raw,value=latest,enable={{is_default_branch}}

Etiquetamos las imágenes con el SHA del commit. ¿Por qué? Porque los SHAs son inmutables, únicos y te dicen exactamente qué código está corriendo en producción. No más pesadillas con “latest”, no más conflictos de versiones.


Paso 3: Escaneo de Seguridad

Antes de desplegar cualquier cosa, asegurémonos de que no estamos enviando vulnerabilidades:

security-scan:
  needs: build-and-push
  runs-on: ubuntu-latest
  
  steps:
  - name: Run Trivy vulnerability scanner
    uses: aquasecurity/trivy-action@master
    with:
      image-ref: ghcr.io/${{ github.repository }}:${{ github.sha }}
      format: 'sarif'
      output: 'trivy-results.sarif'

Trivy escanea nuestra imagen en busca de vulnerabilidades conocidas y las reporta directamente a la pestaña de Seguridad de GitHub. Si se encuentran vulnerabilidades críticas, lo sabrás inmediatamente. Sin dashboards externos, sin logins adicionales - todo permanece en GitHub.


Paso 4: La Magia de GitOps - Actualizando Manifiestos

Acá es donde GitOps realmente sucede. Después de que nuestra imagen está construida y escaneada, actualizamos el repositorio de manifiestos:

update-manifests:
  needs: [build-and-push, security-scan]
  runs-on: ubuntu-latest
  
  steps:
  - name: Checkout manifest repository
    uses: actions/checkout@v4
    with:
      repository: tu-org/tus-manifiestos
      token: ${{ secrets.MANIFEST_REPO_TOKEN }}
      path: manifests

  - name: Update deployment image
    working-directory: manifests
    run: |
      yq eval '.spec.template.spec.containers[0].image = "ghcr.io/${{ github.repository }}:${{ github.sha }}"' \
        -i deployment.yaml
      
  - name: Commit and push changes
    working-directory: manifests
    run: |
      git config --local user.email "[email protected]"
      git config --local user.name "GitHub Action"
      
      git add deployment.yaml
      git commit -m "Update image to ${{ github.sha }}"
      git push

Esto es hermoso en su simplicidad. Hacemos checkout del repo de manifiestos, actualizamos el tag de la imagen usando yq, commiteamos el cambio y pusheamos. Eso es todo. Tus manifiestos ahora reflejan la versión exacta que se acaba de construir.


El Repositorio de Manifiestos

El repositorio de manifiestos es aún más simple. Contiene tus archivos YAML de Kubernetes y… eso es todo. Sin scripts, sin pipelines, solo configuración declarativa:

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mi-app
spec:
  template:
    spec:
      containers:
      - name: app
        image: ghcr.io/tu-org/tu-app:abc123f
        # Este SHA se actualiza automáticamente por CI

Cada cambio en este repositorio está rastreado en Git. Podés ver quién desplegó qué, cuándo y por qué. ¿Necesitás hacer rollback? Solo revertí el commit. ¿Necesitás ver qué está corriendo en producción? Mirá la rama principal.


Configurándolo

¿Listo para implementar esto? Acá está tu checklist:


1. Crear un Token de Acceso Personal

# Andá a GitHub Settings > Developer settings > Personal access tokens
# Creá un token con scope 'repo' para el repositorio de manifiestos
# Guardalo como MANIFEST_REPO_TOKEN en los secrets del repo de tu app

2. Crear Tu Repositorio de Manifiestos

mkdir k8s-manifests
cd k8s-manifests
git init

# Agregá tus archivos YAML de Kubernetes
cp /ruta/a/tus/*.yaml .

git add .
git commit -m "Manifiestos iniciales"
git push

3. Agregar el Workflow Copiá el workflow a .github/workflows/ci.yaml en tu repositorio de aplicación. Actualizá los nombres de los repositorios y listo.


4. Desplegar en Tu Cluster Ahora, tenés dos opciones:

Opción A: Sincronización manual (más simple)

kubectl apply -f https://raw.githubusercontent.com/tu-org/tus-manifiestos/main/deployment.yaml

Opción B: Sincronización automatizada Configurá un CronJob simple en tu cluster que hace pull y aplica los cambios:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: gitops-sync
spec:
  schedule: "*/5 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: sync
            image: bitnami/kubectl:latest
            command:
            - /bin/sh
            - -c
            - |
              kubectl apply -f https://raw.githubusercontent.com/tu-org/tus-manifiestos/main/

Eso es todo. Cada 5 minutos, tu cluster verifica cambios y los aplica. Sin operadores, sin controladores, solo un simple cron job.


Por Qué Esto Funciona

Este enfoque puede parecer demasiado simple, pero es exactamente por eso que funciona:

  • Sin curva de aprendizaje: Si conocés Git y CI/CD básico, estás listo
  • Depurable: Cuando algo se rompe, podés ver exactamente dónde y por qué
  • Portable: Funciona con cualquier cluster de Kubernetes, en cualquier lugar
  • Auditable: Cada cambio está en Git con historial completo
  • Gratis: Usa solo las características gratuitas de GitHub
  • Seguro: Superficie de ataque mínima, seguridad estándar de GitHub

Cuándo Usar Esto

Esta configuración es perfecta para:

  • Equipos pequeños a medianos comenzando con GitOps
  • Proyectos donde la simplicidad supera a las características
  • Equipos que quieren entender todo su pipeline
  • Situaciones donde no podés instalar herramientas adicionales en el cluster

Probablemente no sea ideal si necesitás:

  • Despliegues multi-cluster
  • Estrategias de despliegue complejas (canary, blue-green)
  • Rollback automático basado en métricas
  • Multi-tenancy con RBAC estricto

¿Pero sabés qué? Siempre podés agregar esas características más tarde. Empezá simple, entendé lo básico, después agregá complejidad solo cuando realmente la necesites.


Problemas Comunes y Soluciones

¿La imagen no se actualiza? Verificá que tu política de pull de imagen no esté configurada en IfNotPresent con un tag que no cambia. Usar SHAs resuelve esto automáticamente.


¿Token del repo de manifiestos expirado? Usá tokens de acceso personal de GitHub con fechas de vencimiento más largas, o mejor aún, usá una GitHub App para producción.


¿Necesitás hacer rollback rápidamente?

git revert HEAD
git push
# Esperá la sincronización, o aplicá manualmente

Conclusión

GitOps no tiene que ser complicado. Esta configuración simple te da el 90% de los beneficios con el 10% de la complejidad. Obtenés control de versiones, despliegues automatizados, escaneo de seguridad y auditoría completa con solo un workflow de GitHub Actions.


Empezá acá, sentite cómodo con los conceptos, y después explorá herramientas más avanzadas como ArgoCD o Flux cuando realmente necesites sus características. Recordá, la mejor implementación de GitOps es la que tu equipo puede entender y mantener.


A veces, la solución más simple es la mejor solución. Y en este caso, simple no significa amateur - significa enfocado, mantenible y listo para producción.


¡Espero que te haya sido útil y hayas disfrutado leyéndolo, hasta la próxima!


No tienes cuenta? Regístrate aqui

Ya registrado? Iniciar sesión a tu cuenta ahora.

Iniciar session con GitHub
Iniciar sesion con Google
  • Comentarios

    Online: 0

Por favor inicie sesión para poder escribir comentarios.

by Gabriel Garrido