Anatomi Pod #

Di dalam ekosistem Kubernetes, kita sering kali mendengar bahwa kontainer aplikasi tidak pernah berjalan secara langsung di atas Worker Node. Sebaliknya, kontainer selalu dibungkus di dalam sebuah objek abstraksi terkecil yang disebut Pod. Sebagai unit terkecil yang dapat dijadwalkan, Pod bertindak sebagai host logis yang menyediakan lingkungan komputasi bersama bagi satu atau lebih kontainer aplikasi.

Bagi tim pengembang dan DevOps, menulis spesifikasi Pod (Pod spec) bukan sekadar urusan menyalin templat YAML dari dokumentasi resmi. Di balik struktur manifesnya, terdapat puluhan field konfigurasi yang memiliki implikasi mendalam terhadap keamanan, kinerja, toleransi kegagalan, dan efisiensi biaya kluster. Memahami anatomi Pod secara menyeluruh membantu kita mendesain manifest aplikasi tingkat produksi (production-ready) yang stabil, aman, dan mudah dikelola.


Struktur Manifest Pod Secara Detail #

Setiap kali kita membuat manifest Pod menggunakan format YAML atau JSON, dokumen tersebut terdiri atas empat blok field utama. API Server memproses struktur ini secara ketat guna memvalidasi apakah objek tersebut memenuhi kontrak konfigurasi Kubernetes.

Berikut adalah kerangka utama manifes Pod beserta penjelasan perannya:

apiVersion: v1            # Versi API utama untuk objek inti Kubernetes
kind: Pod                 # Jenis resource yang ingin kita buat
metadata:                 # Metadata identitas (nama, namespace, label, anotasi)
  name: payment-api-prod  # Nama unik Pod di dalam namespace yang sama
  namespace: finance      # Namespace logis tempat Pod ditempatkan
spec:                     # Spesifikasi teknis kontainer, volume, dan kebijakan runtime
  containers:
  - name: web-app
    image: nginx:1.25

Di samping ketiga field masukan (input) di atas, terdapat satu blok field penting yang tidak pernah kita tulis secara manual di dalam dokumen manifes, yaitu status.

  • Dikelola oleh Control Plane: Blok status diisi secara asinkron oleh Kubernetes (melalui Kubelet dan Controller) untuk melaporkan kondisi riil terkini dari Pod (misalnya IP Pod yang dialokasikan, status kesehatan kontainer, dan fase daur hidupnya).
  • Hanya untuk Dibaca: Kita hanya membaca blok status ini melalui perintah kubectl get pod -o yaml untuk keperluan observasi, debugging, dan pemantauan sistem.

Lapisan Identitas: Metadata, Labels, dan Annotations #

Blok metadata adalah kartu identitas dari Pod. Lapisan ini digunakan oleh seluruh komponen internal kluster untuk mengorganisasi jaringan, perizinan keamanan, dan integrasi dengan sistem eksternal.

Labels (Label) #

Label adalah pasangan key-value sederhana yang ditempelkan ke objek Pod. Label digunakan sebagai kriteria pencarian (Selector) untuk mengelompokkan Pod.

  • Service Selector: Objek Service menggunakan label selector untuk menentukan Pod mana saja yang berhak menerima lalu lintas jaringan.
  • Deployment & ReplicaSet: Menggunakan label selector untuk melacak jumlah replika Pod yang sedang berjalan aktif.
  • Horizontal Pod Autoscaler (HPA): Menargetkan Pod berdasarkan kecocokan label.

Kita disarankan menerapkan standardisasi label di produksi, misalnya:

metadata:
  labels:
    app.kubernetes.io/name: payment-gateway  # Nama aplikasi utama
    app.kubernetes.io/part-of: finance-sys    # Bagian dari sistem besar
    app.kubernetes.io/version: v1.4.2        # Versi rilis spesifik
    environment: production                  # Lingkungan deployment
    team: backend-engineers                  # Tim penanggung jawab

Annotations (Anotasi) #

Berbeda dengan Label yang digunakan untuk pencarian dan seleksi, Anotasi (Annotations) digunakan untuk menyimpan data non-identitas yang panjang. Anotasi biasanya dikonsumsi oleh pustaka eksternal, kontroler kustom, atau alat pemantauan.

Contoh penggunaan anotasi di produksi:

metadata:
  annotations:
    prometheus.io/scrape: "true"         # Memberitahu Prometheus untuk mengambil metrik
    prometheus.io/port: "9090"           # Port metrik aplikasi
    vault.hashicorp.com/agent-inject: "true" # Memicu injeksi sidecar Vault

