🚀 Automatiza tus despliegues como un profesional y duerme tranquilo 😎

Guía paso a paso para implementar un flujo CI/CD robusto con herramientas enterprise de código abierto.

Flujo de trabajo CI/CD: Diagrama que ilustra un proceso automatizado de integración y despliegue continuo, incluyendo herramientas como Terraform, Kubernetes, Argo CD y GitHub Actions para la gestión de infraestructura y despliegues.

Con esta guía, te llevaré de la mano para configurar un flujo de trabajo automatizado que garantiza un ciclo de integración y despliegue (CI/CD) eficiente, desde la infraestructura hasta la aplicación.

🔥 ¿Por qué esta arquitectura?

GitOps es una filosofía que lleva el enfoque DevOps un paso más allá, basándose en la idea de que el repositorio Git es la "fuente de verdad". A diferencia de DevOps tradicional, GitOps automatiza todo el ciclo de vida de despliegue directamente desde Git.

Con GitOps, los cambios en el repositorio Git se reflejan de manera automática en el clúster de Kubernetes, asegurando consistencia, trazabilidad y una recuperación ante desastres mucho más rápida.

Esta arquitectura resuelve problemas comunes como:

  • Despliegues manuales propensos a errores
  • Configuraciones inconsistentes entre entornos
  • Ambientes difíciles de replicar

Además, uno de los beneficios clave es la facilidad para realizar rollback a una versión anterior de la infraestructura o de la aplicación. Como todos los cambios están versionados en Git, puedes revertir rápidamente cualquier cambio simplemente volviendo a un commit anterior y permitiendo que Argo CD sincronice el estado del clúster con esa versión.

Con esta combinación, los despliegues se vuelven predecibles, auditables y completamente automatizados, mejorando la eficiencia y seguridad en todo el ciclo de vida del software.

Herramientas clave del ecosistema:

  • Terraform: Infraestructura como código (IaC) en GCP para gestionar recursos de manera declarativa.
  • Kubernetes (GKE): Orquestación de contenedores a gran escala.
  • Argo CD: Implementa GitOps para sincronizar automáticamente el estado de tu repositorio con el clúster Kubernetes.
  • Helm: Facilita la gestión y despliegue de aplicaciones en Kubernetes mediante charts reutilizables.
  • GitHub Actions: CI/CD integrado que automatiza el ciclo completo desde el código hasta el despliegue, asegurando actualizaciones automáticas de Argo CD en Kubernetes.

Resultado:

Despliegues predecibles, auditoría completa de cambios y recuperación ante desastres en minutos. ✨

🛠 Pasos Clave: Arquitectura y Flujo del Proyecto

1. Estructura del Proyecto: Claridad ante todo

├── docker/                 # Configuraciones de contenedores
│   ├── mysql/
│   ├── nginx/
│   ├── php/
│   └── php-fpm/
├── infra/                  # Infraestructura como código
│   └── gcp/
│       ├── modules/        # Módulos Terraform
│       └── main.tf         # Configuración principal
├── kubernetes/             # Manifiestos para Argo CD
│   ├── argocd/
│   └── manifests/
└── laravel/                # Código fuente de la app

🧠 ¿Por qué importa tener un proyecto bien estructurado?

✅ Separación clara entre infraestructura, aplicación y configuración

GitOps permite manejar cada parte de forma desacoplada y versionada:

  • Infraestructura: Terraform
  • Configuración: Helm o manifiestos YAML
  • Código de la aplicación: Laravel

🔁 Módulos reutilizables para distintos entornos (dev/prod)

Con Terraform y Helm, puedes definir módulos genéricos reutilizables con variables para cada entorno (desarrollo, staging, producción), manteniendo consistencia entre ambientes.

⚙️ Configuraciones específicas por servicio

Cada componente tiene su propia configuración declarativa:

  • PHP-FPM: variables de entorno para rendimiento y workers.
  • Nginx: configurado con reglas de Ingress Controller.
  • MySQL (Cloud SQL): seguridad reforzada mediante conexión privada y secretos.

📌 Esta estructura modular mejora la trazabilidad, la seguridad y la automatización de tu arquitectura de despliegue.

2. ⚙️ Configuración Docker: Optimización para tu código

En este proyecto estamos usando PHP con el framework Laravel, y para ello se diseñó un Dockerfile multi-etapa que optimiza la construcción de la imagen final y separa responsabilidades claramente entre dependencias de PHP, assets de frontend y entorno de ejecución.

