Etcd & Cluster Consistency #

Di dalam sistem terdistribusi Kubernetes, konsistensi data adalah segalanya. Jika kita mendeploy aplikasi, merestart node, atau mengubah aturan jaringan, seluruh informasi tersebut harus disimpan di tempat yang tidak hanya aman dari kegagalan fisik, tetapi juga menjamin keakuratan data yang absolut di setiap node. Database tunggal yang mengemban tugas berat ini adalah etcd.

Jika API Server bertindak sebagai pintu masuk kluster, etcd adalah memori atau otak dari seluruh kluster Kubernetes. Semua status konfigurasi, metadata, serta status riil dari setiap resource—dari Pod kecil hingga Secret yang sensitif—disimpan di dalam etcd. Memahami cara kerja etcd, matematika konsensusnya, dan bagaimana merawat kesehatannya adalah prasyarat wajib bagi tim DevOps dan administrator sistem untuk mencegah hilangnya data kluster produksi secara permanen.


Algoritma Konsensus Raft dan Matematika Kuorum #

etcd bukanlah database tradisional yang berjalan di satu server tunggal. Demi menghindari single point of failure (SPOF), etcd berjalan sebagai kluster terdistribusi yang terdiri dari beberapa node (biasanya 3 atau 5 instansi). Tantangan utama dari database terdistribusi adalah bagaimana memastikan bahwa setiap node selalu memiliki catatan data yang sama persis di setiap detik.

Untuk memecahkan masalah ini, etcd menggunakan Algoritma Konsensus Raft. Algoritma Raft membagi waktu berjalan menjadi masa-masa pemilihan (terms) dan menetapkan tiga peran pada node: Leader, Follower, dan Candidate.

flowchart TD
    subgraph RaftConsensus ["Proses Konsensus Replikasi Log Raft"]
        direction TB
        Client["API Server (Client)"] -->|1. Tulis Data: Pod A = Running| Leader["etcd Leader (Node 1)"]
        
        Leader -->|2. Kirim Log AppendEntries| Follower1["etcd Follower (Node 2)"]
        Leader -->|2. Kirim Log AppendEntries| Follower2["etcd Follower (Node 3)"]
        
        Follower1 -->|3. Kirim ACK| Leader
        Follower2 -->|3. Kirim ACK| Leader
        
        NoteOverLeader{"Apakah ACK Diterima oleh Majority?"}
        Leader --> NoteOverLeader
        
        NoteOverLeader -- "Ya (Kuorum Terpenuhi)" --> Commit["4. Commit Log ke Disk & etcd DB"]
        Commit --> RespondClient["5. Kirim HTTP 200 OK ke API Server"]
        
        Commit -.->|6. Sebarkan Event Commit| Follower1
        Commit -.->|6. Sebarkan Event Commit| Follower2
    end

    style Leader stroke:#2ecc71,stroke-width:2px
    style Follower1 stroke:#3498db,stroke-width:2px
    style Follower2 stroke:#3498db,stroke-width:2px
    style Commit stroke:#e74c3c,stroke-width:2px

Matematika Kuorum: Mengapa Jumlah Node Harus Ganjil? #

Dalam algoritma Raft, sebuah transaksi penulisan data hanya dinyatakan berhasil (committed) apabila log transaksi tersebut telah berhasil ditulis ke disk fisik oleh mayoritas node di dalam kluster etcd. Mayoritas ini secara matematis dikenal sebagai Kuorum.

Rumus perhitungan kuorum etcd adalah sebagai berikut:

[\text{Kuorum} = \text{floor}\left(\frac{N}{2}\right) + 1]

Di mana (N) melambangkan jumlah total node etcd di dalam kluster.

Berdasarkan rumus di atas, mari kita analisis perbandingan jumlah node terhadap batas toleransi kegagalan (fault tolerance) kluster:

Jumlah Node ((N)) Nilai Kuorum Toleransi Kegagalan Maksimal Penjelasan
1 1 0 node Tidak ada toleransi kegagalan. Jika node mati, kluster hancur.
2 2 0 node Sangat buruk. Kehilangan 1 node membuat kuorum gagal (hanya tersisa 1 dari 2).
3 2 1 node Standar minimum produksi. Kluster tetap berfungsi jika 1 node mati.
4 3 1 node Tidak efisien. Toleransi sama dengan 3 node, namun memakan biaya ekstra 1 server.
5 3 2 node Direkomendasikan untuk produksi kritis. Mampu menahan 2 node mati sekaligus.
6 4 2 node Tidak efisien. Toleransi sama dengan 5 node.

Menghindari Split-Brain #

