Scale Terraform #

Konfigurasi Terraform yang dimulai kecil — satu main.tf, satu terraform.tfstate — bisa tumbuh hingga mengelola ratusan resource dalam satu konfigurasi. Di titik ini, masalah mulai muncul: terraform plan butuh waktu lama karena harus refresh semua resource, satu kesalahan bisa mempengaruhi infrastruktur yang tidak berhubungan, dan blast radius dari setiap apply menjadi terlalu besar. Scaling Terraform bukan soal menambah spesifikasi mesin yang menjalankan pipeline, tapi soal memecah konfigurasi berdasarkan prinsip yang tepat.

flowchart TD
    A["Small\n1 state file"] --> B["Medium\nfew states"]
    B --> C["Large\nmany states"]
    C --> D["Problems:\nSlow plan\nBig blast radius"]
    D --> E["Split by\nenvironment"]
    D --> F["Split by\ncomponent"]
    D --> G["Split by\nteam"]

    style A fill:#10b981,stroke:#059669,color:#fff
    style B fill:#3b82f6,stroke:#1e40af,color:#fff
    style C fill:#f59e0b,stroke:#d97706,color:#fff
    style D fill:#ef4444,stroke:#dc2626,color:#fff
    style E fill:#8b5cf6,stroke:#6d28d9,color:#fff
    style F fill:#8b5cf6,stroke:#6d28d9,color:#fff
    style G fill:#8b5cf6,stroke:#6d28d9,color:#fff

Tanda Konfigurasi Perlu Dipecah #

SINYAL BAHWA KONFIGURASI SUDAH TERLALU BESAR:

  ✗ terraform plan butuh lebih dari 5 menit (terlalu banyak API refresh)
  ✗ Satu apply mempengaruhi puluhan resource yang tidak berhubungan
  ✗ Tim berbeda sering konflik karena mengubah file yang sama
  ✗ Ada resource yang "tidak boleh disentuh" campur dengan yang rutin berubah
  ✗ Test perubahan kecil butuh menunggu plan seluruh infrastruktur
  ✗ State file lebih dari beberapa MB (ribuan resource)
  ✗ Tim tidak tahu lagi resource apa yang ada di konfigurasi ini

Prinsip Pemisahan: Lifecycle dan Blast Radius #

Cara paling efektif memecah konfigurasi bukan berdasarkan tipe resource, tapi berdasarkan dua pertanyaan: seberapa sering berubah, dan apa dampak jika ada yang salah?

PRINSIP LIFECYCLE:
  Resource yang berubah dengan frekuensi berbeda sebaiknya dipisah.

  Jarang berubah (bulan/tahun):
    VPC, subnets, routing, peering
    IAM roles, SCPs, organizations
    DNS zones (bukan records)
    → Satu konfigurasi, apply sangat jarang

  Sesekali berubah (minggu/bulan):
    Cluster Kubernetes, RDS instances
    Load balancers, security groups
    S3 buckets, KMS keys
    → Konfigurasi terpisah per domain

  Sering berubah (hari/minggu):
    Kubernetes deployments
    Task definitions ECS
    Lambda functions
    DNS records
    → Konfigurasi terpisah, bisa auto-apply

PRINSIP BLAST RADIUS:
  Resource yang kritis (downtime mahal) dipisah dari yang tidak kritis.
  Jika ada bug saat apply database konfigurasi,
  jangan sampai mempengaruhi networking.

Pola Layering: Dependency Antar Konfigurasi #

ARSITEKTUR LAYERED:

  Layer 1: Foundation (paling jarang berubah)
    ├── VPC, subnets, internet gateway
    ├── Route53 zones
    ├── IAM roles dasar
    └── S3 bucket untuk state
    Output: vpc_id, subnet_ids, route53_zone_id

  Layer 2: Platform (berubah saat ada upgrade infrastruktur)
    ├── EKS cluster / ECS cluster
    ├── RDS instances
    ├── ElastiCache
    └── Load balancers
    Input: vpc_id, subnet_ids (dari layer 1)
    Output: cluster_endpoint, db_endpoint, lb_dns_name

  Layer 3: Application (paling sering berubah)
    ├── Kubernetes deployments
    ├── Task definitions ECS
    ├── DNS records (pointing ke LB dari layer 2)
    └── Lambda functions
    Input: cluster_endpoint, db_endpoint (dari layer 2)

  Setiap layer hanya bergantung ke layer di bawahnya.
  Perubahan di layer atas tidak mempengaruhi layer bawah.
