Pular para o conteúdo
Platform Engineering 13 min read

Infrastructure as Code: Terraform vs Crossplane para Plataformas

Compare Terraform e Crossplane para IaC, entenda quando usar cada um, e aprenda a construir abstrações de infraestrutura para sua plataforma.

Por Equipe Integr8 28/12/2024

O Dilema do IaC em Plataformas

Ao construir uma Internal Developer Platform, surge a pergunta: como desenvolvedores devem provisionar infraestrutura? Terraform é o padrão de mercado, mas Crossplane oferece uma abordagem Kubernetes-native que pode ser mais adequada para plataformas.

🔧

Terraform

Ferramenta declarativa com estado centralizado e vasto ecossistema de providers

☸️

Crossplane

Control plane Kubernetes que trata infraestrutura como CRDs nativos

Terraform: O Padrão da Indústria

Arquitetura do Terraform

Fluxo Terraform: arquivos .tf são aplicados e estado é armazenado remotamente
100%
Arquitetura do Terraform

Fluxo Terraform: arquivos .tf são aplicados e estado é armazenado remotamente

Módulos para Abstração

# modules/platform-database/main.tf
variable "name" {
  description = "Database name"
  type        = string
}

variable "size" {
  description = "Database size (small, medium, large)"
  type        = string
  default     = "small"
}

variable "engine" {
  description = "Database engine"
  type        = string
  default     = "postgresql"
}

locals {
  sizes = {
    small  = { instance_class = "db.t3.small", storage = 20 }
    medium = { instance_class = "db.t3.medium", storage = 100 }
    large  = { instance_class = "db.r5.large", storage = 500 }
  }
}

resource "aws_db_instance" "this" {
  identifier        = var.name
  engine            = var.engine
  engine_version    = "15.4"
  instance_class    = local.sizes[var.size].instance_class
  allocated_storage = local.sizes[var.size].storage

  db_name  = replace(var.name, "-", "_")
  username = "admin"
  password = random_password.db.result

  vpc_security_group_ids = [aws_security_group.db.id]
  db_subnet_group_name   = aws_db_subnet_group.this.name

  backup_retention_period = 7
  skip_final_snapshot     = false
  deletion_protection     = true

  tags = {
    Name        = var.name
    ManagedBy   = "terraform"
    Platform    = "true"
  }
}

# Output para uso pela aplicação
output "connection_string" {
  value     = "postgresql://${aws_db_instance.this.username}:${random_password.db.result}@${aws_db_instance.this.endpoint}/${aws_db_instance.this.db_name}"
  sensitive = true
}

Terraform com Atlantis para GitOps

# atlantis.yaml
version: 3
projects:
  - name: platform-infra
    dir: infrastructure/platform
    workspace: production
    autoplan:
      when_modified: ["*.tf", "modules/**/*.tf"]
      enabled: true
    apply_requirements: [approved, mergeable]

  - name: team-a-database
    dir: teams/team-a/database
    workspace: production
    autoplan:
      enabled: true
    apply_requirements: [approved]

workflows:
  default:
    plan:
      steps:
        - init
        - plan:
            extra_args: ["-lock=false"]
    apply:
      steps:
        - apply

Crossplane: IaC Kubernetes-Native

Arquitetura do Crossplane

Crossplane usa Kubernetes como control plane para gerenciar recursos cloud
100%
Arquitetura do Crossplane

Crossplane usa Kubernetes como control plane para gerenciar recursos cloud

Managed Resources

# RDS Instance como CRD
apiVersion: database.aws.crossplane.io/v1beta1
kind: RDSInstance
metadata:
  name: my-database
spec:
  forProvider:
    region: us-east-1
    dbInstanceClass: db.t3.small
    engine: postgres
    engineVersion: "15.4"
    masterUsername: admin
    allocatedStorage: 20
    skipFinalSnapshot: false
  writeConnectionSecretToRef:
    name: my-database-creds
    namespace: team-a
  providerConfigRef:
    name: aws-provider

Compositions para Abstração

O poder do Crossplane está nas Compositions - abstrações que expõem interfaces simplificadas:

# CompositeResourceDefinition (XRD) - Define a interface
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xdatabases.platform.myorg.io
spec:
  group: platform.myorg.io
  names:
    kind: XDatabase
    plural: xdatabases
  versions:
    - name: v1
      served: true
      referenceable: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              required:
                - size
              properties:
                size:
                  type: string
                  enum: [small, medium, large]
                  description: "Database size"
                engine:
                  type: string
                  enum: [postgresql, mysql]
                  default: postgresql

---
# Composition - Implementa a interface
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: database-aws
  labels:
    provider: aws
spec:
  compositeTypeRef:
    apiVersion: platform.myorg.io/v1
    kind: XDatabase

  resources:
    - name: rds-instance
      base:
        apiVersion: database.aws.crossplane.io/v1beta1
        kind: RDSInstance
        spec:
          forProvider:
            region: us-east-1
            engine: postgres
            engineVersion: "15.4"
            publiclyAccessible: false
            skipFinalSnapshot: false
      patches:
        # Map size para instance class
        - type: FromCompositeFieldPath
          fromFieldPath: spec.size
          toFieldPath: spec.forProvider.dbInstanceClass
          transforms:
            - type: map
              map:
                small: db.t3.small
                medium: db.t3.medium
                large: db.r5.large

        - type: FromCompositeFieldPath
          fromFieldPath: spec.size
          toFieldPath: spec.forProvider.allocatedStorage
          transforms:
            - type: map
              map:
                small: 20
                medium: 100
                large: 500

    - name: security-group
      base:
        apiVersion: ec2.aws.crossplane.io/v1beta1
        kind: SecurityGroup
        spec:
          forProvider:
            region: us-east-1
            description: "Database security group"
            ingress:
              - fromPort: 5432
                toPort: 5432
                protocol: tcp
                cidrBlocks:
                  - 10.0.0.0/8

Uso pelo Desenvolvedor

# Isso é tudo que o dev precisa criar!
apiVersion: platform.myorg.io/v1
kind: XDatabase
metadata:
  name: orders-db
  namespace: team-orders
spec:
  size: medium
  engine: postgresql

Comparativo

AspectoTerraformCrossplane
ModeloPull (CLI)Push (Controller)
EstadoRemote backendKubernetes etcd
LinguagemHCLYAML (CRDs)
Drift DetectionManual (plan)Contínuo (reconciliation)
Self-HealingNãoSim
Multi-CloudSimSim
GitOps NativeVia AtlantisNativo
Curva AprendizadoMenorRequer K8s knowledge
EcossistemaMuito maduroCrescendo
💡Nossa Recomendação

Terraform para times que não usam Kubernetes como plataforma central ou precisam de features avançadas. Crossplane para plataformas Kubernetes-first que querem self-service verdadeiro via kubectl/GitOps.

Hybrid Approach

Muitas organizações usam ambos:

Platform team usa Terraform para fundação, App teams usam Crossplane para self-service
100%
Abordagem Híbrida de IaC

Platform team usa Terraform para fundação, App teams usam Crossplane para self-service


    Quer construir a estratégia de IaC para sua plataforma? Fale com nossos especialistas em Platform Engineering.