Qué Es Realmente Crossplane
Crossplane es un operador de Kubernetes que te permite manejar infraestructura cloud usando custom resources de Kubernetes. En vez de escribir archivos HCL y correr terraform apply, escribirs manifests YAML y hacés kubectl apply. Los controllers de Crossplane reconcilian el estado deseado contra el estado real del cloud provider, continuamente.
Las abstracciones clave:
- Managed Resources (MRs), Mapeos directos 1:1 a recurse es cloud. Un MR
RDSInstanceprovisiona una instancia AWS RDS. - Composite Resources (XRs), Tu API de plataforma custom. Definís el schema (via XRDs) y la implementación (via Compositions).
- Claims, Requests con scope a namespace para composite resources. Los equipos de aplicación crean claims; los equipos de plataforma definen qué provisionan los claims.
Armando una API de Plataforma con XRDs y Compositions
Los equipos de aplicación necesitan bases de datos. No deberían necesitar saber sobre VPC subnets, security groups, parameter groups, ni IAM roles. Necesitan: un nombre de base de datos, un engine, un tamaño, y una región.
El XRD (Definición de API)
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: xdatabases.platform.example.com
spec:
group: platform.example.com
names:
kind: XDatabase
plural: xdatabases
claimNames:
kind: Database
plural: databases
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
engine:
type: string
enum: ["postgres", "mysql"]
engineVersion:
type: string
size:
type: string
enum: ["small", "medium", "large"]
region:
type: string
default: "us-east-1"
backupRetentionDays:
type: integer
default: 7
minimum: 1
maximum: 35
required: ["engine", "engineVersion", "size"]
Esa es nuestra API de plataforma. Cinco campos para el equipo de app. Sin VPC IDs, sin reglas de security groups, sin políticas IAM.
El Claim (Lo Que Escriben los Equipos de App)
apiVersion: platform.example.com/v1alpha1
kind: Database
metadata:
name: orders-db
namespace: orders-service
spec:
engine: postgres
engineVersion: "15.4"
size: medium
backupRetentionDays: 14
Eso es todo. Cinco líneas de configuración significativa. El equipo de plataforma controla todo lo demás.
La Composition (Implementación)
La Composition define qué recurse es cloud se crean: una instancia RDS con nuestra configuración de seguridad estándar, un parameter group con nuestros settings, y un Kubernetes secret con los detalles de conexión. Incluye mapeos de t-shirt sizes a instance classes reales (small → db.t4g.micro, medium → db.t4g.medium, large → db.r6g.large) y patches para propagar campos del XR a los recurse es hijos.
La Migración desde Terraform
No arrancamos Terraform de un tirón. La migración llevó cuatro meses:
Mes 1: Importar recurse es existentes. Para cada recurso manejado por Terraform, creamos un MR de Crossplane correspondiente con el ID del recurso cloud y deletionPolicy: Orphan. Crossplane observaba pero no modificaba ni borraba los recursos.
Mes 2: Buildear XRDs y Compositions. Diseñamos la API de plataforma con nuestros equipos de app, enfocándonos en lo que realmente necesitaban configurar.
Mes 3: Migrar app por app. Cada equipo reemplazó sus referencias de Terraform con claims de Crossplane. Porque ambos apuntaban a los mismos recursos, era seguro.
Mes 4: Decomisionar Terraform. Cambiamos deletionPolicy a Delete, borramos el state de Terraform, y archivamos los repos.
Qué Salió Mal
1. Madurez del provider. El provider AWS cubre ~85% de los servicios. Pegamos contra gaps en CloudFront y Cognito. Para esos, usamos el Terraform provider de Crossplane (provider-terraform) como puente, corre Terraform real dentro de un controller de Crossplane. Feo pero funcional.
2. Tormentas de reconciliación. Crossplane reconcilia cada recurso cada 60 segundos por default. Con 2,400 managed resources, son 2,400 llamadas API a AWS por minuto. Pegamos contra rate limits de AWS en la primera semana. El fix: incrementar --poll-interval a 5 minutos para recurse es estables.
3. Secret management. Crossplane escribe connection secrets como Kubernetes Secrets en base64, no encriptados. Tuvimos que agregar External Secrets Operator para sincronizar secrets generados por Crossplane a AWS Secrets Manager.
4. Debugging es más difícil. Cuando un apply de Terraform falla, teners un mensaje de error y un plan diff. Cuando una reconciliación de Crossplane falla, el error está en un evento de Kubernetes en un managed resource anidado dentro de un composite resource que fue creado por un claim. Necesitás crossplane beta trace y kubectl describe a través de tres capas de recurse es para encontrar el error real.
Comparación de Performance
| Métrica | Terraform | Crossplane |
|---|---|---|
| Tiempo para provisionar RDS | 12-15 min | 12-15 min (igual, es AWS) |
| Tiempo para que el dev solicite | 45 min (PR + plan + apply) | 3 min (PR + Argo CD sync) |
| Detección de drift | Manual (terraform plan) | Continua (cada poll interval) |
| Storage de estado | S3 + DynamoDB lock | etcd (Kubernetes) |
| Rollback | terraform apply con state viejo | git revert + Argo CD sync |
¿Lo Haría de Nuevo?
Sí, pero con advertencias:
- Solo si ya se está en Kubernetes. Crossplane sin Kubernetes no tiene sentido.
- Solo si se desea una API de plataforma. Si se es un equipo chico donde todos manejan su propia infra, las capas de abstracción de Crossplane son overhead.
- Presupuestar la curva de aprendizaje. Los conceptos de Crossplane tardan unas dos semanas para un ingeniero de Kubernetes experimentado.
- Mantener Terraform como fallback. Utilizar
provider-terraformpara cualquier recurso que el provider nativo de Crossplane no soporte bien. El purismo no vale outages de producción.
El ecosistema Kubernetes se está moviendo hacia tratar la infraestructura igual que las aplicaciones, manifests declarativos, reconciliación GitOps, y provisioning API-driven. Crossplane es la herramienta más madura en este espacio, y para organizaciones ya invertidas en Kubernetes, es el siguiente paso natural. Entrá nomás con plena conciencia de la complejidad operacional que se está asumiendo.