Kontrak Infrastruktur #

Kubernetes bukan sekadar alat orkestrasi kontainer. Keberadaannya mendefinisikan ulang cara tim pengembang (developers) dan tim platform/operasi (DevOps/SRE) berkolaborasi. Di pusat kolaborasi modern ini, terdapat satu konsep dasar yang sangat krusial namun jarang dibahas secara mendalam: Kontrak Infrastruktur (Infrastructure Contract).

Kontrak infrastruktur adalah kesepakatan dua arah yang implisit sekaligus eksplisit antara aplikasi (kontainer) dengan platform (Kubernetes). Kesepakatan ini menentukan apa yang wajib disediakan oleh platform untuk menjamin kestabilan jalannya kontainer, dan sebaliknya, apa yang harus dijanjikan aplikasi agar platform dapat mengambil keputusan secara otonom dan cerdas ketika terjadi insiden.


Memahami Kontrak Dua Arah #

Ketika kita mendeploy aplikasi ke dalam kluster Kubernetes, kita sedang menandatangani “kontrak kerja” bilateral:

Aplikasi (Developer) berjanji ke Kubernetes:
  ✓ "Aplikasi saya membutuhkan RAM minimal 256Mi dan CPU minimal 0.25 core untuk booting."
  ✓ "Aplikasi saya siap melayani traffic pengguna jika endpoint HTTP `/readyz` sukses."
  ✓ "Aplikasi saya terindikasi sehat jika endpoint HTTP `/healthz` merespons sukses."
  ✓ "Aplikasi saya diidentifikasi secara unik menggunakan tag label `app=payment-api`."
  ✓ "Aplikasi saya akan menangani sinyal terminasi (SIGTERM) untuk menutup koneksi database."

Kubernetes (Platform) berjanji ke Aplikasi:
  ✓ "Saya menjamin kapasitas minimum resource yang Anda minta (requests) selalu tersedia."
  ✓ "Saya akan menghentikan pengiriman traffic jika aplikasi Anda melaporkan diri belum siap."
  ✓ "Saya akan merestart kontainer Anda secara otomatis jika ia terindikasi macet (deadlock)."
  ✓ "Saya akan merutekan lalu lintas jaringan ke Pod yang tepat berdasarkan selector identitas Anda."
  ✓ "Saya akan memberikan waktu toleransi sebelum mematikan paksa aplikasi Anda saat rolling update."

Jika salah satu pihak melanggar janji di dalam kontrak ini, maka ketidakstabilan sistem di lingkungan produksi tidak dapat dihindari.


1. Kontrak Alokasi Resource: Requests, Limits, dan QoS #

Ketika kita menulis manifes Pod spesifikasi resources.requests dan resources.limits, kita sedang menetapkan kontrak resource dengan Scheduler dan Linux Kernel.

  • requests (Janji Minimum): Scheduler Kubernetes menggunakan angka ini untuk mencari node. Kubernetes menjamin kapasitas RAM/CPU sebesar nilai requests ini selalu terkunci eksklusif untuk kontainer kita.
  • limits (Batas Maksimum): Batas komputasi maksimum yang boleh dikonsumsi oleh kontainer.

Konsekuensi Pelanggaran Kontrak Resource: #

Jika kontainer kita mencoba menggunakan resource melebihi kesepakatan limit, Kubernetes dan kernel Linux akan memberikan “hukuman” yang berbeda:

  • Pelanggaran Batas Memori (RAM): RAM bersifat tidak dapat ditekan (non-compressible). Jika RAM fisik habis, sistem tidak bisa diperlambat. Oleh karena itu, jika kontainer mengonsumsi memori melebihi limits.memory, kernel Linux secara instan akan mematikan proses kontainer tersebut. Kontainer mati dengan status OOMKilled (Out Of Memory Killed) dan Kubelet akan merestart-nya.
  • Pelanggaran Batas CPU: CPU bersifat dapat ditekan (compressible). Jika kontainer mengonsumsi CPU melebihi limits.cpu, kernel Linux tidak akan mematikan kontainer. Sebaliknya, kernel akan membatasi kapasitas CPU (CPU Throttling). Aplikasi tetap hidup, namun kinerjanya melambat secara dramatis (mengakibatkan latensi API membengkak).

Kelas Quality of Service (QoS) Kluster #

