Infrastructure as Code — DevOps

Apa itu Infrastructure as Code? Infrastructure as Code (IaC) adalah praktik mendefinisikan dan mengelola infrastruktur menggunakan file konfigurasi, bukan…

Infrastructure as Code (IaC) artinya server dan semua perlengkapannya ditulis sebagai file teks, lalu sebuah tool membuat kenyataannya sama dengan file itu. Bayangin denah rumah dan kontraktor yang membawa buku catatan. Kamu cuma mengubah denahnya. Kontraktor membandingkan denah dengan rumah yang berdiri, lalu mengerjakan selisihnya saja: tambah kamar berarti bangun, ganti warna cat berarti cat ulang di tempat, tapi pindah fondasi berarti bongkar lalu bangun lagi. Buku catatannya mencatat apa saja yang sudah dia bangun. Tool IaC paling populer untuk membuat resource cloud adalah Terraform: denahnya file .tf, kontraktornya terraform, buku catatannya state.

web-1 dan web-2 dibuat dengan tangan lewat dashboard provider (pelajaran VPS Setup). Tiga bulan lagi, siapa yang ingat ukurannya, region-nya, atau SSH key mana yang dipasang? Dengan Terraform, semua itu ada di file yang masuk Git: bisa dibuat ulang persis, punya riwayat, dan perubahannya di-review lewat PR seperti kode biasa. Contoh di sini memakai DigitalOcean; di AWS, GCP, atau Hetzner bentuknya sama, cuma nama resource-nya yang beda.

🧪 Tebakan cepat: kamu mengubah size web-1 dari s-1vcpu-1gb ke s-2vcpu-2gb. Terraform mengubah server yang ada, atau membuat server baru? Kalau region dari sgp1 ke blr1? Jawabannya ada di bagian "Update in-place atau replace".

web-1 dalam Terraform

provider "digitalocean" {} # token dari env DIGITALOCEAN_TOKEN

resource "digitalocean_droplet" "web_1" {
  name        = "web-1"
  region      = "sgp1"
  size        = "s-1vcpu-1gb"
  image       = "ubuntu-24-04-x64"
  resize_disk = false # CPU dan RAM saja, bisa diturunkan lagi
  ssh_keys    = ["3b:16:bf:e4:8b:00:8b:b8:59:8c:a9:d3:f0:19:45:fa"]
  tags        = ["catatan-api"]
}

output "web_1_ip" {
  value = digitalocean_droplet.web_1.ipv4_address
}

init, plan, apply

Perintah Yang terjadi
terraform init mengunduh provider dan menyiapkan tempat state. Sekali per folder.
terraform plan mengecek keadaan nyata (refresh), membandingkan dengan file, lalu mencetak rencana. Gak mengubah apa pun.
terraform apply mencetak rencana yang sama, menunggu kamu mengetik yes, lalu menjalankannya dan mencatat hasilnya di state.
terraform destroy menghapus semua resource yang tercatat di state.

web-1 sudah ada sebelum file ini ditulis. Tanpa langkah tambahan, apply akan membuat droplet kedua. Jadi masukkan dulu yang sudah ada ke state dengan terraform import digitalocean_droplet.web_1 412345678 (id droplet-nya), lalu pastikan terraform plan bilang No changes. Setelah itu baru ubah file.

Ini apply setelah size diubah ke s-2vcpu-2gb, bagian akhirnya:

  Enter a value: yes

digitalocean_droplet.web_1: Modifying... [id=412345678]
digitalocean_droplet.web_1: Modifications complete after 58s [id=412345678]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

Outputs:

web_1_ip = "203.0.113.10"

Update in-place atau replace