🐳 Dockerfile Multi-etapa (PHP-FPM + Nginx)

# Stage 1: Instalación de dependencias
FROM composer:latest AS composer_deps
WORKDIR /app
COPY . .
RUN composer install --no-dev --optimize-autoloader

# Stage 2: Build de assets
FROM node:20 AS node_deps
WORKDIR /app
COPY . .
RUN npm ci && npm run build

# Stage 3: Imagen final
FROM php:8.3-fpm
WORKDIR /var/www
COPY --from=composer_deps /app/vendor ./vendor
COPY --from=node_deps /app/public/build ./public/build
COPY docker/nginx/default.conf /etc/nginx/conf.d/default.conf

✅ Puntos clave

  • 🔽 Reduce el tamaño final de la imagen Las etapas de instalación y build no se incluyen en la imagen de producción.
  • ⚙️ Configuración específica para PHP-FPM y Nginx Permite controlar cada servicio según las necesidades del proyecto.
  • 🔒 Uso de usuario no-root (appuser) Mejora la seguridad del contenedor en producción.
  • 📦 Separación de responsabilidades Composer y Node manejan sus dependencias por separado, haciendo el build más limpio y controlado.

🧠 Esta estructura es ideal para producción: ligera, segura y desacoplada. También permite integración perfecta con GitOps y pipelines CI/CD.

3. ☁️ Autenticación en GCP: Prepárate para la nube

Antes de desplegar cualquier recurso en Google Cloud Platform (GCP), es fundamental configurar correctamente la autenticación y el proyecto donde vamos a trabajar.

📥 Instalación de gcloud CLI

Primero, instalamos la herramienta de línea de comandos oficial de GCP (gcloud) para poder interactuar con los servicios de Google desde nuestra terminal.

👉 Documentación oficial aquí

🔐 Autenticación y configuración básica

Una vez instalado gcloud, autenticamos nuestro usuario y seleccionamos el proyecto de trabajo:

# Inicia sesión en tu cuenta de Google
gcloud auth login

# Define el proyecto donde se desplegarán los recursos
gcloud config set project {nombredelproyecto-id}

# Establece las credenciales por defecto para las aplicaciones
gcloud auth application-default login

Nota: El comando gcloud auth application-default login es esencial cuando usas herramientas que requieren autenticación explícita con Application Default Credentials (ADC), como Terraform de GCP.


🛡️ Permisos (IAM) necesarios

Para realizar despliegues de infraestructura y gestionar servicios como Kubernetes, Cloud SQL y Storage, el usuario debe tener asignados los siguientes roles:

  • roles/owner — Permisos completos sobre el proyecto (útil para desarrollo y pruebas).
  • roles/cloudsql.admin — Permite crear y administrar instancias de Cloud SQL.
  • roles/storage.admin — Control total sobre buckets de almacenamiento.
  • roles/container.admin — Administración de clústeres de Kubernetes (GKE).

⚠️ Importante: Si trabajás en entornos de producción, es recomendable aplicar el principio de mínimos privilegios y asignar únicamente los roles estrictamente necesarios para cada tarea.

4. ⚙️ Infraestructura como código con Terraform

En este proyecto usamos Terraform para definir y desplegar infraestructura en GCP de forma declarativa. Todo está dividido en módulos reutilizables, lo que facilita el mantenimiento, la escalabilidad y la organización.

🗂️ Estructura de módulos

modules/
├── cloud-sql/          # MySQL gestionado por GCP (Cloud SQL)
├── gke-cluster/        # Clúster de Kubernetes (GKE)
└── helm-deploy/        # Instalación de Argo CD vía Helm

Cada módulo contiene su propio main.tf, variables.tf y outputs.tf, lo cual permite una reutilización limpia y controlada de recursos.

Recomendación:
Siempre que puedas, divide tu infraestructura en módulos separados por responsabilidad.
Eso mejora la trazabilidad y facilita el trabajo en equipo.

📄 main.tf (Configuración principal)

Ejemplo de cómo se instancian los módulos en el archivo raíz del proyecto:

terraform {
  required_version = ">= 1.0"
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "~> 2.0"
    }
    helm = {
      source  = "hashicorp/helm"
      version = "~> 2.0"
    }
  }

  backend "gcs" {
    bucket = "my-tfstate-bucket"
    prefix = "terraform/state"
  }
}