Kubernetes mengelompokkan Pod ke dalam tiga kelas QoS berdasarkan konfigurasi request/limit untuk menentukan prioritas penggusuran (eviction priority) saat node kehabisan memori:

  1. Guaranteed (Prioritas Tertinggi): Nilai request dan limit untuk CPU dan memori ditulis secara eksplisit dan memiliki angka yang persis sama. Pod jenis ini adalah warga kelas satu yang paling dilindungi dari penggusuran.
  2. Burstable: Nilai request dan limit dikonfigurasi namun angkanya berbeda (limit lebih besar dari request). Pod akan digusur jika node kehabisan memori setelah Pod kelas BestEffort habis.
  3. BestEffort (Prioritas Terendah): Kita sama sekali tidak menulis spesifikasi request dan limit. Pod ini tidak mendapatkan jaminan resource apa pun dan menjadi target pertama yang akan dimatikan paksa saat node mengalami tekanan kapasitas.

2. Kontrak Kesehatan dan Ketersediaan: Health Probes #

Kubernetes tidak dapat menebak apakah aplikasi kita benar-benar berfungsi hanya dengan mendeteksi apakah proses kontainer sedang aktif. Sebuah aplikasi Java atau Node.js dapat berjalan tanpa error secara proses, namun di dalamnya mengalami kebocoran memori, deadlock thread, atau koneksi database yang terputus total.

Untuk itu, aplikasi harus menyediakan endpoint khusus untuk diperiksa oleh Kubelet melalui tiga jenis Probe:

Liveness Probe (Deteksi Kematian) #

Menjawab pertanyaan: “Apakah aplikasi ini masih hidup atau sudah macet total?”. Jika probe ini gagal beberapa kali berturut-turut, Kubelet akan mematikan kontainer dan membuat kontainer baru (restart).

Readiness Probe (Kesiapan Traffic) #

Menjawab pertanyaan: “Apakah aplikasi ini sudah siap menerima traffic pengguna?”. Selama aplikasi sedang melakukan startup, membaca cache awal, atau bermigrasi, probe ini harus bernilai gagal. Kubernetes tidak akan merestart kontainer jika probe ini gagal; ia hanya akan mengeluarkan IP Pod tersebut dari daftar load balancer Service agar pengguna tidak mendapatkan error 502/504.

Startup Probe (Perlindungan Booting) #

Dikhususkan untuk aplikasi warisan tebal yang membutuhkan waktu booting sangat lama (misal: 2-3 menit). Startup probe memblokir pemantauan liveness dan readiness probe sampai kontainer benar-benar menyelesaikan inisialisasi boot pertamanya.


ANTI-PATTERN: Endpoint Liveness & Readiness Identik
// KITA MELAKUKAN:
- Mengonfigurasi endpoint yang sama (misal: `/healthz`) untuk liveness dan readiness probe:
  livenessProbe: { httpGet: { path: /healthz } }
  readinessProbe: { httpGet: { path: /healthz } }
- Di dalam kode aplikasi, endpoint `/healthz` dikonfigurasi untuk memeriksa koneksi ke Database SQL eksternal.
// KONSEKUENSI DI PRODUKSI:
- Ketika database mengalami overload sesaat, endpoint `/healthz` gagal merespons.
- Kubernetes mendeteksi liveness probe gagal. Kontainer langsung dimatikan dan direstart secara masif lintas kluster (Cascading Restarts).
- Proses restart masif ini memperparah masalah jaringan dan memperpanjang downtime layanan, padahal kontainer aplikasi kita sebenarnya sehat walafiat.
✓ SOLUSI YANG BENAR:
- Pisahkan endpoint:
  - **Liveness Probe** memanggil endpoint ringan (misal: `/liveness`) yang hanya memverifikasi internal web server aktif dan tidak terikat koneksi database eksternal.
  - **Readiness Probe** memanggil endpoint `/readiness` yang secara aktif menguji kesiapan koneksi ke database, cache, dan dependensi API eksternal.
- Jika database mati, traffic akan dihentikan ke Pod tersebut, namun Pod tidak akan direstart tanpa henti.

3. Kontrak Identitas: Label & Selector #