Spesifikasi Kontainer dan Daur Hidup Image #

Inti dari deklarasi Pod berada di dalam array spec.containers. Bagian ini mendefinisikan kontainer aplikasi yang akan berjalan bersama di dalam satu Pod.

spec:
  containers:
  - name: api-server
    image: my-registry.io/finance/api:v1.4.2
    imagePullPolicy: IfNotPresent
    ports:
    - name: http-port
      containerPort: 8080
      protocol: TCP
    command: ["/app/bin/server"]
    args: ["--port=8080", "--verbose=true"]

Penjelasan Field Kontainer: #

  • ports: Mendefinisikan port kontainer. Sangat disarankan memberikan properti name (misalnya http-port) agar objek Service dapat menunjuk ke nama port tersebut alih-alih angka port statis. Hal ini memudahkan kita mengganti port aplikasi di kemudian hari tanpa perlu mengubah manifest Service.
  • command & args: Digunakan untuk menimpa (override) konfigurasi default Dockerfile. Properti command akan menimpa instruksi ENTRYPOINT pada image Docker, sedangkan args akan menimpa instruksi CMD.
  • imagePullPolicy: Menentukan kebijakan pengunduhan image kontainer dari registry ke Worker Node:
    • Always: Kubelet selalu menghubungi image registry untuk melakukan pull image terbaru setiap kali Pod dijalankan, meskipun image dengan tag tersebut sudah ada di disk lokal node. Ini adalah kebijakan default jika kita menggunakan tag image :latest.
    • IfNotPresent: Kubelet hanya akan melakukan pull image dari registry jika image tersebut belum tersedia di disk lokal node. Kebijakan ini sangat efisien untuk meminimalkan waktu booting kontainer (cold-start) di produksi.
    • Never: Kubelet tidak akan pernah mencoba menghubungi registry. Ia mengasumsikan image sudah dipasang secara manual di node host.

Kontrak Resource: Requests vs Limits #

Kubernetes mewajibkan kita mendeklarasikan kebutuhan komputasi kontainer melalui blok resources. Konfigurasi ini bertindak sebagai kontrak kerja antara aplikasi kita dan Scheduler kluster.

    resources:
      requests:
        cpu: "250m"      # 250 millicores (setara dengan 0.25 vCPU)
        memory: "256Mi"  # 256 Mebibytes (MiB)
      limits:
        cpu: "1000m"     # 1000 millicores (setara dengan 1 vCPU)
        memory: "512Mi"  # 512 Mebibytes (MiB)

CPU (Compressible Resource) #

CPU dikelola menggunakan scheduler CFS (Completely Fair Scheduler) di kernel Linux. CPU bersifat compressible (dapat ditekan/diperlambat):

  • Requests: Nilai CPU request digunakan oleh Scheduler untuk mencari node yang memiliki kapasitas tersisa. Nilai ini dijamin selalu didapatkan oleh kontainer.
  • Limits: Batas maksimum konsumsi CPU. Jika kontainer mencoba mengonsumsi CPU melebihi nilai limits, Kubernetes tidak akan membunuh kontainer tersebut. Kernel Linux hanya akan melakukan throttling (membatasi laju siklus CPU kontainer), yang mengakibatkan performa aplikasi melambat secara drastis.

Memory (Incompressible Resource) #

Memori dikelola secara kaku. Memori bersifat incompressible (tidak dapat ditekan):

  • Requests: Membantu Scheduler menempatkan Pod di node yang memiliki sisa memori fisik yang cukup.
  • Limits: Batas keras (hard limit) memori fisik. Jika kontainer mengonsumsi memori melebihi batas limits, kernel Linux akan memicu mekanisme Out Of Memory (OOM) Killer. Kontainer akan langsung dibunuh secara paksa dengan status OOMKilled (exit code 137).

Berikut perbandingan perilaku sistem operasi terhadap CPU dan Memory saat melampaui limit:

Dimensi Perilaku Batas CPU Terlewati Batas Memory Terlewati
Tindakan Sistem Throttling (Aplikasi melambat) OOMKilled (Kontainer mati mendadak)
Resilience Kontainer tetap hidup Kontainer di-restart otomatis oleh Kubelet
Dampak ke Pengguna Latensi request meningkat Layanan mengalami kegagalan sesaat (downtime)

Environment Variables dan Downward API #