Menggunakan jumlah node ganjil menjamin bahwa jika terjadi gangguan jaringan yang membagi kluster menjadi dua bagian terpisah secara fisik (network partition), hanya akan ada satu bagian wilayah yang berhasil mempertahankan kuorum.

Misalnya, pada kluster 5 node yang terpecah menjadi wilayah A (3 node) dan wilayah B (2 node):

  • Wilayah A memiliki 3 node, memenuhi kuorum minimum ((\ge 3)). Wilayah ini tetap dapat memproses transaksi baca dan tulis secara normal.
  • Wilayah B hanya memiliki 2 node, kurang dari kuorum minimum. Wilayah ini secara otomatis menolak transaksi penulisan guna mencegah percabangan data yang bertolak belakang (Split-Brain). Saat jaringan pulih, node di Wilayah B akan menyelaraskan datanya dengan Wilayah A.

Model Penyimpanan MVCC dan Defragmentasi #

etcd mengadopsi model penyimpanan MVCC (Multi-Version Concurrency Control). Berbeda dengan database tradisional yang langsung menimpa data lama saat terjadi pembaruan (in-place updates), etcd tidak pernah menghapus data lama secara langsung.

Setiap kali data diperbarui, etcd akan membuat versi salinan baru dengan nomor Revision yang terus meningkat secara global. Nomor revision ini bertindak sebagai stempel waktu logis.

  • Membaca data masa lalu: Karena menyimpan riwayat versi, etcd memungkinkan API Server membaca status objek kluster pada revision tertentu di masa lalu.
  • Mekanisme Watch Efisien: Revision bertindak sebagai jangkar pelacak. Jika koneksi jaringan antara API Server dan etcd terputus selama beberapa detik, API Server dapat menyambung kembali dan meminta: “Kirimkan seluruh event perubahan yang terjadi sejak revision nomor 10450”.

Secara internal, etcd memetakan data dalam dua lapisan:

  1. B-tree di Memori (RAM): Menyimpan indeks berupa nama key dan sejarah nomor revision.
  2. bbolt DB di Disk (Penyimpanan): Database key-value transaksional sederhana yang menyimpan nomor revision sebagai key sesungguhnya dan isi objek JSON Kubernetes sebagai valuenya.

Masalah Fragmentasi Space Disk #

Karena etcd terus-menerus menyimpan versi revision baru, kapasitas disk fisik penyimpanan database akan membengkak dari waktu ke waktu. Untuk mencegah kehabisan kapasitas, etcd melakukan proses Compaction (pemadatan) secara berkala (Kubernetes biasanya memicu ini setiap 5 menit secara otomatis). Compaction akan menghapus seluruh catatan revision historis sebelum batas revision tertentu.

Namun, proses Compaction tidak mengembalikan ruang disk kosong tersebut ke sistem operasi. Ruang kosong tersebut tetap dikunci oleh etcd dan ditandai sebagai ruang internal yang tidak terpakai. Kondisi ini disebut Fragmentasi. Jika fragmentasi dibiarkan tanpa tindakan, etcd akan menyentuh batas kuota database maksimum (secara default berkisar antara 2 GB hingga 8 GB) dan secara otomatis mengunci kluster ke dalam status Read-Only (database space exceeded).

Untuk membebaskan ruang disk secara fisik dan mengembalikannya ke sistem operasi host, kita wajib menjalankan proses Defragmentasi.

# Lakukan kompresi/compaction secara manual hingga revision ke 100000
ETCDCTL_API=3 etcdctl compact 100000 \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# Jalankan defragmentasi untuk membebaskan ruang disk fisik (lakukan secara bergantian di setiap node)
ETCDCTL_API=3 etcdctl defrag \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

Strategi Backup dan Restore etcd untuk Pemulihan Bencana #

Banyak tim DevOps melakukan kesalahan dengan menganggap bahwa melakukan backup manifes YAML aplikasi (misal menggunakan GitOps) sudah cukup. Manifes Git tidak menyimpan status operasional dinamis kluster seperti alokasi IP Service, status volume penyimpanan cloud, metadata sertifikat, status operator, dan sejarah deployment yang sedang berjalan. Satu-satunya cara memulihkan kluster secara utuh setelah terjadi bencana infrastruktur adalah dengan memulihkan snapshot data etcd.

Langkah 1: Mengambil Snapshot etcd yang Valid #

Untuk mengambil snapshot etcd secara konsisten, kita wajib menggunakan tool etcdctl dengan menyertakan sertifikat keamanan TLS kluster:

# Backup snapshot etcd
ETCDCTL_API=3 etcdctl snapshot save /var/lib/backup/etcd-snapshot-prod.db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

Setelah snapshot disimpan, pastikan kita memverifikasi keutuhan file snapshot tersebut menggunakan status command:

