L
Infrastructure as Code (IaC) • Literally Web ID

Automasi Infrastruktur Skala Enterprise Menggunakan Terraform dan AWS VPC

⏱️ 18 menit baca • 📅 16 Oct 2026 • 👁️ 1 views
Automasi Infrastruktur Skala Enterprise Menggunakan Terraform dan AWS VPC

1. Konteks Industri & Analisis Akar Masalah

Dalam lanskap infrastruktur enterprise modern, kecepatan deployment, konsistensi lingkungan (environment consistency), dan mitigasi kesalahan manusia (human error) bukan lagi sekadar nilai tambah, melainkan syarat mutlak kelangsungan bisnis. Pendekatan tradisional di mana administrator jaringan melakukan provisioning server, router, subnet, dan firewall secara manual melalui AWS Management Console (sering disebut ClickOps) telah terbukti meninggalkan kerentanan fatal dalam skala produksi. Masalah utama dari ClickOps adalah tidak adanya jejak audit riil, tingginya risiko penyimpangan konfigurasi (configuration drift), ketidakmampuan mereplikasi lingkungan staging agar identik dengan produksi, serta lambatnya proses pemulihan bencana (Disaster Recovery).

Sebagai contoh nyata, sebuah platform e-commerce enterprise dengan stack backend Go/Node.js, database MySQL/MariaDB berklaster, serta caching Redis, sering kali mengalami kegagalan sistem bukan karena cacat kodenya, melainkan karena inkonsistensi tabel routing VPC, kesalahan konfigurasi Security Group lintas availability zone (AZ), atau kurang optimalnya alokasi NAT Gateway yang menyebabkan lonjakan latensi jaringan. Tanpa adanya dokumentasi infrastruktur yang hidup sebagai kode, melacak akar masalah dari latensi database atau kegagalan koneksi WebSocket antar microservice menjadi mimpi buruk bagi tim DevOps dan Site Reliability Engineer (SRE).

Perbandingan kondisi Sebelum dan Sesudah penerapan Infrastructure as Code (IaC) menggunakan Terraform dan AWS VPC:

  • Sebelum (Manual / ClickOps):
    • Waktu provisioning jaringan dasar memakan waktu hari atau minggu.
    • Sering terjadi drift konfigurasi antara development, staging, dan production.
    • Audit keamanan jaringan sangat sulit dilakukan karena perubahan terjadi secara ad-hoc.
    • Skalabilitas horizontal terhambat oleh keterbatasan dokumentasi arsitektur jaringan.
  • Sesudah (Terraform & Multi-Tier VPC):
    • Seluruh infrastruktur didefinisikan dalam kode version-controlled (GitOps).
    • Provisioning seluruh lingkungan produksi selesai dalam hitungan menit.
    • Konsistensi 100% terjaga antara berbagai environment.
    • Otomatisasi pengujian plan Terraform mencegah kesalahan syntax dan pelanggaran kebijakan keamanan sebelum apply.

[!IMPORTANT] Pendekatan deklaratif pada Terraform memastikan bahwa infrastruktur selalu berada pada desired state yang diinginkan, menghilangkan ruang untuk modifikasi manual yang tidak terdokumentasi di konsol AWS.


2. Konsep Inti & Deep Dive Arsitektur

Untuk membangun infrastruktur enterprise yang tangguh, kita harus memahami bagaimana AWS VPC bekerja di balik layar, mulai dari konstruksi logis hingga abstraksi state Terraform.

Prinsip Dasar State Management pada Terraform

Terraform bekerja dengan mencatat keadaan nyata sumber daya infrastruktur ke dalam file state (biasanya terraform.tfstate). Dalam lingkungan enterprise, state ini tidak boleh disimpan secara lokal, melainkan harus disimpan secara terpusat dan aman menggunakan backend remote seperti AWS S3 dengan enkripsi KMS, serta dikunci menggunakan DynamoDB untuk mencegah race condition saat eksekusi paralel oleh beberapa engineer atau pipeline CI/CD.

Desain Multi-Tier VPC

Arsitektur jaringan enterprise yang ideal memisahkan beban kerja berdasarkan tingkat sensitivitas keamanannya:

  1. Public Subnet: Berisi komponen yang menghadap langsung ke internet, seperti Application Load Balancer (ALB), Nginx/Caddy Reverse Proxy, dan NAT Gateway.
  2. Private Subnet (Application Tier): Berisi core backend (Go/Node.js/PHP-FPM), worker queue (Supervisor), dan real-time WebSocket server. Subnet ini tidak memiliki direct route ke Internet Gateway (IGW).
  3. Database Subnet (Data Tier): Berisi database relasional (MySQL/MariaDB) dan sistem in-memory caching (Redis). Akses mutlak dibatasi hanya dari Application Tier.
