Crossplane: Gestión de Infraestructura en Kubernetes

Crossplane convierte Kubernetes en un plano de control universal para gestionar infraestructura en la nube utilizando los mismos mecanismos declarativos que usas para tus aplicaciones. Esta guía explica cómo instalar Crossplane, configurar proveedores, crear recursos gestionados, composiciones y habilitar flujos GitOps para el aprovisionamiento de infraestructura.

Requisitos Previos

  • Clúster Kubernetes 1.26+ (kind, k3s, EKS, GKE, AKS o similar)
  • kubectl configurado apuntando al clúster
  • Helm 3.x instalado
  • Credenciales de proveedor cloud (AWS, GCP, Azure, etc.)
# Verificar la versión de Kubernetes
kubectl version --short

# Verificar Helm
helm version --short

Instalación de Crossplane

# Añadir el repositorio Helm de Crossplane
helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update

# Instalar Crossplane en el namespace dedicado
helm install crossplane \
  crossplane-stable/crossplane \
  --namespace crossplane-system \
  --create-namespace \
  --version 1.14.0

# Verificar que los pods están corriendo
kubectl get pods -n crossplane-system

Salida esperada:

NAME                                       READY   STATUS    RESTARTS   AGE
crossplane-7d9f4c6b9-xk8mz                1/1     Running   0          2m
crossplane-rbac-manager-5b8f9c7d6-vt2pq   1/1     Running   0          2m
# Instalar la CLI de Crossplane (kubectl crossplane)
curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh | sh
mv crossplane /usr/local/bin/

# Verificar la instalación
kubectl crossplane --version

Instalación de Proveedores

Los proveedores son el puente entre Crossplane y los servicios cloud:

# Instalar el proveedor de AWS
cat << 'EOF' | kubectl apply -f -
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-aws-s3
spec:
  package: xpkg.upbound.io/upbound/provider-aws-s3:v0.47.0
EOF

# Verificar que el proveedor se instaló correctamente
kubectl get providers
kubectl describe provider provider-aws-s3

Configurar las credenciales del proveedor:

# Crear el Secret con las credenciales de AWS
cat << 'EOF' > /tmp/aws-credentials.txt
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
EOF

kubectl create secret generic aws-secret \
  -n crossplane-system \
  --from-file=creds=/tmp/aws-credentials.txt

# Crear el ProviderConfig para AWS
cat << 'EOF' | kubectl apply -f -
apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: aws-secret
      key: creds
EOF

# Limpiar el archivo temporal con credenciales
rm /tmp/aws-credentials.txt

Recursos Gestionados

Un Managed Resource (MR) es la representación de un recurso cloud en Kubernetes:

# Crear un bucket S3 con Crossplane
cat << 'EOF' | kubectl apply -f -
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
  name: mi-bucket-crossplane
  annotations:
    crossplane.io/external-name: mi-bucket-produccion-2024
spec:
  forProvider:
    region: eu-west-1
    tags:
      Entorno: produccion
      Gestionado: crossplane
  providerConfigRef:
    name: default
EOF

# Monitorear el estado del recurso
kubectl get bucket mi-bucket-crossplane
kubectl describe bucket mi-bucket-crossplane
# Crear una base de datos RDS
cat << 'EOF' | kubectl apply -f -
apiVersion: rds.aws.upbound.io/v1beta1
kind: Instance
metadata:
  name: mi-base-datos
spec:
  forProvider:
    region: eu-west-1
    instanceClass: db.t3.micro
    engine: postgres
    engineVersion: "15.4"
    dbName: miapp
    username: admin
    passwordSecretRef:
      namespace: default
      name: rds-password
      key: password
    allocatedStorage: 20
    skipFinalSnapshot: true
  providerConfigRef:
    name: default
EOF

Composiciones y XRDs

Las Composiciones permiten abstraer recursos complejos en APIs personalizadas:

# Definir un XRD (Composite Resource Definition)
cat << 'EOF' | kubectl apply -f -
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xpostgresqlinstances.plataforma.empresa.com
spec:
  group: plataforma.empresa.com
  names:
    kind: XPostgreSQLInstance
    plural: xpostgresqlinstances
  claimNames:
    kind: PostgreSQLInstance
    plural: postgresqlinstances
  versions:
    - name: v1alpha1
      served: true
      referenceable: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                parameters:
                  type: object
                  properties:
                    storageGB:
                      type: integer
                      description: "Tamaño del almacenamiento en GB"
                    version:
                      type: string
                      description: "Versión de PostgreSQL"
                  required:
                    - storageGB
