Problema
Muchas organizaciones están dejando de depender de un único proveedor y adoptan estrategias multi‑cloud o cambian de proveedor al iniciar un nuevo proyecto. El desafío no es solo aprender la consola de otro vendor; es mantener la coherencia operativa cuando los equipos gestionan clústers de Kubernetes, pipelines de CI/CD y recursos de infraestructura en distintas nubes.
En la práctica, los ingenieros se enfrentan a:
- Re‑escritura de scripts de provisión que estaban atados a recursos específicos de AWS, GCP o Azure.
- Inconsistencias en la configuración de Helm charts entre clústers que, aunque idénticos a nivel de Kubernetes, usan diferentes tipos de discos, balanceadores o IAM.
- Dificultad para aplicar políticas de seguridad y observabilidad de forma homogénea.
El problema se vuelve crítico cuando el tiempo de entrega se reduce y la presión por evitar interrupciones aumenta. La solución necesita ser reutilizable, no un parche puntual para un solo proveedor.
Causa
- Acoplamiento a APIs propietarias – Cuando el código de Terraform, scripts de Bash o pipelines de Jenkins hacen referencia directa a recursos como
aws_eks_clusterogoogle_container_cluster, la portabilidad se rompe. - Falta de abstracción en los módulos de IaC – Módulos monolíticos que incluyen tanto la lógica de red como la de compute mezclan conceptos que varían entre nubes.
- Gestión de estados aislados – Cada proveedor mantiene su propio backend de estado (S3, GCS, Azure Blob). Sin una estrategia centralizada, la sincronización entre entornos se vuelve manual y propensa a errores.
- Políticas de seguridad divergentes – IAM en AWS, IAM en GCP y RBAC en Azure tienen sintaxis distintas; replicar las mismas reglas a mano genera desalineación.
- Pipeline de CI/CD rígido – Pipelines que asumen una única variable de entorno para el provider terminan fallando al cambiar de contexto.
Solución
Adoptar una arquitectura provider‑agnostic basada en tres capas:
-
Módulos Terraform genéricos
- Separar la lógica de infraestructura común (VPC, subnets, IAM roles) de la lógica específica del provider.
- Cada módulo expone variables como
cidr_block,instance_typey un bloqueprovider_configque se llena con los valores del vendor. - Utilizar el patrón “wrapper module” que delega al módulo nativo (
aws_vpc,google_compute_network, etc.) según el valor devar.cloud_provider.
-
Kubernetes como capa de convergencia
- Mantener los Helm charts idénticos en todos los clústers.
- Parametrizar los valores que cambian entre nubes (por ejemplo,
service.type,storageClass) mediantevalues.yamlgenerados por Terraform (helm_releaseconsetdinámico). - Usar Cluster Autoscaler y External DNS con proveedores específicos, pero encapsular su instalación en scripts que reciben el nombre del provider.
-
CI/CD y gestión de estado centralizada
- Configurar un backend de Terraform remoto común, como Terraform Cloud o un bucket S3 replicado, y usar workspaces para separar
dev,stagingyprod. - En los pipelines, definir una variable
CLOUD_PROVIDERy cargar el provider config correspondiente antes de ejecutarterraform init. - Añadir pruebas de plan automáticas que comparen el diff entre proveedores; cualquier recurso inesperado detiene el despliegue.
- Configurar un backend de Terraform remoto común, como Terraform Cloud o un bucket S3 replicado, y usar workspaces para separar
Ejemplo de wrapper module
module "network" {
source = "./modules/network"
cloud_provider = var.cloud_provider # "aws", "gcp" o "azure"
cidr_block = var.cidr_block
region = var.region
}
Dentro de ./modules/network/main.tf:
variable "cloud_provider" {}
variable "cidr_block" {}
variable "region" {}
# Provider‑specific resources
resource "aws_vpc" "this" {
count = var.cloud_provider == "aws" ? 1 : 0
cidr_block = var.cidr_block
tags = { Name = "vpc-aws-${var.region}" }
}
resource "google_compute_network" "this" {
count = var.cloud_provider == "gcp" ? 1 : 0
name = "vpc-gcp-${var.region}"
auto_create_subnetworks = false
}
Este patrón permite añadir nuevos proveedores sin tocar la lógica de negocio.
Cuándo aplicar esta solución
- Migraciones planificadas entre AWS, GCP o Azure, donde el tiempo de inactividad debe ser mínimo.
- Entornos multi‑cloud que requieren despliegues simultáneos en al menos dos proveedores.
- Equipos con experiencia en Kubernetes que buscan reutilizar Helm charts y pipelines.
No es la mejor opción cuando:
- La arquitectura depende de servicios exclusivos de un vendor (por ejemplo, AWS Lambda con triggers específicos).
- El proyecto es un prototipo de corta duración y la sobrecarga de abstraer el provider no se justifica.
Código
# Inicializar Terraform con backend remoto y seleccionar workspace
terraform init -backend-config="bucket=my-tf-state" -backend-config="key=${var.cloud_provider}/state.tfstate"
terraform workspace select ${ENVIRONMENT} || terraform workspace new ${ENVIRONMENT}
# Generar values.yaml para Helm según provider
cat <<EOF > helm-values-${var.cloud_provider}.yaml
service:
type: ${var.cloud_provider == "aws" ? "LoadBalancer" : "NodePort"}
storageClass: ${var.cloud_provider == "gcp" ? "standard" : "gp2"}
EOF
# Deploy Helm chart con valores dinámicos
helm upgrade --install my-app ./charts/my-app \
-f helm-values-${var.cloud_provider}.yaml \
--set image.tag=${CI_COMMIT_SHA}
Verificación
- Ejecutar
terraform planen cada workspace y confirmar que el número de recursos creados coincide con el esperado para el provider. - Verificar que el clúster Kubernetes se levante sin errores de
helm install. - Comprobar que los endpoints de la aplicación responden tanto en AWS como en GCP mediante
curl -I https://my-app.${var.cloud_provider}.example.com. - Revisar los logs de CI/CD; cualquier desviación en el plan debe generar una alerta.
Notas adicionales
- Naming convention: incluye siempre el provider en los nombres de recursos (
vpc-aws-us-east-1) para evitar colisiones al replicar el estado. - Políticas de IAM: crea plantillas JSON genéricas y usa
terraformpara rellenar los ARN específicos del provider. - Testing: emplea
kitchen-terraformoTerratestpara validar módulos en cada nube antes de mergear a producción. - Costos ocultos: al replicar infraestructura, revisa los precios de egress y almacenamiento; lo que parece idéntico puede tener facturación distinta.
- Documentación interna: mantén un README por módulo que indique qué variables son provider‑specific y cuáles son common.
Adoptar esta estrategia reduce la fricción al pasar de un cloud a otro, permite que los equipos reutilicen sus conocimientos de Kubernetes y Helm, y mantiene la infraestructura bajo control mediante Terraform. Con una capa de abstracción bien definida, el salto entre proveedores deja de ser una tarea de último minuto y se convierte en un proceso repetible y fiable.