Ada kalanya kontainer aplikasi membutuhkan konfigurasi dinamis yang bervariasi antar lingkungan deployment (Development, Staging, Production). Kubernetes menyediakan tiga mekanisme penyuntikan variabel lingkungan (environment variables):

    env:
    # 1. Nilai Statis Langsung
    - name: APP_MODE
      value: "production"

    # 2. Referensi dari ConfigMap / Secret
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: payment-secrets
          key: db-password

    # 3. Downward API (Mengambil Informasi Internal Pod)
    - name: MY_POD_IP
      valueFrom:
        fieldRef:
          fieldPath: status.podIP

Downward API #

Downward API memungkinkan kontainer aplikasi mengetahui metadata dirinya sendiri tanpa perlu melakukan kueri (query) API Server Kubernetes secara langsung menggunakan token. Hal ini meningkatkan keamanan kluster karena kita tidak perlu membuka izin RBAC bagi aplikasi hanya untuk mengetahui nama pod atau IP address host.

Field metadata yang umum diakses melalui Downward API meliputi:

  • metadata.name: Nama Pod saat ini.
  • metadata.namespace: Namespace tempat Pod berjalan.
  • status.podIP: Alamat IP internal Pod.
  • spec.nodeName: Nama Worker Node tempat Pod dijadwalkan.

Abstraksi Volume dan Volume Mounts #

Secara default, sistem file kontainer bersifat ephemeral (sementara). Jika kontainer mengalami crash dan di-restart oleh Kubelet, seluruh file baru yang ditulis di dalam kontainer tersebut akan hilang tanpa bekas. Untuk mempertahankan data, kita harus menggunakan abstraksi volumes.

spec:
  volumes:
  - name: config-directory
    configMap:
      name: app-config-files
  - name: cache-scratch
    emptyDir: {}
  - name: database-storage
    persistentVolumeClaim:
      claimName: db-pvc
  containers:
  - name: main-app
    image: my-app:v1
    volumeMounts:
    - name: config-directory
      mountPath: /app/config
      readOnly: true
    - name: cache-scratch
      mountPath: /tmp/cache

Karakteristik Volume Utama: #

  • emptyDir: Volume kosong sementara yang dibuat di dalam direktori penyimpanan node host. Volume ini hanya bertahan selama Pod hidup. Jika kontainer di dalam Pod mati dan di-restart, data di dalam emptyDir tetap aman. Namun, jika Pod dihapus dari node (evict atau scale down), emptyDir beserta seluruh isinya akan dihancurkan selamanya.
  • configMap / secret: Me-mount isi ConfigMap atau Secret sebagai file teks biasa di dalam direktori kontainer. Fitur ini secara otomatis memperbarui file di dalam kontainer ketika kita memperbarui objek ConfigMap di API Server (proses sinkronisasi memakan waktu sekitar 60 detik).
  • persistentVolumeClaim (PVC): Menghubungkan Pod ke volume penyimpanan persisten eksternal (seperti AWS EBS, Google Persistent Disk, atau NFS) yang daur hidupnya terpisah dari daur hidup Pod.

Health Checking: Liveness, Readiness, dan Startup Probes #

Kubernetes memantau kesehatan aplikasi kita menggunakan agen Kubelet melalui tiga mekanisme probe (uji kesehatan):

    livenessProbe:
      httpGet:
        path: /healthz/live
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 10
      failureThreshold: 3

    readinessProbe:
      httpGet:
        path: /healthz/ready
        port: 8080
      periodSeconds: 5
      failureThreshold: 2

    startupProbe:
      httpGet:
        path: /healthz/live
        port: 8080
      failureThreshold: 30
      periodSeconds: 10
flowchart TD
    PodStart["Pod Dijalankan (Container Booting)"] --> StartupRun{"1. Startup Probe\nApakah aplikasi sudah menyala?"}
    
    StartupRun -- "Gagal (Melampaui Batas)" --> StartupKill["Kubelet me-restart Kontainer"]
    StartupRun -- "Sukses" --> LiveReadyActive["Matikan Startup Probe\nAktifkan Liveness & Readiness Probes"]
    
    LiveReadyActive --> LivenessCheck{"2. Liveness Probe\nApakah aplikasi hang/deadlock?"}
    LiveReadyActive --> ReadinessCheck{"3. Readiness Probe\nApakah aplikasi siap terima traffic?"}
    
    LivenessCheck -- "Gagal" --> LiveKill["Kubelet me-restart Kontainer"]
    LivenessCheck -- "Sukses" --> LiveOK["Biarkan Berjalan"]
    
    ReadinessCheck -- "Gagal" --> ReadRemove["Lepas IP Pod dari Service Endpoint\n(Traffic dihentikan)"]
    ReadinessCheck -- "Sukses" --> ReadAdd["Sambungkan IP Pod ke Service Endpoint\n(Traffic diizinkan masuk)"]

    StartupKill --> PodStart
    LiveKill --> PodStart

