Crossplane: gerenciando infraestrutura cloud como recursos Kubernetes

1. Introdução ao Crossplane e a Filosofia de Infraestrutura como Código no Kubernetes

Crossplane é um projeto open-source mantido pela Cloud Native Computing Foundation (CNCF) que transforma o Kubernetes em um plano de controle universal para gerenciar infraestrutura cloud. Diferente de ferramentas tradicionais como Terraform ou Pulumi, que operam fora do ecossistema Kubernetes, o Crossplane permite que equipes definam e provisionem recursos de cloud (bancos de dados, buckets, redes) usando a mesma API declarativa do Kubernetes.

A principal vantagem está na unificação: times de plataforma e aplicação compartilham o mesmo cluster Kubernetes como ponto central de gerenciamento. Isso elimina a necessidade de ferramentas externas, pipelines separados e contextos diferentes para infraestrutura e aplicações.

2. Arquitetura e Componentes Principais do Crossplane

Crossplane é construído sobre três conceitos fundamentais:

Providers: São extensões que conectam o Crossplane a provedores cloud (AWS, GCP, Azure, Oracle) ou serviços específicos (Datadog, GitHub). Cada provider expõe Managed Resources que representam recursos reais da nuvem.

Managed Resources: São objetos Kubernetes que mapeiam diretamente para recursos cloud. Por exemplo, um Bucket do provider AWS S3 ou um DBInstance do provider AWS RDS.

Composite Resources (XRs) e Claims: XRs permitem agrupar múltiplos Managed Resources em um único objeto personalizado. Claims são versões simplificadas que times de aplicação criam para consumir infraestrutura sem conhecer detalhes internos.

3. Instalação e Configuração Inicial do Crossplane no Cluster

A instalação pode ser feita via Helm:

helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update
helm install crossplane crossplane-stable/crossplane --namespace crossplane-system --create-namespace

Após instalar, configure um Provider. Exemplo com 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:v1.10.0
EOF

Para autenticação, crie um Secret com as credenciais AWS:

apiVersion: v1
kind: Secret
metadata:
  name: aws-creds
  namespace: crossplane-system
stringData:
  credentials: |
    [default]
    aws_access_key_id = AKIA...
    aws_secret_access_key = ...

E um ProviderConfig referenciando o Secret:

apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: aws-creds
      key: credentials

Agora, crie seu primeiro Managed Resource — um bucket S3:

apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
  name: meu-bucket-crossplane
spec:
  forProvider:
    region: us-east-1
  providerConfigRef:
    name: default

4. Criando Composite Resources (XRs) para Abstração de Infraestrutura

Composite Resources permitem criar templates reutilizáveis. Primeiro, defina um CompositeResourceDefinition (XRD) — o schema do recurso composto:

apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xpostgresqlinstances.database.example.org
spec:
  group: database.example.org
  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:
              storageGB:
                type: integer
                default: 20
              engineVersion:
                type: string
                default: "15"

Depois, crie uma Composition que mapeia esse XRD para recursos reais:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: xpostgresqlinstances.aws.database.example.org
spec:
  compositeTypeRef:
    apiVersion: database.example.org/v1alpha1
    kind: XPostgreSQLInstance
  resources:
    - name: subnetgroup
      base:
        apiVersion: rds.aws.upbound.io/v1beta1
        kind: SubnetGroup
        spec:
          forProvider:
            region: us-east-1
            subnetIds:
              - subnet-123
              - subnet-456
    - name: securitygroup
      base:
        apiVersion: ec2.aws.upbound.io/v1beta1
        kind: SecurityGroup
        spec:
          forProvider:
            region: us-east-1
            ingress:
              - fromPort: 5432
                toPort: 5432
                protocol: tcp
                cidrBlocks:
                  - "10.0.0.0/8"
    - name: database
      base:
        apiVersion: rds.aws.upbound.io/v1beta1
        kind: Instance
        spec:
          forProvider:
            region: us-east-1
            dbInstanceClass: db.t3.micro
            engine: postgres
            allocatedStorage: 20
            engineVersion: "15"
            masterUsername: admin
            masterPasswordSecretRef:
              name: db-password
              namespace: crossplane-system
      patches:
        - type: FromCompositeFieldPath
          fromFieldPath: spec.storageGB
          toFieldPath: spec.forProvider.allocatedStorage
        - type: FromCompositeFieldPath
          fromFieldPath: spec.engineVersion
          toFieldPath: spec.forProvider.engineVersion

5. Consumindo Infraestrutura com Claims

Times de aplicação criam apenas um Claim simples:

apiVersion: database.example.org/v1alpha1
kind: PostgreSQLInstance
metadata:
  name: meu-postgres-app
  namespace: aplicacao
spec:
  storageGB: 50
  engineVersion: "16"
  writeConnectionSecretToRef:
    name: db-connection

O Crossplane automaticamente provisiona o subnet group, security group e instância RDS, expondo os detalhes de conexão no Secret db-connection.

6. Gerenciamento de Ciclo de Vida

Crossplane gerencia dependências automaticamente. Se você definir um recurso que depende de outro (ex: RDS depende de subnet), ele aguarda a criação do recurso pai.

Atualizações em Compositions não afetam recursos já criados — apenas novos Claims usam a versão mais recente. Para migrar recursos existentes, é necessário atualizar manualmente os Managed Resources ou recriar os Claims.

O drift detection é nativo: Crossplane reconcilia continuamente o estado desejado com o real na cloud, corrigindo alterações manuais.

7. Integração com GitOps

Armazene XRD, Composition e Providers em um repositório Git:

infra/
├── crossplane/
│   ├── providers/
│   │   └── provider-aws.yaml
│   ├── xrds/
│   │   └── postgres-instance.yaml
│   └── compositions/
│       └── postgres-instance.yaml

Com ArgoCD ou Flux, sincronize esses arquivos com o cluster. Para controle de acesso, use RBAC do Kubernetes:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: aplicacao
  name: claim-creator
rules:
- apiGroups: ["database.example.org"]
  resources: ["postgresqlinstances"]
  verbs: ["create", "get", "list", "watch", "delete"]

8. Casos de Uso Avançados e Considerações de Segurança

Para múltiplas contas AWS, crie diferentes ProviderConfigs:

apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: conta-producao
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: aws-creds-producao

Para segurança, integre com HashiCorp Vault usando o provider vault e armazene senhas como secrets gerenciados. Aplique o princípio de menor privilégio: cada ProviderConfig deve ter permissões específicas apenas para os recursos que gerencia.

Monitore com métricas do Crossplane (disponíveis via Prometheus) e configure health checks para detectar recursos em estado degradado.

Referências