# Layer 3 membaca output dari Layer 2 via remote state
data "terraform_remote_state" "platform" {
  backend = "s3"
  config = {
    bucket = "my-terraform-state"
    key    = "platform/terraform.tfstate"
    region = "ap-southeast-1"
  }
}

resource "aws_route53_record" "app" {
  zone_id = data.terraform_remote_state.platform.outputs.route53_zone_id
  name    = "api.example.com"
  type    = "CNAME"
  ttl     = 300
  records = [data.terraform_remote_state.platform.outputs.lb_dns_name]
}

Struktur Direktori untuk Infrastruktur Besar #

infra-repo/
  ├── modules/                      ← Shared, versioned modules
  │   ├── networking/
  │   ├── eks-cluster/
  │   └── rds/
  │
  ├── foundation/                   ← Layer 1
  │   ├── networking/
  │   │   ├── main.tf
  │   │   ├── outputs.tf
  │   │   └── backend.tf
  │   └── iam/
  │
  ├── platform/                     ← Layer 2
  │   ├── eks/
  │   ├── databases/
  │   └── load-balancers/
  │
  ├── applications/                 ← Layer 3
  │   ├── api-service/
  │   ├── worker-service/
  │   └── frontend/
  │
  └── scripts/
      ├── plan-all.sh               ← Plan semua layer secara berurutan
      └── apply-all.sh
# plan-all.sh — plan semua layer dengan order yang benar
#!/bin/bash
set -e

ENVIRONMENT=${1:-staging}

echo "=== Layer 1: Foundation ==="
cd foundation/networking
terraform plan -var="environment=$ENVIRONMENT"

echo "=== Layer 2: Platform ==="
cd ../../platform/eks
terraform plan -var="environment=$ENVIRONMENT"

echo "=== Layer 3: Applications ==="
cd ../../applications/api-service
terraform plan -var="environment=$ENVIRONMENT"

Paralel vs Serial Execution #

SERIAL (default untuk dependency):
  Layer 1 selesai → Layer 2 mulai → Layer 3 mulai
  Cocok untuk dependency yang ketat antar layer

PARALEL (untuk konfigurasi yang independen):
  Layer 2: eks, databases, load-balancers bisa diapply bersamaan
  Masing-masing hanya bergantung ke Layer 1 (sudah selesai)

  Dalam CI/CD matrix:
  jobs:
    apply-platform:
      strategy:
        matrix:
          component: [eks, databases, load-balancers]
        max-parallel: 3
      steps:
        - run: terraform apply platform/${{ matrix.component }}/tfplan

  PERHATIAN: Paralel hanya aman jika tidak ada dependency antar komponen
  yang dirun paralel. Jika EKS butuh output dari databases, harus serial.

Mengelola Banyak Environment dengan Satu Konfigurasi #

# Pola: Satu konfigurasi, tiga environment, backend berbeda

# environments/dev/backend.tf
terraform {
  backend "s3" {
    bucket = "terraform-state-dev"
    key    = "platform/eks/terraform.tfstate"
    region = "ap-southeast-1"
  }
}

# environments/production/backend.tf
terraform {
  backend "s3" {
    bucket = "terraform-state-production"
    key    = "platform/eks/terraform.tfstate"
    region = "ap-southeast-1"
  }
}

# Konfigurasi utama (symlink atau shared) menggunakan variable
variable "environment" {
  type = string
}

module "eks" {
  source = "../../modules/eks"

  cluster_name    = "myapp-${var.environment}"
  node_count      = var.environment == "production" ? 5 : 2
  instance_type   = var.environment == "production" ? "m5.xlarge" : "t3.medium"
}

flowchart LR
    A["1 big state\n(200 resources)"] -->|"split"| B["networking\nstate"]
    A -->|"split"| C["compute\nstate"]
    A -->|"split"| D["database\nstate"]
    B --> E["Fast plan\nSmall blast radius"]
    C --> E
    D --> E

    style A fill:#ef4444,stroke:#dc2626,color:#fff
    style B fill:#3b82f6,stroke:#1e40af,color:#fff
    style C fill:#3b82f6,stroke:#1e40af,color:#fff
    style D fill:#3b82f6,stroke:#1e40af,color:#fff
    style E fill:#10b981,stroke:#059669,color:#fff

State Segmentation Strategy #

Saat infrastruktur tumbuh, satu state file menjadi bottleneck.

# REKOMENDASI: Pecah state berdasarkan blast radius