EOF

# Crear la Composition que implementa el XRD
cat << 'EOF' | kubectl apply -f -
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: postgresql-aws
  labels:
    provider: aws
    servicio: postgresql
spec:
  compositeTypeRef:
    apiVersion: plataforma.empresa.com/v1alpha1
    kind: XPostgreSQLInstance
  resources:
    - name: rds-instance
      base:
        apiVersion: rds.aws.upbound.io/v1beta1
        kind: Instance
        spec:
          forProvider:
            region: eu-west-1
            engine: postgres
            instanceClass: db.t3.micro
            username: admin
            skipFinalSnapshot: true
      patches:
        # Parchar el tamaño del disco desde los parámetros del claim
        - type: FromCompositeFieldPath
          fromFieldPath: spec.parameters.storageGB
          toFieldPath: spec.forProvider.allocatedStorage
        # Parchar la versión del motor
        - type: FromCompositeFieldPath
          fromFieldPath: spec.parameters.version
          toFieldPath: spec.forProvider.engineVersion
EOF

Claims de Infraestructura

Los Claims son la interfaz que usan los equipos de desarrollo para solicitar infraestructura:

# Los desarrolladores crean Claims en su propio namespace
cat << 'EOF' | kubectl apply -f -
apiVersion: plataforma.empresa.com/v1alpha1
kind: PostgreSQLInstance
metadata:
  name: bd-mi-aplicacion
  namespace: equipo-backend
spec:
  parameters:
    storageGB: 20
    version: "15.4"
  compositionSelector:
    matchLabels:
      provider: aws
  writeConnectionSecretToRef:
    name: credenciales-postgres
EOF

# Verificar el estado del claim
kubectl get postgresqlinstance -n equipo-backend

# Ver los recursos compuestos creados
kubectl get composite

# Ver las credenciales de conexión generadas
kubectl get secret credenciales-postgres -n equipo-backend -o yaml

Flujos GitOps con Crossplane

Integrar Crossplane con Argo CD o Flux para GitOps:

# Estructura de repositorio GitOps recomendada
mkdir -p infra/{compositions,claims,providerconfigs}

# El repositorio se organiza así:
# infra/
# ├── compositions/     # Definiciones de XRDs y Compositions
# ├── claims/           # Claims por entorno y equipo
# └── providerconfigs/  # Configuraciones de proveedores (sin credenciales)

# Ejemplo de Application de Argo CD para Crossplane
cat << 'EOF' | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: infraestructura-crossplane
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/mi-org/infra-gitops
    targetRevision: main
    path: infra/compositions
  destination:
    server: https://kubernetes.default.svc
    namespace: crossplane-system
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
EOF

Solución de Problemas

# Verificar el estado de los proveedores
kubectl get providers
kubectl describe provider provider-aws-s3

# Ver los eventos de un recurso gestionado
kubectl describe bucket mi-bucket-crossplane | grep -A 20 Events

# Ver los logs del pod del proveedor
kubectl logs -n crossplane-system \
  $(kubectl get pods -n crossplane-system -l pkg.crossplane.io/revision -o name | head -1)

# Verificar recursos compuestos y su estado
kubectl get composite
kubectl get managed

# Depurar un claim que no se resuelve
kubectl describe postgresqlinstance bd-mi-aplicacion -n equipo-backend

# Forzar la sincronización de un recurso
kubectl annotate bucket mi-bucket-crossplane \
  crossplane.io/paused="true" --overwrite
kubectl annotate bucket mi-bucket-crossplane \
  crossplane.io/paused="false" --overwrite

El provider no se conecta:

# Verificar que el Secret de credenciales existe
kubectl get secret aws-secret -n crossplane-system

# Verificar la configuración del ProviderConfig
kubectl describe providerconfig default

Conclusión

Crossplane representa una evolución natural del Infrastructure as Code, permitiendo gestionar recursos cloud usando la misma API declarativa de Kubernetes que ya conoces. Las Composiciones son el elemento más poderoso ya que permiten crear abstracciones que ocultan la complejidad a los equipos de desarrollo, mientras que GitOps garantiza que el estado deseado siempre se aplica de forma auditable. Con Crossplane, la infraestructura se convierte en un ciudadano de primera clase en tu ecosistema Kubernetes.