provider "google" {
  project = var.project_id
  region  = var.region
  zone    = var.zone
}

data "google_client_config" "default" {}
module "gke_cluster" {
  source       = "./modules/gke-cluster"
  cluster_name = "ftrick-cluster"
  machine_type = "e2-medium"
}

module "cloud_sql" {
  source        = "./modules/cloud-sql"
  instance_name = "ftrick-db"
  db_password   = var.db_password
}

🔐 Importante:
Recuerda definir tus variables sensibles como db_password en archivos .tfvars o con Terraform Cloud/Workspaces para evitar exponer credenciales.

🧙 Comandos mágicos

Una vez definido todo, se utilizan los siguientes comandos para desplegar tu infraestructura:

# Inicializa los proveedores, backends y módulos
terraform init

# Muestra un plan de ejecución (simulación de cambios)
terraform plan

# Aplica la infraestructura (¡despliegue real!)
terraform apply

🛠️ Consejo útil:
Usa terraform apply -auto-approvesolo en entornos de desarrollo o pruebas.
En producción, siempre revisa el plan antes de aplicar.

🔁 Reutilización en distintos entornos

Puedes reutilizar los mismos módulos para múltiples entornos (dev, staging, prod) simplemente usando diferentes archivos de variables.

terraform apply -var-file="envs/dev.tfvars"
terraform apply -var-file="envs/prod.tfvars"

🎯 Mejora continua:
Usa Workspaces de Terraform o CI/CD (como GitHub Actions + Argo CD) para automatizar y separar despliegues por entorno.

5. ☸️ Configuración Kubernetes: El corazón del despliegue

Una vez que la infraestructura está lista, toca orquestar los componentes de tu aplicación en Kubernetes. Esto se hace a través de manifiestos YAML que definen cómo se comportan tus contenedores, servicios, accesos y configuraciones.

📦 laravel-deployment.yaml

Este archivo define cómo se ejecuta tu contenedor Laravel dentro del clúster:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: laravel-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: laravel
  template:
    metadata:
      labels:
        app: laravel
    spec:
      containers:
        - name: laravel
          image: us-central1-docker.pkg.dev/ftrick-455901/ftrick-laravel:latest
          envFrom:
            - secretRef:
                name: laravel-env
          volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx/nginx.conf
              subPath: nginx.conf
      volumes:
        - name: nginx-config
          configMap:
            name: nginx-config

Consejo:
Usa envFrom.secretRef para cargar tus variables de entorno sensibles.
De esta manera no quedan expuestas en tus archivos YAML.

📡 Archivos clave adicionales

📁 laravel-service.yaml

Define un servicio interno que expone tu deployment en el clúster (ClusterIP):

apiVersion: v1
kind: Service
metadata:
  name: laravel-service
spec:
  selector:
    app: laravel
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: ClusterIP

🔁 Recomendación:
Usa este servicio como punto de entrada para tu Ingress o para otros servicios internos como base de datos o Redis.

🌐 laravel-ingress.yaml

Permite el acceso público a tu aplicación a través de HTTP(S):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: laravel-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
    - host: laravel.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: laravel-service
                port:
                  number: 80

🌍 Importante:
Configura correctamente tu dominio y certificados si vas a exponer la app a internet (puedes usar cert-manager para certificados SSL automáticos).

⚙️ nginx-config.yaml (ConfigMap)

Configura detalles específicos de Nginx en tu contenedor Laravel:

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  nginx.conf: |
    server {
      listen 80;
      index index.php index.html;
      root /var/www/public;

      location / {
        try_files $uri $uri/ /index.php?$query_string;
      }

      location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      }
    }

💡 Tip extra:
Usa ConfigMaps para separar tu lógica de configuración del código.
Esto mejora el mantenimiento y evita tener que reconstruir la imagen cada vez que cambie un ajuste de Nginx.

6. 🔄 GitOps con Argo CD: Sincronización automática

Con Argo CD, llevamos la filosofía GitOps a otro nivel: tu repositorio Git se convierte en la fuente de verdad, y Argo CD se encarga de sincronizar automáticamente los manifiestos con el estado real de tu clúster Kubernetes.

📦 Application Manifest: argocd-app.yaml

Define una aplicación gestionada por Argo CD, especificando el repositorio Git y la ruta donde están los manifiestos.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: laravel-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: 'https://github.com/tu-user/repo'
    targetRevision: HEAD
    path: kubernetes/manifests
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: default
  syncPolicy:
    automated:
      prune: true        # Elimina recursos que ya no están en el repo
      selfHeal: true     # Repara diferencias automáticamente