+-----------------------------------------------------------------------------------+
| AWS VPC (10.0.0.0/16)                                                             |
|                                                                                   |
|  +---------------------------+  +---------------------------+                     |
|  | Public Subnet (AZ-A)      |  | Public Subnet (AZ-B)      |                     |
|  | - Internet Gateway        |  | - Internet Gateway        |                     |
|  | - ALB & NAT Gateway       |  | - ALB & NAT Gateway       |                     |
|  +-------------+-------------+  +-------------+-------------+                     |
|                |                              |                                   |
|                v                              v                                   |
|  +---------------------------+  +---------------------------+                     |
|  | Private App Subnet (AZ-A) |  | Private App Subnet (AZ-B) |                     |
|  | - Go / Node.js / PHP      |  | - Go / Node.js / PHP      |                     |
|  +-------------+-------------+  +-------------+-------------+                     |
|                |                              |                                   |
|                v                              v                                   |
|  +---------------------------+  +---------------------------+                     |
|  | Database Subnet (AZ-A)    |  | Database Subnet (AZ-B)    |                     |
|  | - MySQL Cluster & Redis   |  | - MySQL Cluster & Redis   |                     |
|  +---------------------------+  +---------------------------+                     |
+----------------------------------------------------------------------------------+

3. Perbandingan Komparatif / Analisis Pendekatan

Berikut adalah analisis komparatif berbagai metode manajemen state dan arsitektur VPC untuk beban kerja enterprise:

Parameter / Fitur Lokal State Terraform Remote State (S3 + DynamoDB) ClickOps (AWS Console)
Colaborasi Tim Buruk (Risiko overwrite file) Sangat Baik (Locking mechanism) Sangat Buruk (Konflik manual)
Audit Trail Manual (Git log saja) Otomatis (Terraform state + Versioning S3) Sangat Terbatas / Tidak Ada
Recovery Time (DR) Lambat & Rentan Error Cepat & Otomatis Sangat Lambat
Keamanan Data State Rendah (Plain text di disk) Tinggi (Encrypted at rest KMS) N/A
Latensi Jaringan Tergantung Desain Dioptimalkan via AWS Region S3 Tergantung kebiasaan admin

[!TIP] Selalu gunakan remote state dengan enkripsi AES-256 dan aktifkan DynamoDB State Locking untuk mencegah kerusakan state akibat eksekusi konkuren.


4. Prasyarat Sistem & Persiapan Lingkungan

Sebelum mulai menulis kode Terraform, pastikan lingkungan kerja Anda memenuhi spesifikasi berikut:

  1. Terraform CLI: Versi >= 1.5.0 terinstal di mesin lokal atau runner CI/CD.
  2. AWS CLI: Terkonfigurasi dengan credential yang memiliki IAM permissions memadai (AdministratorAccess atau kebijakan custom yang mencakup EC2, VPC, S3, DynamoDB, IAM).
  3. Struktur Direktori Proyek:
    infra-enterprise-vpc/
    ├── backend.tf
    ├── provider.tf
    ├── variables.tf
    ├── vpc.tf
    ├── subnets.tf
    ├── gateways.tf
    ├── route_tables.tf
    ├── security_groups.tf
    └── outputs.tf
    

Verifikasi instalasi dengan perintah terminal:

terraform -version
aws sts get-caller-identity

5. Panduan Implementasi Langkah demi Langkah

Berikut adalah implementasi kode konfigurasi Terraform yang bersih, modular, dan siap produksi tanpa menggunakan placeholder.

provider.tf

terraform {
  required_version = ">= 1.5.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region

  default_tags {
    tags = {
      Environment = var.environment
      ManagedBy   = "Terraform"
      Project     = "Enterprise-Web-Infrastructure"
    }
  }
}

backend.tf

terraform {
  backend "s3" {
    bucket         = "enterprise-terraform-state-bucket-prod"
    key            = "vpc/terraform.tfstate"
    region         = "ap-southeast-1"
    encrypt        = true
    dynamodb_table = "enterprise-terraform-lock"
  }
}

variables.tf

variable "aws_region" {
  type        = string
  default     = "ap-southeast-1"
  description = "AWS Region utama untuk infrastruktur enterprise"
}

variable "environment" {
  type        = string
  default     = "production"
  description = "Nama environment deployment"
}

variable "vpc_cidr" {
  type        = string
  default     = "10.100.0.0/16"
  description = "CIDR block untuk VPC Enterprise"
}