Tiga Jenis Probe Secara Detail: #

  1. Liveness Probe: Menentukan apakah kontainer aplikasi kita masih hidup atau mengalami kegagalan sistemik (deadlock). Jika liveness probe mengembalikan kode HTTP error (seperti 500) beberapa kali berturut-turut sesuai failureThreshold, Kubelet akan membunuh kontainer dan merestartnya.
  2. Readiness Probe: Menentukan apakah kontainer sudah siap menerima lalu lintas jaringan (incoming traffic) dari pengguna. Jika readiness probe gagal, Kubernetes tidak akan membunuh kontainer. Ia hanya akan mencopot IP address Pod dari daftar endpoint Service. Pengguna tidak akan diarahkan ke Pod yang belum siap (misalnya saat aplikasi masih melakukan loading cache ke memori).
  3. Startup Probe: Digunakan khusus untuk mengamankan aplikasi yang membutuhkan waktu booting sangat lama (misal aplikasi Java enterprise lama yang butuh waktu 5 menit untuk menyala). Selama startup probe masih berjalan dan belum sukses, liveness dan readiness probe akan dinonaktifkan. Hal ini mencegah Kubelet membunuh kontainer secara prematur akibat salah mengira aplikasi sedang hang padahal masih dalam proses booting awal.

Kebijakan Graceful Termination dan Restart Policy #

Kubernetes didesain untuk dinamis. Pod dapat dipindahkan, dihapus, atau digantikan setiap saat. Untuk menghindari transaksi terputus di tengah jalan (corrupted transactions), Kubernetes menerapkan siklus pemutusan rapi (Graceful Termination):

Alur Penghapusan Pod:

1. Perintah Hapus Diterima (kubectl delete atau autoscale scale-down)
        │
        ▼
2. Pod status berubah menjadi "Terminating"
        │
        ▼
3. Eksekusi Paralel Dua Alur:
   ├── A. IP Pod dicopot dari seluruh Service Endpoints (Traffic baru ditolak)
   └── B. Kubelet mengirim sinyal SIGTERM ke proses utama (PID 1) di dalam kontainer
        │
        ▼
4. Aplikasi melakukan Pembersihan (Graceful Shutdown):
   ├── Selesaikan request HTTP yang sedang berjalan aktif
   └── Tutup koneksi database pool secara teratur
        │
        ▼
5. Tunggu hingga durasi "terminationGracePeriodSeconds" (default: 30 detik) habis
        │
        ▼
6. Jika proses belum mati, Kubelet mengirim sinyal SIGKILL (Mati Paksa)

Jika aplikasi kita memerlukan waktu pembersihan yang lama (misal membutuhkan waktu 45 detik untuk mengosongkan antrean antarmuka memori), kita wajib menaikkan nilai toleransi di manifest Pod:

spec:
  terminationGracePeriodSeconds: 60 # Memberikan waktu 60 detik bagi aplikasi sebelum dibunuh paksa

Restart Policy #

Ditentukan oleh properti spec.restartPolicy untuk mengendalikan tindakan Kubelet saat kontainer mati:

  • Always (Default): Selalu restart kontainer tanpa memedulikan kode keluar (exit code). Sangat tepat untuk aplikasi web/API yang harus terus menyala.
  • OnFailure: Hanya restart kontainer jika ia keluar dengan kode error (exit code tidak sama dengan 0). Sangat tepat untuk Job batch processing.
  • Never: Tidak pernah merestart kontainer. Tepat untuk tugas diagnostik satu kali jalan.

Menafsirkan Status dan Kondisi Pod #

Memahami status Pod sangat membantu kita melakukan troubleshooting masalah operasional di produksi:

# Periksa fase Pod secara cepat
kubectl get pod payment-api-prod

# Lihat riwayat event terperinci
kubectl describe pod payment-api-prod

Fase Pod (Pod Phases): #

  • Pending: Manifes Pod telah diterima oleh API Server, namun Scheduler belum berhasil menemukan node yang cocok, atau node sedang dalam proses mengunduh image kontainer dari registry.
  • Running: Pod telah diikat ke sebuah Worker Node, seluruh kontainer telah dibuat, dan minimal ada satu kontainer yang sedang berjalan atau dalam proses startup/restart.
  • Succeeded: Seluruh kontainer di dalam Pod telah selesai dijalankan dengan sukses (exit code 0) dan tidak akan direstart lagi (misalnya pada Job yang selesai).
  • Failed: Seluruh kontainer di dalam Pod telah berhenti, dan minimal ada satu kontainer yang berhenti dengan kode kesalahan (exit code bukan 0).
  • Unknown: API Server kehilangan komunikasi dengan Kubelet di Worker Node tempat Pod berada (biasanya akibat gangguan jaringan fisik server).