# Per environment + per component:
infrastructure/
├── networking/
│   ├── dev/
│   ├── staging/
│   └── production/
├── compute/
│   ├── dev/
│   ├── staging/
│   └── production/
└── database/
    ├── dev/
    ├── staging/
    └── production/
flowchart TD
    A["Monolithic
State"] -->|"Pecah"| B["networking/
state"]
    A -->|"Pecah"| C["compute/
state"]
    A -->|"Pecah"| D["database/
state"]
    A -->|"Pecah"| E["security/
state"]

    style A fill:#ffebee,stroke:#c62828
    style B fill:#e3f2fd,stroke:#1565c0
    style C fill:#e8f5e9,stroke:#2e7d32
    style D fill:#fff3e0,stroke:#e65100
    style E fill:#f3e5f5,stroke:#6a1b9a

Terragrunt untuk Scale #

# Terragrunt membantu mengelola banyak module di scale besar
# Fitur: DRY config, dependency management, parallel execution

# Install
brew install terragrunt

# Struktur Terragrunt
infrastructure/
├── terragrunt.hcl           # Root config
├── networking/
│   ├── terragrunt.hcl       # Module config
│   └── main.tf
├── compute/
│   ├── terragrunt.hcl
│   └── main.tf
└── database/
    ├── terragrunt.hcl
    └── main.tf

# Apply semua sekaligus
terragrunt run-all apply

# Apply dengan dependency ordering
terragrunt run-all apply --terragrunt-parallelism 3

Module Layering Strategy #

LAYERING STRATEGY:

Layer 1: Foundation
├── VPC, Subnets, Security Groups
├── IAM roles, KMS keys
└── Terraform state backend

Layer 2: Platform
├── EKS/GKE/ECS cluster
├── RDS instances
├── ElastiCache
└── Load balancers

Layer 3: Application
├── Application deployments
├── DNS records
├── CDN configuration
└── Monitoring dashboards

DEPENDENCY: Layer 3 → Layer 2 → Layer 1
flowchart TD
    subgraph L1["Layer 1: Foundation"]
        VPC["VPC"]
        IAM["IAM"]
        KMS["KMS"]
    end
    
    subgraph L2["Layer 2: Platform"]
        EKS["EKS"]
        RDS["RDS"]
        ALB["ALB"]
    end
    
    subgraph L3["Layer 3: Application"]
        APP["App Deploy"]
        DNS["DNS"]
        MON["Monitoring"]
    end
    
    L1 --> L2
    L2 --> L3
    
    style L1 fill:#e3f2fd,stroke:#1565c0
    style L2 fill:#fff3e0,stroke:#e65100
    style L3 fill:#e8f5e9,stroke:#2e7d32

Large State Management #

# State > 100MB bisa menyebabkan slow plan/apply
# SOLUSI: Split menjadi beberapa state

# Indikator state terlalu besar:
# - terraform plan > 5 menit
# - Memory usage tinggi
# - Frequent lock contention
# - Banyak unrelated changes dalam satu apply

# Split strategy by service:
# networking/terraform.tfstate
# compute/terraform.tfstate
# database/terraform.tfstate
# monitoring/terraform.tfstate

# Setiap state = satu team yang bertanggung jawab
# State file size monitoring
ls -lh terraform.tfstate

# State summary
terraform state list | wc -l

# Periksa state file content
terraform show -json | jq '.values.root_module.resources | length'

Ringkasan #

  • Tanda konfigurasi perlu dipecah: plan butuh lebih dari 5 menit, tim sering konflik di file yang sama, ada resource “tidak boleh disentuh” campur dengan yang rutin berubah.
  • Pecah berdasarkan lifecycle dan blast radius, bukan berdasarkan tipe resource — resource yang jarang berubah dan kritis dipisah dari yang sering berubah dan tidak terlalu kritis.
  • Pola layering (foundation → platform → application) memberikan dependency yang jelas: setiap layer hanya bergantung ke layer di bawahnya, perubahan di atas tidak mempengaruhi bawah.
  • Gunakan terraform_remote_state untuk membaca output antar konfigurasi yang terpisah — tapi ingat ini menciptakan coupling antar konfigurasi.
  • Komponen di layer yang sama (EKS, database, load balancer) bisa diapply secara paralel jika tidak ada dependency antar sesama.
  • Mulai sederhana — jangan premature decompose konfigurasi sebelum ada masalah nyata. Pecah hanya ketika sinyal sudah jelas muncul.

← Sebelumnya: Lifecycle Management   Berikutnya: Performance Optimization →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact