🚀 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.
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.
🔐 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 comodb_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:
Usaterraform apply -auto-approve
solo 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:
UsaenvFrom.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:
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 paradev
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 permisosroles/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.
🚀 ¡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! 💥