Anti-Pattern dalam Konfigurasi Pod #

Berikut adalah kesalahan fatal yang sering kali dilakukan oleh pengembang saat menyusun manifest Pod di lingkungan produksi:

Anti-Pattern 1: Mengabaikan Pengisian Requests/Limits (BestEffort QoS) #

Mendeploy Pod tanpa batasan resource demi kepraktisan penulisan YAML.

ANTI-PATTERN: Tidak Menuliskan Blok resources pada Container Spec
// KITA MELAKUKAN:
- Mendeploy Pod database dan API microservice tanpa mendeklarasikan spesifikasi CPU & RAM requests/limits.

// KONSEKUENSI DI PRODUKSI:
- Pod dikategorikan sebagai kelas QoS "BestEffort" (prioritas terendah).
- Jika Worker Node mengalami overload (kehabisan RAM fisik), Kubelet akan langsung memilih Pod BestEffort 
  sebagai target pertama untuk dibunuh paksa (evict) guna menyelamatkan kesehatan sistem node host.
- Aplikasi kita akan terus-menerus mati mendadak secara acak tanpa adanya log error internal dari aplikasi.
✓ SOLUSI YANG BENAR:
- Selalu deklarasikan blok CPU dan RAM requests secara realistis demi mendapatkan kelas QoS "Burstable" atau "Guaranteed".
- Ini memandu Scheduler mencari node yang benar-benar sanggup menampung load aplikasi secara fisik.

Anti-Pattern 2: Menggunakan Tag Image :latest di Produksi #

Mengandalkan tag default tanpa nomor versi eksplisit.

ANTI-PATTERN: Menulis image: my-app:latest pada Pod Spec
// KITA MELAKUKAN:
- Menulis tag :latest pada container image di manifest Deployment kluster produksi.
- Melakukan push pembaruan kode ke registry dengan tag :latest, lalu berharap Kubernetes memperbaruinya otomatis.

// KONSEKUENSI DI PRODUKSI:
- Tidak Ada Perubahan: Karena Kubernetes bekerja secara deklaratif, jika nama image dan tag tidak berubah 
  (tetap :latest), Deployment Controller menganggap tidak ada perubahan konfigurasi. Aplikasi kita tidak akan di-update.
- Jika node mengalami crash dan melakukan restart Pod, ia akan melakukan pull image terbaru secara diam-diam. 
  Hal ini mengakibatkan situasi kacau di mana sebagian Pod menjalankan kode lama dan sebagian menjalankan kode baru.
✓ SOLUSI YANG BENAR:
- Selalu gunakan tag versi yang eksplisit dan unik (misalnya nomor commit SHA Git atau versi semantik seperti `:v1.4.2`).
- Ini menjamin konsistensi kode program yang berjalan di seluruh replika Pod kluster produksi kita.

Ringkasan #

  • Unit Penjadwalan Terkecil — Pod adalah abstraksi host logis yang mewadahi satu atau beberapa kontainer aplikasi agar dapat berjalan bersama di dalam kluster.
  • Kontrak Komputasi Wajib — Selalu deklarasikan nilai CPU/Memory requests dan limits di setiap kontainer produksi untuk menjamin prioritas alokasi resource dan kelas QoS yang aman.
  • Penanganan Throttling vs OOMKilled — Melebihi limit CPU memicu throttling (aplikasi lambat), sedangkan melebihi limit Memory memicu OOMKilled (kontainer mati seketika).
  • Dua Arah Hubungan Label — Gunakan Label untuk pengelompokan penargetan Service/HPA, dan gunakan Anotasi untuk menyimpan metadata penunjang tools eksternal.
  • Uji Kesehatan Aplikasi — Terapkan Liveness Probe untuk mendeteksi deadlock, Readiness Probe untuk mengatur rute lalu lintas traffic jaringan, dan Startup Probe untuk melindungi proses booting yang lama.
  • Shutdown Secara Teratur — Pastikan aplikasi mendengarkan sinyal SIGTERM untuk melakukan graceful shutdown, dan sesuaikan nilai terminationGracePeriodSeconds sesuai kebutuhan pembersihan resource.
  • Gunakan Tag Eksplisit — Hindari tag image :latest guna menjamin ketepatan versi dan kelancaran siklus rolling update deklaratif Kubernetes.

← Sebelumnya: Etcd & Cluster Consistency   Berikutnya: Single & Multi Container →

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