Crossplane: Transformando o Kubernetes em um Painel de Controle Multi‑Cloud

Arquitetura base do Crossplane

crossplane estende o plano de controle do kubernetes com CRDs que representam recursos de provedores de nuvem. Cada provedor (AWS, GCP, azure, etc.) expõe um ProviderConfig que encapsula credenciais e região. O controlador do provedor traduz as especificações declarativas em chamadas API nativas, mantendo o estado desejado como ManagedResources. Essa camada permite que o cluster funcione como um orquestrador de infraestrutura, eliminando a necessidade de ferramentas externas para provisionamento.

Instalação via Helm

O caminho mais rápido para colocar o Crossplane em produção é usar o chart oficial. O comando abaixo cria o namespace crossplane-system e instala a versão estável:

helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update
helm upgrade --install crossplane crossplane-stable/crossplane 
  --namespace crossplane-system 
  --create-namespace 
  --set args={--debug}

Após a instalação, verifique os pods:

kubectl get pods -n crossplane-system

Configuração de provedores de nuvem

Para cada nuvem, crie um ProviderConfig que aponta para um secret contendo as credenciais. Exemplo para AWS:

apiVersion: v1
kind: Secret
metadata:
  name: aws-creds
  namespace: crossplane-system
type: Opaque
stringData:
  key: "[YOUR_AWS_ACCESS_KEY_ID]"
  secret: "[YOUR_AWS_SECRET_ACCESS_KEY]"
---
apiVersion: aws.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
  name: aws-provider
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: aws-creds
      key: key
      secretKey: secret
  region: us-east-1

O mesmo padrão se aplica a GCP (gcp.crossplane.io/v1beta1) e Azure (azure.crossplane.io/v1beta1), mudando apenas o tipo de secret e os campos de configuração.

Validação automática

Crossplane executa um webhook de validação que impede a criação de recursos com credenciais ausentes ou regiões incompatíveis. O log do controlador exibe mensagens como ProviderConfig not found quando o secret está mal referenciado, facilitando o CI/CD.

Composições (Compositions) e Claims

O poder do Crossplane reside nas Compositions, que agrupam múltiplos ManagedResources em um único recurso de alto nível (Claim). Um exemplo clássico: provisionar um banco de dados PostgreSQL com VPC, sub‑rede e regras de firewall em apenas um XPostgreSQLInstance.

apiVersion: database.example.org/v1alpha1
kind: XPostgreSQLInstance
metadata:
  name: prod-db
spec:
  parameters:
    storageGB: 100
    engineVersion: "13"
  writeConnectionSecretToRef:
    name: prod-db-conn
    namespace: default

A composição que atende esse claim fica em um Composition separado:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: xpostgresqlinstance-aws
spec:
  compositeTypeRef:
    apiVersion: database.example.org/v1alpha1
    kind: XPostgreSQLInstance
  resources:
  - name: vpc
    base:
      apiVersion: ec2.aws.crossplane.io/v1beta1
      kind: VPC
      spec:
        forProvider:
          cidrBlock: 10.0.0.0/16
    patches:
    - fromFieldPath: "metadata.labels[environment]"
      toFieldPath: "spec.forProvider.tags[environment]"
  - name: subnet
    base:
      apiVersion: ec2.aws.crossplane.io/v1beta1
      kind: Subnet
      spec:
        forProvider:
          cidrBlock: 10.0.1.0/24
          vpcId: "${resource.vpc.id}"
    patches:
    - fromFieldPath: "metadata.labels[environment]"
      toFieldPath: "spec.forProvider.tags[environment]"
  - name: rds
    base:
      apiVersion: rds.aws.crossplane.io/v1beta1
      kind: DBInstance
      spec:
        forProvider:
          dbInstanceClass: db.t3.medium
          engine: postgres
          allocatedStorage: 100
          masterUsername: admin
          masterPasswordSecretRef:
            name: prod-db-conn
            namespace: default
            key: password
    patches:
    - fromFieldPath: "spec.parameters.storageGB"
      toFieldPath: "spec.forProvider.allocatedStorage"
    - fromFieldPath: "spec.parameters.engineVersion"
      toFieldPath: "spec.forProvider.engineVersion"

Observe o uso de ${resource.vpc.id} para referenciar dinamicamente o ID da VPC criada anteriormente. Essa interpolação elimina scripts ad‑hoc e garante consistência transacional.

Automação via GitOps

Armazene o manifesto da Claim e da Composition em um repositório Git. O ArgoCD ou Flux monitora o diretório infrastructure/ e aplica as mudanças automaticamente. Exemplo de kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- provider-aws.yaml
- composition-postgres.yaml
- claim-prod-db.yaml

Com o webhook do repositório configurado, qualquer PR que altere storageGB dispara um rollout que atualiza o recurso RDS sem intervenção manual.

