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:#fffTanda 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:#fffState 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:#6a1b9aTerragrunt 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:#2e7d32Large 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_stateuntuk 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 →