# Verifikasi integritas snapshot database
ETCDCTL_API=3 etcdctl snapshot status /var/lib/backup/etcd-snapshot-prod.db --write-out=table

Langkah 2: Proses Restore Snapshot #

[!CAUTION] Proses pemulihan (restore) etcd bersifat destruktif. Tindakan ini akan menghapus seluruh data kluster saat ini dan memundurkan status kluster ke waktu snapshot diambil. Selama pemulihan berlangsung, seluruh proses kontroler Kubernetes harus dihentikan sementara.

Untuk memulihkan kluster multi-node dari snapshot, kita harus menjalankan perintah restore di setiap node etcd secara mandiri sebelum menjalankan proses API Server:

# Jalankan pemulihan data di Node-1
ETCDCTL_API=3 etcdctl snapshot restore /var/lib/backup/etcd-snapshot-prod.db \
  --name=etcd-node-1 \
  --data-dir=/var/lib/etcd-restored \
  --initial-cluster=etcd-node-1=https://10.240.0.10:2380,etcd-node-2=https://10.240.0.11:2380,etcd-node-3=https://10.240.0.12:2380 \
  --initial-cluster-token=etcd-token-token-baru \
  --initial-advertise-peer-urls=https://10.240.0.10:2380

Setelah perintah restore dijalankan di seluruh node, kita perlu:

  1. Mengubah jalur direktori data etcd di dalam konfigurasi manifest etcd host (biasanya di /etc/kubernetes/manifests/etcd.yaml) untuk mengarah ke /var/lib/etcd-restored.
  2. Merestart layanan Kubelet untuk memuat ulang konfigurasi baru.
  3. Memeriksa kesehatan kluster dengan perintah kubectl get nodes.

Persyaratan Kecepatan Tulis Disk (fsync) dan SSD #

etcd adalah sistem yang sangat sensitif terhadap latensi I/O disk. Setiap kali data ditulis ke etcd, algoritma Raft mewajibkan data log tersebut disimpan secara permanen ke disk fisik menggunakan operasi sistem fsync sebelum transaksi dikonfirmasi ke kluster.

Operasi fsync memaksa sistem operasi Linux untuk mengabaikan memori cache sistem operasi dan menulis data secara langsung ke disk fisik. Pada disk mekanis tradisional (HDD) atau media penyimpanan jaringan (network storage seperti AWS EBS non-SSD), operasi ini memakan waktu yang lama.

Visualisasi Dampak Latensi fsync Tinggi pada etcd:

flowchart TD
    A["Latensi Disk Tinggi (HDD / Slow Cloud Storage)"] --> B["Operasi fsync memakan waktu > 10ms"]
    B --> C["Heartbeat Raft antar node etcd terlambat sampai"]
    C --> D["Follower menganggap Leader mati (karena tidak ada heartbeat)"]
    D --> E["Memicu pemilihan Leader baru (Leader Election Drop) secara berulang"]
    E --> F["Kluster mengalami ketidakstabilan total (split-brain parsial & write timeouts)"]

Untuk menjamin stabilitas kluster produksi, etcd mensyaratkan:

  • Latensi fsync pada persentil ke-99 (p99) harus berada di bawah 10 milidetik (idealnya di bawah 1 milidetik).
  • Kita wajib menggunakan solid-state drive (SSD) dengan IOPS yang mumpuni. Sangat dilarang keras menempatkan direktori data etcd pada harddisk mekanis (HDD) atau media penyimpanan bersama (shared network filesystem seperti NFS).

Anti-Pattern dalam Pengelolaan etcd #

Berikut adalah dua kesalahan fatal yang sering kali dilakukan oleh tim administrator sistem saat mengelola etcd di lingkungan produksi:

Anti-Pattern 1: Menggunakan HDD atau Shared Storage untuk Direktori Data etcd #

Kesalahan fatal dalam pemilihan media penyimpanan fisik database kluster.

ANTI-PATTERN: Menempatkan Direktori Data etcd pada Network Storage Lambat
// KITA MELAKUKAN:
- Menjalankan kluster etcd mandiri di lingkungan cloud AWS.
- Menyimpan direktori data `/var/lib/etcd` pada volume penyimpanan EBS bertipe `gp2` 
  dengan IOPS rendah tanpa melakukan alokasi IOPS khusus (PIOPS).
- Atau menempatkan direktori data di atas sistem berkas jaringan bersama seperti NFS.

// KONSEKUENSI DI PRODUKSI:
- Kehilangan Kepemimpinan Berulang: Saat beban kluster meningkat (misal terjadi deployment 100 Pod sekaligus), 
  latensi fsync melonjak hingga >100ms. Node-node etcd akan kehilangan komunikasi heartbeat Raft.