Gerenciamento de segredos e rotatividade

Crossplane cria um Secret contendo as credenciais de conexão (host, porta, usuário, senha). Para rotatividade automática, combine o controlador crossplane/provider-aws com o external-secrets da comunidade. O fluxo:

  1. O provedor de segredos (AWS Secrets Manager) armazena a senha.
  2. O ExternalSecret sincroniza o valor para o secret prod-db-conn.
  3. Crossplane detecta a mudança e reinicia o DBInstance com a nova senha.

Manifesto de ExternalSecret:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: prod-db-conn
spec:
  secretStoreRef:
    name: aws-secret-store
    kind: SecretStore
  target:
    name: prod-db-conn
    creationPolicy: Owner
  dataFrom:
  - extract:
      key: /prod/postgres/password

Políticas de controle de acesso (RBAC) avançado

Crossplane respeita o RBAC nativo do Kubernetes. Defina ClusterRole e ClusterRoleBinding que concedem permissão apenas para recursos específicos, por exemplo, permitir que a equipe de dados crie apenas XPostgreSQLInstance e nada mais.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: data-team-crossplane
rules:
- apiGroups: ["database.example.org"]
  resources: ["xpostgresqlinstances"]
  verbs: ["create","get","list","watch","delete"]
- apiGroups: ["apiextensions.crossplane.io"]
  resources: ["compositions"]
  verbs: ["get","list","watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: data-team-binding
subjects:
- kind: Group
  name: data-team
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: data-team-crossplane
  apiGroup: rbac.authorization.k8s.io

Essa segmentação impede que usuários criem recursos fora do escopo, reduzindo a superfície de ataque.

Observabilidade e métricas

Crossplane expõe métricas via Prometheus no endpoint /metrics. Ative o ServiceMonitor se usar o Prometheus Operator:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: crossplane-monitor
  labels:
    release: prometheus
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: crossplane
  endpoints:
  - port: metrics
    interval: 30s

As métricas crossplane_resource_claims_total e crossplane_managed_resource_status permitem criar alertas que detectam falhas de provisionamento antes que impactem a aplicação.

Integração com OpenTelemetry

Instale o collector sidecar no namespace crossplane-system e configure o exportador para o backend de sua escolha (Jaeger, Tempo). O collector captura spans gerados pelos controladores ao chamar APIs de nuvem, oferecendo rastreamento de latência de provisionamento.

apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: crossplane-collector
spec:
  mode: deployment
  config: |
    receivers:
      otlp:
        protocols:
          grpc:
    exporters:
      otlp:
        endpoint: tempo:4317
    service:
      pipelines:
        traces:
          receivers: [otlp]
          exporters: [otlp]

Escalabilidade e alta disponibilidade

Crossplane pode ser executado em modo HA usando múltiplas réplicas do controlador. Defina o Deployment com replicas: 3 e habilite o leader election via flag --leader-election. O Helm chart já inclui essa configuração, basta sobrescrever:

helm upgrade --install crossplane crossplane-stable/crossplane 
  --namespace crossplane-system 
  --set replicaCount=3 
  --set args={--leader-election}

O etcd do cluster Kubernetes garante consistência de estado; o controlador persiste o status em status.conditions de cada recurso, permitindo que réplicas concorrentes reconciliem sem conflitos.

Destruição controlada e políticas de retenção

Ao deletar um Claim, o Crossplane executa a política DeletionPolicy definida na Composition. Opções:

  • Delete: remove o recurso gerenciado imediatamente.
  • Orphan: deixa o recurso na nuvem, útil para migrações.
  • Retain: mantém o recurso, mas remove a referência do Kubernetes.

Exemplo de Claim com política de retenção:

apiVersion: database.example.org/v1alpha1
kind: XPostgreSQLInstance
metadata:
  name: staging-db
spec:
  deletionPolicy: Orphan
  parameters:
    storageGB: 50
    engineVersion: "13"

Essa granularidade evita perdas acidentais em ambientes de teste, enquanto ainda permite a limpeza automática em produção.

Pipeline CI/CD completa

Um pipeline típico usa GitHub Actions para validar o YAML, aplicar via kubectl e monitorar o status com kubectl wait. Exemplo de workflow:

name: Deploy Infra
on:
  push:
    paths:
      - 'infrastructure/**'
jobs:
  apply:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Set up kubectl
      uses: azure/setup-kubectl@v3
      with:
        version: 'v1.28.0'
    - name: Apply manifests
      run: |
        kubectl apply -k infrastructure/
    - name: Wait for DB
      run: |
        kubectl wait --for=condition=Ready XPostgreSQLInstance/prod-db --timeout=10m

O comando kubectl wait bloqueia o job até que o recurso atinja o estado Ready, garantindo que a aplicação downstream só seja implantada após a infraestrutura estar pronta.