Load Balancing & kube-proxy #
Di dalam ekosistem Kubernetes, objek Service menyediakan alamat IP virtual (ClusterIP) dan nama DNS internal yang stabil bagi sekumpulan Pod yang dinamis. Ketika sebuah aplikasi mengirimkan request HTTP ke alamat IP virtual Service tersebut, ada mekanisme di balik layar yang bertugas mendistribusikan lalu lintas data tersebut ke salah satu Pod backend secara merata.
Komponen utama yang bertanggung jawab mengelola penyeimbangan beban (load balancing) internal ini adalah kube-proxy. kube-proxy adalah sebuah agen (network agent) yang berjalan sebagai DaemonSet di setiap node worker kluster kita.
Satu konsep penting yang wajib kita pahami sejak awal adalah: kube-proxy tidak memproses lalu lintas data secara langsung. kube-proxy tidak berada di dalam jalur data (data path) utama. kube-proxy bertindak sebagai pengatur aturan jaringan (control agent) yang bertugas mengonfigurasi kernel Linux lokal agar kernel tersebutlah yang melakukan pengalihan paket data secara instan di tingkat kernel VM host.
Artikel ini akan membahas arsitektur kube-proxy, cara kerja penyeimbangan beban di dalam mode iptables dan IPVS, perbedaan performa keduanya, serta teknik kustomisasi lalu lintas eksternal.
Cara Kerja kube-proxy: Pemantauan API Server #
kube-proxy bekerja dengan cara terus-menerus memantau (watch) Kubernetes API Server. Ia mendengarkan setiap kali ada pembuatan, perubahan, atau penghapusan objek Service, Endpoints, dan EndpointSlice di dalam kluster.
Ketika sebuah Service baru dibuat:
- API Server mengalokasikan alamat IP virtual (ClusterIP, misalnya
10.96.45.12) untuk Service tersebut. - Kubernetes Controller Manager mencatat seluruh alamat IP Pod yang sehat yang cocok dengan selector Service ke dalam daftar Endpoints.
- kube-proxy di setiap node worker menangkap event tersebut, lalu menuliskan aturan jaringan baru (routing rules) ke dalam firewall kernel Linux lokal di node worker-nya masing-masing.
Dengan membiarkan kernel Linux host yang mengalihkan paket data, Kubernetes menghindari terjadinya bottleneck performa. Paket data dialihkan dengan kecepatan hardware kernel tanpa perlu berpindah konteks (context switching) ke proses aplikasi user-space.
Penyelaman Mode iptables (Default Mode) #
Secara default, kube-proxy dikonfigurasi menggunakan mode iptables. iptables adalah utilitas administrasi firewall bawaan kernel Linux yang bertugas memotong, memodifikasi, dan mengalihkan paket data berdasarkan aturan terstruktur.
Ketika menggunakan mode iptables, kube-proxy akan membuat serangkaian rantai aturan (chains) khusus untuk setiap Service di dalam kluster.
Mari kita perhatikan bagaimana rantai aturan iptables ini disusun oleh kube-proxy:
flowchart TD
Packets["Paket Masuk (Target: 10.96.45.12:80)"] --> KService["Chain: KUBE-SERVICES"]
KService -- "Match ClusterIP" --> KServiceChain["Chain: KUBE-SVC-API-SERVICE"]
KServiceChain -- "Probabilitas 33%" --> KEPodA["Chain: KUBE-SEP-POD-A"]
KServiceChain -- "Probabilitas 50% (Sisa)" --> KEPodB["Chain: KUBE-SEP-POD-B"]
KServiceChain -- "Fallback (Sisa)" --> KEPodC["Chain: KUBE-SEP-POD-C"]
KEPodA --> DNATA["DNAT ke Pod A (10.244.1.5:8080)"]
KEPodB --> DNATB["DNAT ke Pod B (10.244.2.3:8080)"]
KEPodC --> DNATC["DNAT ke Pod C (10.244.1.9:8080)"]
1. Alur Rantai iptables (Step-by-Step) #
Mari kita bedah rantai aturan di atas ketika client memanggil Service api-service (10.96.45.12:80) yang memiliki 3 replika Pod backend:
- KUBE-SERVICES: Paket data pertama kali ditangkap oleh rantai utama
KUBE-SERVICES. Rantai ini mendeteksi: “Apakah IP tujuan paket ini cocok dengan ClusterIP Service kita?”. Jika cocok (tujuan10.96.45.12), lompat ke rantai khusus Service tersebut:KUBE-SVC-API-SERVICE. - KUBE-SVC-API-SERVICE (Probabilitas Acak): Karena Service memiliki 3 Pod backend, rantai ini menggunakan modul statistik kernel
statisticuntuk mendistribusikan lalu lintas secara acak berdasarkan nilai probabilitas:- Baris 1: Dengan probabilitas 0.33 (33%), lompat ke rantai
KUBE-SEP-POD-A(Pod A). - Baris 2: Dengan probabilitas 0.50 (50% dari sisa 66% lalu lintas), lompat ke rantai
KUBE-SEP-POD-B(Pod B). Nilai ini setara dengan 33% dari total lalu lintas keseluruhan. - Baris 3: Jika kedua baris di atas terlewati (sisa lalu lintas), lompat langsung ke rantai
KUBE-SEP-POD-C(Pod C).
- Baris 1: Dengan probabilitas 0.33 (33%), lompat ke rantai
- KUBE-SEP-POD-A (Destination NAT): Rantai endpoint ini mengeksekusi perintah DNAT (Destination Network Address Translation) untuk mengganti alamat IP tujuan paket dari IP virtual Service
10.96.45.12:80menjadi alamat IP asli Pod target, yaitu10.244.1.5:8080.
Keterbatasan Utama Mode iptables #
Meskipun mode iptables sangat matang dan stabil, ia memiliki kelemahan arsitektur yang serius saat dijalankan pada kluster berskala besar:
- Pencarian Sekuensial O(n): iptables mengevaluasi aturan secara linear dari atas ke bawah. Jika kluster kita memiliki 10.000 Service dengan masing-masing 5 Endpoint, kernel Linux harus mengevaluasi hingga puluhan ribu aturan untuk setiap paket data tunggal yang masuk. Hal ini memicu lonjakan latensi jaringan kluster dan penggunaan CPU node worker.
- Update Non-Atomik: Ketika salah satu Pod backend mati atau berganti IP, kube-proxy harus melakukan flush (pembersihan) seluruh tabel iptables node worker dan menulis ulang seluruh aturan dari awal. Proses non-atomik ini dapat memicu terjadinya packet drop sesaat (latency spikes) di kluster yang sangat sibuk.
Penyelaman Mode IPVS (IP Virtual Server) #
Untuk mengatasi keterbatasan performa mode iptables di kluster besar, Kubernetes menyediakan mode alternatif yang bernama IPVS (IP Virtual Server). IPVS adalah bagian dari Linux Virtual Server (LVS) yang tertanam di dalam kernel Linux sejak lama dan dirancang khusus untuk menangani load balancing berkinerja tinggi.
IPVS menggunakan struktur data Hash Table untuk menyimpan aturan routing Service, bukan daftar sekuensial linear.
Mari kita perhatikan perbandingan kompleksitas pencarian antara mode iptables dan IPVS melalui diagram berikut:
flowchart TD
subgraph iptablesMode["Mode iptables (Linear Lookup - O(n) Complexity)"]
Packets1["Paket Masuk"] --> Rule1{"Rule 1?"}
Rule1 -- "No" --> Rule2{"Rule 2?"}
Rule2 -- "No" --> RuleN{"Rule N?"}
RuleN -- "Yes" --> Dest1["Redirect to Pod"]
end
subgraph ipvsMode["Mode IPVS (Hash Table Lookup - O(1) Complexity)"]
Packets2["Paket Masuk"] --> HashKey["Hash Key: ClusterIP:Port"]
HashKey --> HashTable{{"Hash Table Lookup"}}
HashTable --> Dest2["Redirect to Pod (Instant)"]
end
Keunggulan Teknis IPVS #
- Kompleksitas O(1) Konstan: Berapapun jumlah Service yang kita miliki di kluster (baik 100 maupun 100.000 Service), IPVS hanya membutuhkan satu kali pencarian hash instan untuk menemukan Pod tujuan. Latensi kueri jaringan tetap konstan dan sangat rendah.
- Pembaruan Atomik: Pembaruan tabel routing IPVS dilakukan secara incremental (hanya mengubah baris IP Pod yang berubah saja) dan bersifat atomik. Tidak ada pembersihan tabel keseluruhan, sehingga meminimalkan risiko packet drop saat rolling update.
- Pilihan Algoritma Load Balancing: Berbeda dengan iptables yang hanya mendukung pemilihan acak probabilistik, IPVS menyediakan berbagai pilihan algoritma load balancing yang canggih:
rr(Round-Robin): Membagi lalu lintas bergantian secara berurutan.lc(Least Connection): Mengirimkan lalu lintas baru ke Pod yang saat ini memiliki jumlah koneksi aktif paling sedikit (sangat direkomendasikan untuk database/long-lived connections).sh(Source Hashing): Memetakan lalu lintas berdasarkan hash IP client (untuk sticky sessions).dh(Destination Hashing): Memetakan berdasarkan IP tujuan.
Cara Mengaktifkan Mode IPVS #
Kita harus memperbarui berkas konfigurasi kube-proxy ConfigMap di namespace kube-system:
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-proxy
namespace: kube-system
data:
config.conf: |-
mode: "ipvs" # Mengaktifkan mode IPVS
ipvs:
scheduler: "lc" # Menggunakan algoritma Least Connection
[!WARNING] Sebelum mengaktifkan mode IPVS di kluster mandiri (self-managed), kita wajib memastikan bahwa modul-modul kernel Linux yang dibutuhkan oleh IPVS telah di-load secara otomatis pada sistem operasi host node worker kita. Modul-modul tersebut adalah:
ip_vs,ip_vs_rr,ip_vs_wrr,ip_vs_sh, dannf_conntrack.
Kebijakan Lalu Lintas Eksternal (External Traffic Policy) #
Untuk Service yang menerima lalu lintas dari luar kluster (tipe NodePort atau LoadBalancer), Kubernetes menyediakan properti .spec.externalTrafficPolicy untuk mengatur bagaimana lalu lintas eksternal tersebut diproses melintasi node worker kluster.
spec:
type: LoadBalancer
externalTrafficPolicy: Cluster # Pilihan default
# atau:
# externalTrafficPolicy: Local
1. externalTrafficPolicy: Cluster (Default)
#
Ketika paket data dari luar mendarat di Node A, tetapi Kubernetes scheduler menempatkan Pod aplikasi kita di Node B:
- Kube-proxy di Node A akan melakukan proses Source NAT (SNAT) untuk meneruskan paket melintasi jaringan internal kluster ke Node B.
- Kelebihan: Distribusi lalu lintas data sangat merata ke seluruh Pod aplikasi di kluster.
- Kekurangan: Kita kehilangan alamat IP asli client internet luar karena alamat IP tersebut tertimpa oleh alamat IP internal Node A saat proses SNAT. Selain itu, ada latensi tambahan karena paket harus melompat antar node worker (network hop).
2. externalTrafficPolicy: Local
#
Ketika paket data dari luar mendarat di Node A:
-
Kube-proxy di Node A hanya akan meneruskan paket tersebut ke Pod yang berjalan di dalam Node A saja.
-
Jika di Node A tidak ada Pod aplikasi kita, paket data akan dibuang (dropped).
-
Kelebihan: Alamat IP asli client internet luar tetap terjaga secara utuh (Source IP preservation) karena tidak ada proses SNAT lintas node. Tidak ada pula tambahan lompatan jaringan lintas node.
-
Kekurangan: Distribusi lalu lintas data dapat menjadi tidak merata jika jumlah replika Pod kita di setiap node worker tidak seimbang.
-
Catatan: Untuk LoadBalancer Service cloud, cloud provider secara otomatis akan melakukan health check ke port NodePort khusus di setiap node worker. Node worker yang tidak memiliki Pod aktif akan merespon gagal pada health check, sehingga cloud load balancer tidak akan mengirimkan lalu lintas ke node tersebut.
Anti-Pattern vs Solusi dalam Load Balancing Kluster #
Mari kita pelajari beberapa kesalahan fatal terkait konfigurasi kube-proxy dan load balancing beserta perbandingan kodenya.
Anti-Pattern 1: Membiarkan Mode iptables Aktif pada Kluster Skala Besar #
Kita mengoperasikan kluster Kubernetes produksi yang meng-host ribuan microservices dan memiliki puluhan ribu objek Service aktif, namun kita membiarkan kube-proxy berjalan menggunakan mode default iptables.
Kode Manifest Salah (Mode iptables Bawaan) #
# JANGAN GUNAKAN INI PADA CLUSTER BESAR: Performa I/O akan memburuk
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-proxy
namespace: kube-system
data:
config.conf: |-
mode: "iptables" # Traversal linear O(n) yang lambat untuk ribuan Service
Konsekuensi Buruk #
Setiap kali Pod aplikasi mengirim request ke Service lain, kernel node worker membutuhkan waktu CPU yang signifikan untuk menelusuri puluhan ribu baris aturan iptables secara berurutan. Latensi request HTTP internal kluster akan membengkak, dan utilisasi CPU node worker akan habis terkuras hanya untuk memproses aturan firewall jaringan.
Kode Solusi (Migrasi ke Mode IPVS dengan Algoritma Least Connection) #
# SOLUSI: Aktifkan IPVS untuk performa lookup O(1) yang konstan
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-proxy
namespace: kube-system
data:
config.conf: |-
mode: "ipvs" # Beralih ke hash table routing
ipvs:
scheduler: "lc" # Menggunakan Least Connection untuk optimalisasi beban kerja database
Anti-Pattern 2: Menggunakan externalTrafficPolicy: Local Tanpa Distribusi Pod yang Merata
#
Kita menyalakan properti externalTrafficPolicy: Local pada Service LoadBalancer untuk mempertahankan IP client asli, tetapi kita membiarkan Pod kita hanya menumpuk di satu node worker saja akibat tidak adanya aturan afinitas.
Kode Manifest Salah (Service Local Tanpa Topology Constraints) #
# JANGAN LAKUKAN INI: Lalu lintas Load Balancer tidak akan seimbang
apiVersion: v1
kind: Service
metadata:
name: web-service-local
namespace: production
spec:
type: LoadBalancer
externalTrafficPolicy: Local # Paket hanya diproses di node yang memiliki Pod
selector:
app: web-server
ports:
- port: 80
targetPort: 80
Konsekuensi Buruk #
Jika kluster memiliki 3 node worker, dan seluruh 3 replika Pod aplikasi kita dijadwalkan oleh Kubernetes di Node 1 saja, maka cloud load balancer hanya akan mengirimkan lalu lintas data ke Node 1. Node 2 dan Node 3 tidak menerima lalu lintas sama sekali. Node 1 akan mengalami overload CPU, sementara resource Node 2 dan Node 3 terbuang sia-sia.
Kode Solusi (Menerapkan Topology Spread Constraints pada Pod) #
Kita wajib menambahkan konfigurasi topologySpreadConstraints atau podAntiAffinity pada manifes Deployment Pod kita untuk menjamin bahwa replika Pod kita disebar secara merata di seluruh node worker kluster.
# SOLUSI: Memaksa scheduler menyebarkan Pod ke seluruh node worker secara merata
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server-deployment
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: web-server
template:
metadata:
labels:
app: web-server
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule # Paksa scheduler untuk tidak menumpuk Pod di satu node
labelSelector:
matchLabels:
app: web-server
containers:
- name: web-container
image: nginx:alpine
Panduan Praktis Debugging kube-proxy & IPVS #
Jika lalu lintas Service tidak terdistribusi dengan benar, kita dapat melakukan verifikasi internal pada node worker.
1. Memeriksa Log DaemonSet kube-proxy #
Dapatkan daftar nama Pod kube-proxy yang berjalan di namespace kube-system:
kubectl get pods -n kube-system -l k8s-app=kube-proxy
Tinjau log salah satu Pod kube-proxy untuk memastikan ia berhasil memuat aturan jaringan tanpa adanya error:
kubectl logs -n kube-system pod/kube-proxy-abc123
Pastikan tidak ada pesan error seperti "Can't use ipvs mode, inserting iptables rules instead" yang menandakan modul kernel IPVS di host node worker belum dimuat.
2. Memeriksa Tabel IPVS Langsung di Node Worker #
Jika kita memiliki akses SSH ke node worker, kita dapat menginstal utility ipvsadm untuk melihat isi tabel pencarian hash IPVS secara nyata:
# Menginstal utilitas IPVS administration di Debian/Ubuntu
sudo apt-get install -y ipvsadm
# Melihat daftar aturan load balancing IPVS aktif
sudo ipvsadm -ln
Hasil output akan menampilkan pemetaan IP virtual Service (ClusterIP) ke IP asli Pod target secara instan:
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.96.45.12:80 lc
-> 10.244.1.5:8080 Masq 1 0 0 # Pod A
-> 10.244.2.3:8080 Masq 1 0 0 # Pod B
-> 10.244.1.9:8080 Masq 1 0 0 # Pod C
Tinjau apakah kolom RemoteAddress sudah sesuai dengan daftar alamat IP Pod yang sehat.
Ringkasan #
- kube-proxy mengelola aturan, bukan memproses paket: kube-proxy bertindak sebagai control agent yang menulis aturan routing di host node worker, sementara kernel Linux yang mengalihkan data.
- iptables menggunakan linear lookup O(n): Sangat matang, namun performanya menurun pada kluster skala besar karena mengevaluasi aturan jaringan secara sekuensial.
- IPVS menggunakan Hash Table O(1): Sangat disarankan untuk kluster produksi berskala besar karena performa pencarian rute konstan dan pembaruannya bersifat atomik.
- IPVS mendukung algoritma Least Connection (
lc): Mengirimkan lalu lintas baru ke Pod yang memiliki koneksi aktif paling sedikit; sangat ideal untuk beban kerja database yang berat.- externalTrafficPolicy: Local mempertahankan IP Client asli: Menghindari proses Source NAT (SNAT) lintas node worker agar aplikasi dapat memverifikasi IP asli client internet luar.
- Sebarkan Pod secara merata saat menggunakan policy Local: Selalu gunakan
topologySpreadConstraintspada Deployment Pod guna mencegah ketidakseimbangan beban kerja lintas node worker.- Verifikasi IPVS dengan
ipvsadm -ln: Gunakan utilitas administrasi IPVS langsung di dalam node worker untuk memeriksa kelancaran pemetaan IP virtual Service ke alamat IP kontainer asli.