Kubernetes tidak menggunakan alamat IP untuk merutekan lalu lintas jaringan ke Pod aplikasi. Karena IP Pod bersifat dinamis dan dapat berubah sewaktu-waktu, Kubernetes menggunakan Label dan Selector sebagai kontrak identitas untuk membangun hubungan antar objek.

# Manifes Service (Selector Kontrak)
apiVersion: v1
kind: Service
metadata:
  name: payment-service
spec:
  selector:
    app: payment-api # Service melacak Pod yang memiliki label ini
  ports:
  - port: 80
    targetPort: 8080

Selama Pod yang dideploy memiliki metadata label app: payment-api, objek Service secara otomatis akan memasukkan alamat IP Pod tersebut ke dalam daftar endpoint peruteannya. Hubungan longgar (loose coupling) ini memastikan kita dapat merilis versi baru aplikasi tanpa perlu memperbarui konfigurasi load balancer secara manual.


4. Kontrak Terminasi: Graceful Shutdown #

Siklus hidup aplikasi di Kubernetes sangat dinamis. Pod akan sering dihapus dan dibuat baru karena proses rolling update, autoscaling down, atau drain node pemeliharaan.

Ketika Kubernetes memutuskan untuk mematikan sebuah Pod, ia akan mengirimkan sinyal terminasi sistem operasi Linux standar. Aplikasi wajib menangani sinyal ini dengan baik untuk menghindari terputusnya transaksi pengguna di tengah jalan.

Alur Kerja Graceful Termination: #

  1. Pod diubah ke status Terminating: IP Pod dihapus dari seluruh endpoint Service agar tidak menerima traffic baru.
  2. Sinyal SIGTERM dikirim: Kubernetes mengirimkan sinyal SIGTERM (Signal 15) ke proses utama (PID 1) di dalam kontainer.
  3. Masa Tunggu Grace Period: Kubernetes menunggu selama masa toleransi yang ditentukan di manifes (default terminationGracePeriodSeconds: 30 detik).
  4. Eksekusi SIGKILL: Jika aplikasi masih aktif setelah masa toleransi habis, Kubernetes mengirimkan sinyal SIGKILL (Signal 9) untuk mematikan proses secara paksa.

Berikut adalah contoh kode implementasi penanganan sinyal SIGTERM di level aplikasi (menggunakan Node.js sebagai contoh):

// BENAR: Penanganan Graceful Shutdown Sinyal SIGTERM
const express = require('express');
const app = express();
const server = app.listen(8080);

process.on('SIGTERM', () => {
  console.log('Sinyal SIGTERM diterima. Memulai penutupan server secara bertahap...');
  
  // 1. Berhenti menerima request baru
  server.close(() => {
    console.log('HTTP Server telah ditutup.');
    
    // 2. Tutup koneksi database secara aman
    db.close().then(() => {
      console.log('Koneksi Database berhasil ditutup.');
      process.exit(0); // Exit dengan kode sukses
    });
  });
});

Aplikasi yang tidak menangani sinyal SIGTERM akan langsung mati mendadak saat rolling update terjadi, meninggalkan transaksi database menggantung dan memicu error koneksi bagi pengguna.


Ringkasan #

  • Kontrak Infrastruktur adalah Kesepakatan — Aplikasi mendeklarasikan kebutuhan dan perilakunya, dan Kubernetes menggunakan informasi tersebut untuk mengelola kluster secara mandiri.
  • CPU Throttling vs OOMKilled — Pelanggaran batas memori (limit RAM) berujung pada kontainer dimatikan paksa (OOMKilled), sedangkan pelanggaran CPU hanya memperlambat kinerja kontainer (throttling).
  • Semantik Probe yang Tepat — Liveness probe bertugas memicu restart kontainer yang macet, sedangkan Readiness probe mengontrol rute lalu lintas traffic pengguna (jangan gunakan endpoint database untuk liveness).
  • Graceful Shutdown SIGTERM — Pastikan kode aplikasi Anda menangkap sinyal SIGTERM untuk menyelesaikan transaksi berjalan dan menutup koneksi database sebelum mati.
  • ** terminationGracePeriodSeconds** — Selalu sesuaikan batas waktu shutdown di manifes kluster jika aplikasi Anda membutuhkan waktu lama untuk membersihkan data transient memori.

← Sebelumnya: Konfigurasi   Berikutnya: Arsitektur Overview →

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