📁 Ruta path:

Asegúrate de que esta ruta dentro del repo contenga todos tus YAMLs (Deployment, Service, Ingress, etc.).

🌐 Exponer Argo CD con LoadBalancer

Para acceder a la interfaz web de Argo CD desde fuera del clúster, expón el servicio argocd-server con tipo LoadBalancer.

kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'

🔧 Importante:

Asegúrate de que los puertos 80 y 443 estén abiertos en el firewall de tu GCP para que el LoadBalancer pueda recibir tráfico HTTP/HTTPS.

Puedes verificar la IP externa asignada con:

kubectl get svc argocd-server -n argocd

🔐 Obtener contraseña de acceso inicial

Una vez desplegado Argo CD, el usuario por defecto es admin. Para obtener la contraseña inicial (auto-generada):

kubectl get secret argocd-initial-admin-secret -n argocd \
  -o jsonpath="{.data.password}" | base64 -d

✅ Acceder a Argo CD

Abre tu navegador y entra a:

https://

Usa el usuario admin y la contraseña obtenida.

Desde ahí podrás visualizar, sincronizar y administrar todas tus aplicaciones GitOps 🚀

7. ⚙️ CI/CD con GitHub Actions: Automatización total

Con GitHub Actions automatizamos todo el ciclo de integración y despliegue de nuestra aplicación Laravel. Cada vez que haces un push, se construye una nueva imagen Docker y se sincroniza automáticamente con Argo CD en Kubernetes.

📄 GitHub Actions Workflow: .github/workflows/deploy.yml

name: Build & Deploy

on:
  push:
    branches:
      - main

env:
  GCP_REGION: us-central1
  PROJECT_ID: project_id
  IMAGE_NAME: image_name
jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout repo
      uses: actions/checkout@v3

    - name: Set up Docker auth
      run: |
        echo ${{ secrets.GCP_CREDENTIALS }} > gcp-key.json
        gcloud auth activate-service-account --key-file=gcp-key.json
        gcloud auth configure-docker $GCP_REGION-docker.pkg.dev

    - name: Build & Push Docker image
      run: |
        docker build -t $GCP_REGION-docker.pkg.dev/$PROJECT_ID/$IMAGE_NAME:$GITHUB_SHA .
        docker push $GCP_REGION-docker.pkg.dev/$PROJECT_ID/$IMAGE_NAME:$GITHUB_SHA

  deploy:
    needs: build
    runs-on: ubuntu-latest

    steps:
    - name: Argo CD Sync
      run: |
        curl -k -X POST https://$ARGOCD_SERVER/api/v1/applications/ftrick-app/sync \
          -H "Authorization: Bearer $ARGOCD_TOKEN"

🔐 Secretos necesarios

Debes configurar estos secretos en tu repositorio (Settings > Secrets > Actions):

Nombre Descripción
GCP_CREDENTIALS Archivo JSON con credenciales del service account con permisos de push
ARGOCD_TOKEN Token de autenticación de Argo CD (admin o token con permisos de sincronizar)
ARGOCD_SERVER Dirección pública (o interna) del servidor de Argo CD, sin https://

🧠 Recomendaciones

  • Usa la rama main o crea un workflow diferente para dev si necesitas entornos separados.
  • Usa :latest como etiqueta solo en desarrollo. En producción, usar :$GITHUB_SHA permite versionar cada build.
  • Asegúrate de que el Service Account usado en GCP_CREDENTIALS tenga permisos roles/artifactregistry.writer.

🧪 Resultado

Este pipeline:

  • Construye y sube tu imagen Docker a Artifact Registry.
  • Notifica a Argo CD, quien se encarga de actualizar Kubernetes si detecta un cambio.

Pipeline de Argo CD: Diagrama que muestra el flujo de trabajo de despliegue continuo utilizando Argo CD, incluyendo la sincronización automática entre el repositorio Git y el clúster de Kubernetes para implementar aplicaciones siguiendo el enfoque GitOps.


🚀 ¡Haz el push final y mira cómo se despliega mágicamente!

git add .
git commit -m "Deploy automático con GitOps 🚀"
git push origin main

Nota: En 5 minutos tendrás tu aplicación accesible vía la IP del LoadBalancer. ¡Así de simple! 💥