variable "availability_zones" {
  type        = list(string)
  default     = ["ap-southeast-1a", "ap-southeast-1b"]
  description = "Daftar Availability Zones"
}

vpc.tf

resource "aws_vpc" "enterprise_vpc" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "${var.environment}-enterprise-vpc"
  }
}

gateways.tf

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.enterprise_vpc.id

  tags = {
    Name = "${var.environment}-igw"
  }
}

resource "aws_eip" "nat_eip" {
  count  = length(var.availability_zones)
  domain = "vpc"

  tags = {
    Name = "${var.environment}-nat-eip-${count.index}"
  }
}

resource "aws_nat_gateway" "nat" {
  count         = length(var.availability_zones)
  allocation_id = aws_eip.nat_eip[count.index].id
  subnet_id     = aws_subnet.public[count.index].id

  tags = {
    Name = "${var.environment}-nat-gw-${count.index}"
  }

  depends_on = [aws_internet_gateway.igw]
}

subnets.tf

resource "aws_subnet" "public" {
  count                   = length(var.availability_zones)
  vpc_id                  = aws_vpc.enterprise_vpc.id
  cidr_block              = cidrsubnet(var.vpc_cidr, 4, count.index)
  availability_zone       = var.availability_zones[count.index]
  map_public_ip_on_launch = true

  tags = {
    Name = "${var.environment}-public-subnet-${var.availability_zones[count.index]}"
    Tier = "Public"
  }
}

resource "aws_subnet" "private_app" {
  count             = length(var.availability_zones)
  vpc_id            = aws_vpc.enterprise_vpc.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 4, count.index + 2)
  availability_zone = var.availability_zones[count.index]

  tags = {
    Name = "${var.environment}-private-app-subnet-${var.availability_zones[count.index]}"
    Tier = "Application"
  }
}

resource "aws_subnet" "database" {
  count             = length(var.availability_zones)
  vpc_id            = aws_vpc.enterprise_vpc.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 4, count.index + 4)
  availability_zone = var.availability_zones[count.index]

  tags = {
    Name = "${var.environment}-database-subnet-${var.availability_zones[count.index]}"
    Tier = "Database"
  }
}

route_tables.tf

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.enterprise_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }

  tags = {
    Name = "${var.environment}-public-rt"
  }
}

resource "aws_route_table" "private" {
  count  = length(var.availability_zones)
  vpc_id = aws_vpc.enterprise_vpc.id

  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.nat[count.index].id
  }

  tags = {
    Name = "${var.environment}-private-rt-${var.availability_zones[count.index]}"
  }
}

resource "aws_route_table_association" "public" {
  count          = length(var.availability_zones)
  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}

resource "aws_route_table_association" "private_app" {
  count          = length(var.availability_zones)
  subnet_id      = aws_subnet.private_app[count.index].id
  route_table_id = aws_route_table.private[count.index].id
}

security_groups.tf