Plan memberi tanda di depan tiap resource. Mengubah size:

  # digitalocean_droplet.web_1 will be updated in-place
  ~ resource "digitalocean_droplet" "web_1" {
        id                   = "412345678"
        name                 = "web-1"
      ~ size                 = "s-1vcpu-1gb" -> "s-2vcpu-2gb"
        tags                 = [
            "catatan-api",
        ]
        # (21 unchanged attributes hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

~ artinya update in-place: server yang sama di-resize, id dan IP-nya tetap. Mengubah region (potongan plan-nya, versi lengkapnya di Lab):

  # digitalocean_droplet.web_1 must be replaced
-/+ resource "digitalocean_droplet" "web_1" {
      ~ id                   = "412345678" -> (known after apply)
      ~ ipv4_address         = "203.0.113.10" -> (known after apply)
      ~ region               = "sgp1" -> "blr1" # forces replacement
    }

Plan: 1 to add, 0 to change, 1 to destroy.

-/+ artinya replace: server lama dihapus, lalu server baru dibuat. # forces replacement menunjuk atribut penyebabnya. Jadi jawaban tebakannya: size mengubah server yang ada, region membuat server baru. Droplet gak bisa pindah region atau ganti image di tempat. Akibatnya besar: IP baru (DNS catatan.example.com harus ikut diganti), dan isi disk hilang, termasuk volume Postgres di dalamnya. Selalu baca baris Plan: sebelum mengetik yes; + saja berarti create, - saja berarti destroy.

State: buku catatan Terraform

terraform.tfstate adalah file JSON yang mencatat resource mana di file yang terhubung ke benda nyata mana: digitalocean_droplet.web_1 adalah droplet 412345678 dengan IP 203.0.113.10. Tanpa state, Terraform gak tahu server itu miliknya, dan akan membuat yang baru. Dua aturan:

terraform {
  backend "s3" {
    bucket       = "catatan-terraform-state"
    key          = "prod/web.tfstate"
    region       = "ap-southeast-1"
    encrypt      = true
    use_lockfile = true # lock di bucket, Terraform 1.10 ke atas
  }
}

Versi lama memakai tabel DynamoDB (dynamodb_table) untuk lock. Pilihan lain: bucket yang kompatibel S3 seperti DigitalOcean Spaces, atau HCP Terraform.

Drift: diubah di luar Terraform

Saat traffic melonjak, Dewi me-resize web-1 ke s-2vcpu-4gb lewat dashboard. File dan state masih bilang s-2vcpu-2gb. Itu drift: kenyataan gak lagi sama dengan catatan. terraform plan berikutnya me-refresh dulu dan melaporkannya (dipotong):

Note: Objects have changed outside of Terraform

  # digitalocean_droplet.web_1 has changed
  ~ resource "digitalocean_droplet" "web_1" {
      ~ memory               = 2048 -> 4096
      ~ size                 = "s-2vcpu-2gb" -> "s-2vcpu-4gb"
    }

Lalu plan-nya berencana mengembalikan web-1 ke s-2vcpu-2gb, karena file adalah sumber kebenarannya. Dua pilihan: apply untuk mengembalikan, atau tulis s-2vcpu-4gb di file supaya plan bilang No changes. Jangan apply tanpa membaca plan. Banyak tim menjalankan terraform plan terjadwal di CI untuk menangkap drift.

Plan itu diff

Inti plan adalah membandingkan keadaan sekarang dengan yang diinginkan:

function planChanges(currentState, desiredState) {
  const creates = [];
  const updates = [];
  const destroys = [];
  for (const name in desiredState) {
    if (!currentState[name]) {
      creates.push({ action: "create", resource: name, config: desiredState[name] });
    } else if (JSON.stringify(currentState[name]) !== JSON.stringify(desiredState[name])) {
      const changes = {};
      for (const key in desiredState[name]) {
        if (currentState[name][key] !== desiredState[name][key]) changes[key] = desiredState[name][key];
      }
      updates.push({ action: "update", resource: name, changes });
    }
  }
  for (const name in currentState) {
    if (!desiredState[name]) destroys.push({ action: "destroy", resource: name });
  }
  return [...creates, ...updates, ...destroys];
}
current desired Hasil
{ server1: { type: "t3.micro" } } { server1: { type: "t3.small" }, server2: { type: "t3.micro" } } create server2 dengan config, lalu update server1 dengan changes: { type: "t3.small" }
{ db: { size: "10GB" }, cache: { size: "1GB" } } { db: { size: "10GB" } } destroy cache; db sama, dilewati

Terraform dan Ansible

Terraform membuat dan mengubah resource lewat API provider: server, DNS, database, bucket. Ini provisioning. Ansible masuk ke dalam server lewat SSH dan mengatur isinya: paket, file config, service. Ini configuration management. Keduanya idempotent: dijalankan sepuluh kali, hasil akhirnya sama dengan sekali, karena keduanya mengecek keadaan dulu dan cuma mengerjakan selisihnya. Alternatif Terraform: OpenTofu (turunan open source-nya) dan Pulumi (ditulis dengan TypeScript atau Python).

⚠️ Jebakan yang sering ketemu

TL;DR: IaC menulis infrastruktur sebagai file yang masuk Git, jadi bisa dibuat ulang, punya riwayat, dan di-review. Terraform (HCL, deklaratif) membaca provider dan resource, lalu init mengunduh provider, plan me-refresh dan mencetak rencana tanpa mengubah apa pun, apply menjalankannya setelah yes, destroy menghapus semuanya. ~ berarti update in-place (size: server sama, IP sama), -/+ berarti replace karena atribut # forces replacement (region, image: server baru, IP baru, disk hilang). State mencatat hubungan resource dengan benda nyatanya: jangan di-commit, simpan di remote backend dengan locking. Drift muncul saat ada yang mengubah di luar Terraform, dan plan melaporkannya sebagai "Objects have changed outside of Terraform". Plan pada dasarnya diff: create, lalu update dengan atribut yang berubah, lalu destroy. Terraform untuk provisioning, Ansible untuk mengatur isi server.

Yang akan kamu pelajari