- Kegagalan API Server: API Server akan mengembalikan error `503 Service Unavailable` secara beruntun 
  kepada client karena koneksi database etcd terputus akibat proses pemilihan leader yang tidak kunjung selesai.
- Kerusakan Data: Jika partisi jaringan mengalami flapping secara cepat, data dapat mengalami inkonsistensi.
✓ SOLUSI YANG BENAR:
- Selalu gunakan local SSD (seperti NVMe) yang terpasang langsung pada fisik server untuk direktori data etcd.
- Jika menggunakan cloud provider (AWS), gunakan volume bertipe `gp3` dengan minimum alokasi IOPS sebesar 3000, 
  atau gunakan tipe `io2` (Provisioned IOPS) guna menjamin batas latensi fsync tetap di bawah 1ms.
- Gunakan utilitas `fio` untuk melakukan benchmarking kinerja disk sebelum menginstal etcd di server:
  fio --name=write_latency --filename=test.fio --ioengine=psync --dx=write --fsync=1 --bufsiz=4k --size=10m

Anti-Pattern 2: Mengabaikan Pengaturan Quota Database dan Pembersihan Log (No Compaction) #

Membiarkan database etcd bertumbuh tanpa batas hingga mengunci sistem secara sepihak.

ANTI-PATTERN: Menjalankan etcd Tanpa Auto-Compaction
// KITA MELAKUKAN:
- Mengonfigurasi kluster etcd tanpa menyertakan argumen `--auto-compaction-retention`.
- Melakukan aktivitas deployment, scaling, dan penghapusan ribuan Pod setiap hari di lingkungan CI/CD yang padat.

// KONSEKUENSI DI PRODUKSI:
- Kehabisan Quota Space: Riwayat modifikasi revision terus menumpuk di file database bbolt DB. 
  Setelah database menyentuh batas default (2 GB), etcd mendeteksi alarm `NOSPACE` dan mengunci database.
- Kluster Lumpuh Total: Kluster Kubernetes secara mendadak berubah menjadi Read-Only. 
  Admin tidak dapat melakukan `kubectl delete`, `kubectl edit`, atau bahkan menskalakan aplikasi untuk menurunkan beban.
✓ SOLUSI YANG BENAR:
- Selalu aktifkan fitur auto-compaction pada konfigurasi start-up etcd dengan durasi retensi tertentu, misalnya 1 jam:
  --auto-compaction-retention=1
- Buat skrip cron job harian atau gunakan monitoring alert Prometheus untuk mendeteksi kapasitas penyimpanan etcd.
- Jika kluster terlanjur terkunci akibat alarm NOSPACE, lakukan langkah penyelamatan berikut:
  1. Dapatkan daftar alarm yang aktif: 
     `etcdctl alarm list`
  2. Dapatkan revision terbaru: 
     `etcdctl endpoint status --write-out=table`
  3. Lakukan compaction manual hingga nomor revision terbaru.
  4. Jalankan defragmentasi disk secara berurutan di setiap node.
  5. Hapus alarm yang mengunci kluster: 
     `etcdctl alarm disarm`

Ringkasan #

  • Sumber Kebenaran Kluster — etcd menyimpan seluruh status konfigurasi dan metadata Kubernetes; kehilangan etcd tanpa backup berarti kehilangan seluruh status kluster secara permanen.
  • Konsensus Kuat Raft — etcd menggunakan algoritma Raft untuk menjamin konsistensi kuat (strong consistency), di mana data hanya dianggap committed jika berhasil ditulis oleh mayoritas node.
  • Kaidah Kuorum Ganjil — Selalu gunakan jumlah node ganjil (3 atau 5) guna mengoptimalkan toleransi kegagalan dan menghindari resiko perpecahan data (split-brain) saat terjadi partisi jaringan.
  • Sistem Penyimpanan MVCC — etcd tidak menimpa data lama melainkan membuat salinan revision baru, memungkinkan API Server melacak sejarah perubahan status objek kluster secara andal.
  • Manajemen Ruang Disk — Jalankan proses Compaction secara rutin guna membuang sejarah revision lama, dan ikuti dengan proses Defragmentasi guna mengembalikan ruang disk kosong ke OS.
  • Snapshot untuk Bencana — Backup manifes YAML tidaklah cukup; kita wajib menjadwalkan pengambilan snapshot biner etcd secara berkala dan menguji proses restorasi secara periodik.
  • SSD adalah Kebutuhan Mutlak — Latensi operasi fsync disk wajib berada di bawah 10ms (ideal <1ms); penggunaan HDD atau media penyimpanan jaringan lambat akan merusak komunikasi konsensus Raft.

← Sebelumnya: Controller Manager   Berikutnya: Anatomi Pod →

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