resource "aws_security_group" "alb" {
  name        = "${var.environment}-alb-sg"
  description = "Security group for public facing Application Load Balancer"
  vpc_id      = aws_vpc.enterprise_vpc.id

  ingress {
    description = "Allow HTTP from Anywhere"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "Allow HTTPS from Anywhere"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "app" {
  name        = "${var.environment}-app-sg"
  description = "Security group for backend app servers (Go/Node/PHP)"
  vpc_id      = aws_vpc.enterprise_vpc.id

  ingress {
    description     = "Allow traffic exclusively from ALB"
    from_port       = 8080
    to_port         = 8080
    protocol        = "tcp"
    security_groups = [aws_security_group.alb.id]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "database" {
  name        = "${var.environment}-database-sg"
  description = "Security group for MySQL and Redis"
  vpc_id      = aws_vpc.enterprise_vpc.id

  ingress {
    description     = "Allow MySQL traffic exclusively from App Servers"
    from_port       = 3306
    to_port         = 3306
    protocol        = "tcp"
    security_groups = [aws_security_group.app.id]
  }

  ingress {
    description     = "Allow Redis traffic exclusively from App Servers"
    from_port       = 6379
    to_port         = 6379
    protocol        = "tcp"
    security_groups = [aws_security_group.app.id]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

outputs.tf

output "vpc_id" {
  value       = aws_vpc.enterprise_vpc.id
  description = "ID dari VPC yang telah dibuat"
}

output "public_subnet_ids" {
  value       = aws_subnet.public[*].id
  description = "List ID Public Subnets"
}

output "private_app_subnet_ids" {
  value       = aws_subnet.private_app[*].id
  description = "List ID Private App Subnets"
}

output "database_subnet_ids" {
  value       = aws_subnet.database[*].id
  description = "List ID Database Subnets"
}

6. Bedah Kode & Penjelasan Detail Baris Kunci

  1. Fungsi cidrsubnet(var.vpc_cidr, 4, count.index): Fungsi ini secara dinamis memecah CIDR utama 10.100.0.0/16 dengan menambahkan 4 bit subnet, menghasilkan blok jaringan yang bersih tanpa risiko tumpang tindih (overlapping). Nilai count.index dan offset tambahan memastikan pemisahan logis antara subnet publik, aplikasi, dan database.
  2. Explicit Dependency (depends_on): Pada aws_nat_gateway.nat, kami mendeklarasikan depends_on = [aws_internet_gateway.igw]. Ini penting karena NAT Gateway memerlukan rute aktif ke Internet Gateway agar dapat meneruskan paket dari subnet privat ke luar VPC.
  3. Zero-Trust Security Group: Penerapan security_groups = [aws_security_group.alb.id] pada aturan ingress App Server memastikan bahwa backend web server (seperti Go, Node.js, atau PHP-FPM) sama sekali tidak dapat diakses langsung dari internet publik, melainkan harus melalui Load Balancer.

7. Skenario Produksi & Studi Kasus Dunia Nyata

Dalam studi kasus nyata beban tinggi (high concurrency), misalnya platform flash sale dengan jutaan request per detik yang ditangani oleh klaster Go backend dan database MySQL Master-Replica:

  • Traffic Spike: ALB mendistribusikan beban secara merata ke instance di Public Subnet.
  • Internal Routing: Permintaan diteruskan ke App Servers di Private Subnet melalui port 8080 tanpa melintasi public internet.
  • Data Persistence: Query database diredam oleh Redis cluster di Database Subnet, mengurangi beban langsung ke MySQL hingga 70%. NAT Gateway dikonfigurasi redundan di setiap AZ untuk mencegah single point of failure jika salah satu AZ mengalami gangguan hardware fisik.

8. Common Pitfalls, Anti-Patterns & Solusi

  • Anti-Pattern 1: Menyimpan Secret / Credential di dalam File Terraform.
    • Solusi: Gunakan AWS Secrets Manager atau HashiCorp Vault, dan integrasikan melalui data sources Terraform.
  • Anti-Pattern 2: Menggunakan Satu NAT Gateway untuk Seluruh AZ.
    • Solusi: Selalu buat satu NAT Gateway per Availability Zone untuk menjamin ketersediaan tinggi (High Availability) dan menghindari biaya lintas AZ yang membengkak.
  • Anti-Pattern 3: Hardcoding IP CIDR Blocks secara Manual.
    • Solusi: Manfaatkan fungsi cidrsubnet() bawaan Terraform untuk kalkulasi subnet yang otomatis dan aman.

9. Benchmark, Metrik Kinerja & Tuning Performa

Untuk menguji ketahanan jaringan VPC dan Load Balancer yang telah diprovisikan, gunakan tool pengujian beban seperti k6 atau wrk:

# Contoh uji beban HTTP menggunakan wrk dengan 12 koneksi simultan selama 30 detik
wrk -t12 -c400 -d30s https://alb-enterprise-production-123456.ap-southeast-1.elb.amazonaws.com/healthz

Metrik Kinerja Utama yang Harus Dipantau (via AWS CloudWatch):

  • NetworkIn / NetworkOut pada NAT Gateway.
  • Latency rata-rata pada ALB (TargetResponseTime).
  • HTTPCode_Target_5XX_Count untuk mendeteksi error pada backend web server.

10. Checklist Penerapan Produksi & Kesimpulan

Pastikan checklist berikut terpenuhi sebelum melakukan merge ke branch produksi:

  • Remote state S3 terenkripsi dengan KMS dan terkunci via DynamoDB.
  • Seluruh CIDR subnet tidak saling tumpang tindih.
  • Security Group menerapkan prinsip Least Privilege (tidak ada 0.0.0.0/0 pada port database atau aplikasi internal).
  • NAT Gateway tersedia di setiap AZ untuk redundansi.
  • Jalankan perintah terraform plan dan verifikasi bahwa tidak ada destroy yang tidak diinginkan.

Dengan mengimplementasikan panduan ini, Anda telah membangun fondasi arsitektur jaringan cloud enterprise yang aman, dapat diandalkan, dan siap diskalakan untuk mendukung pertumbuhan bisnis digital modern.

🏷️ Tags: #Devops #Terraform #Aws #Infrastructure As Code #Vpc