Kubernetes kümenizde birkaç düzine servis ve birkaç yüz pod varken varsayılan ağ modeli olan kube-proxy (iptables modunda) gayet sessiz çalışır. Ancak mikroservis sayınız binleri aştığında, Kubernetes Service ve EndpointSlice nesneleri büyüdükçe kümede açıklanamayan ağ gecikmeleri, anlık CPU sıçramaları ve dakikalar süren servis güncelleme gecikmeleri başlar.
Bunun sebebi çok basittir: iptables hiçbir zaman bir yük dengeleyici (Load Balancer) olarak tasarlanmamıştır. Paket filtreleme amacıyla yazılan sıralı bir kural tablosudur.
Bugünkü yazımızda, kube-proxy‘nin mimari sınırlarını, eBPF (Extended Berkeley Packet Filter) tabanlı Cilium mimarisinin ağ paketlerini nasıl ışık hızına çıkardığını ve canlı bir kümede kube-proxy‘yi tamamen çöpe atıp Cilium Host-Reachable Services (Kube-Proxy Replacement) moduna nasıl geçeceğimizi inceliyoruz.
1. Matematiksel Darboğaz: $O(N)$ iptables vs. $O(1)$ BPF Maps
Kubernetes üzerinde 5.000 servis ve 20.000 pod’luk bir altyapınız olduğunu düşünelim:
A. iptables Zincir Felaketi ($O(N)$)
Bir paket pod’dan çıkıp bir ClusterIP servisine gitmek istediğinde, Linux çekirdeği paket eşleşene kadar binlerce satırlık KUBE-SERVICES, KUBE-SVC-* ve KUBE-SEP-* zincirlerini yukarıdan aşağıya sırayla (sequential) tarar:
- 10.000 kural varsa ve aradığınız kural listenin sonundaysa, her bir paket için 10.000 kontrol yapılır.
- Tek bir pod eklendiğinde veya silindiğinde,
kube-proxytek bir kuralı güncellemek yerine tüm iptables kurallarını çekirdek seviyesinde baştan yazar (atomic replace). Bu işlem büyük kümelerde çekirdek kilitlemelerine (xtables lock) ve CPU %100 takılmalarına yol açar.
B. Cilium ve eBPF Mimarisi ($O(1)$)
Cilium, çekirdek paket filtreleme zincirlerini (netfilter/iptables) tamamen atlar. Servis ve uç nokta (endpoint) bilgilerini Linux çekirdeğindeki verimli Hash tablolarında (BPF Maps) tutar:
- Servis sayınız 10 da olsa 50.000 de olsa, yönlendirme maliyeti sabittir (
$O(1)$
- karmaşıklık). Çekirdek, IP adresini doğrudan BPF Map içinde arar ve tek bir işlemde paketi hedef pod’a yönlendirir.
2. Mimari Karşılaştırma: iptables vs. IPVS vs. Cilium eBPF
| Kriter / Özellik | kube-proxy (iptables) | kube-proxy (IPVS) | Cilium eBPF (kube-proxy Free) |
| Arama Karmaşıklığı | $O(N)$ (Sıralı tarama) | $O(1)$ (Hash IP Set) | $O(1)$ (Doğrudan Çekirdek BPF Map) |
| Kural Güncelleme Süresi | Yavaş (Tüm tablo baştan yazılır) | Hızlı (IPSet delta güncelleme) | Mikrosaniye (Atomic Map Update) |
| Bağlantı Takip (conntrack) | Tamamen bağımlı (Kilitlenmeye açık) | Bağımlı (nf_conntrack) | eBPF Connection Tracking (Bypass) |
| Socket-Level Dengeleme (sockops) | Yok (Paket TCP/IP katmanını gezer) | Yok | Var (TCP Handshake soket seviyesinde bağlanır) |
| XDP (eXpress Data Path) Desteği | Desteklemez | Desteklemez | Var (Ağ kartı sürücüsünde paket karşılama) |
| Gözlemlenebilirlik (Observability) | Kısıtlı (tcpdump, iptables log) | Kısıtlı | Eşsiz (Hubble ile L3/L4/L7 canlı izleme) |
Cilium’un Gizli Silahı: Socket-Level Load Balancing (sockops)
Geleneksel mimaride aynı düğüm (node) üzerindeki iki pod birbiriyle konuşurken, paket sanal ağ kartından (veth) çıkar, Linux TCP/IP yığınını dolaşır, NAT işlemine girer ve diğer pod’un veth arayüzüne teslim edilir.
Cilium sockops kancalarıyla (hook), uygulamanın connect() sistem çağrısını henüz soket aşamasındayken yakalar. Eğer hedef pod aynı fiziksel sunucudaysa, çekirdeğin tüm TCP/IP ağ yığınını baypas ederek kaynak soket tamponunu (send buffer) doğrudan hedef soket tamponuna (receive buffer) bağlar! Bu sayede aynı node üzerindeki pod iletişimi neredeyse Unix Domain Socket hızına ve sıfır ağ gecikmesine ulaşır.
3. Canlı Kümede kube-proxy‘den Cilium eBPF’e Geçiş Adımları
Kurumsal bir Kubernetes kümesinde (kubeadm, RKE2 veya EKS Anywhere) kube-proxy‘yi tamamen emekli edip Cilium’u tek yetkili kılmak için aşağıdaki adımları izleyebilirsiniz.
Adım 1: Kubernetes API Server Bilgilerini Doğrulayınkube-proxy kaldırıldığında Cilium pod’ları ayağa kalkarken henüz DNS veya Service IP’leri çalışmıyor olacaktır. Bu nedenle Cilium’a Kubernetes API sunucusunun fiziksel IP’sini ve portunu açıkça vermeliyiz:
# API Server IP ve Portunu öğrenin
kubectl cluster-info
Adım 2: Cilium’u Helm ile Kube-Proxy Replacement Modunda Kurun
Cilium’u doğrudan kubeProxyReplacement=true bayrağıyla ve soket seviyesi optimizasyonlarla dağıtıyoruz:
helm repo add cilium https://helm.cilium.io/
helm repo update
helm upgrade --install cilium cilium/cilium --version 1.16.4 \
--namespace kube-system \
--set kubeProxyReplacement=true \
--set k8sServiceHost="10.100.10.10" \
--set k8sServicePort="6443" \
--set bpf.masquerade=true \
--set bpf.tproxy=true \
--set hostServices.enabled=true \
--set nodePort.enabled=true \
--set externalIPs.enabled=true \
--set hostPort.enabled=true \
--set socketLB.enabled=true \
--set socketLB.hostNamespaceOnly=false \
--set ipam.mode=kubernetes \
--set routingMode=native \
--set ipv4NativeRoutingCIDR="10.244.0.0/16" \
--set autoDirectNodeRoutes=true \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true
- routingMode=native & autoDirectNodeRoutes=true: Paketleri VXLAN/Geneve tünellerine (overlay encapsulation) sokmadan, doğrudan fiziksel 25GbE L2 ağ üzerinden yerel yönlendirmeyle (L3 Direct Routing) aktararak MTU kaybını ve CPU başlık işleme maliyetini sıfırlar.
- socketLB.hostNamespaceOnly=false: Soket seviyesinde yük dengelemeyi sadece host süreçleri için değil, tüm pod namespace’leri için aktif eder.
Adım 3: kube-proxy DaemonSet’ini Devre Dışı Bırakın ve Temizleyin
Cilium pod’larının sağlıklı çalıştığını (cilium status) teyit ettikten sonra artık eski kube-proxy‘yi silebiliriz:
# 1. kube-proxy DaemonSet'ini silin
kubectl -n kube-system delete daemonset kube-proxy
# 2. ConfigMap'ini temizleyin
kubectl -n kube-system delete configmap kube-proxy
# 3. Düğümler üzerinde birikmiş olan eski iptables kurallarını sıfırlayın (Tüm node'larda çalıştırılır):
sudo iptables-save | grep -v KUBE | sudo iptables-restore
4. Performansı ve eBPF Haritalarını Doğrulama
Cilium’un servis trafiğini gerçekten çekirdek eBPF tabloları üzerinden yönettiğini doğrulamak için Cilium CLI veya düğüm içindeki BPF araçlarını kullanabiliriz.
A. Servis Yük Dengeleme Tablosunu Kontrol Edin
# Cilium ajanı içinde aktif yük dengeleme kurallarını ve BPF Map durumunu listeleyin
kubectl -n kube-system exec -ti ds/cilium -c cilium-agent -- cilium bpf lb list
Çıktıda Kubernetes ClusterIP, NodePort ve LoadBalancer servislerinin doğrudan hedef Pod IP’leriyle eşleştiğini ve hiçbir iptables kuralına ihtiyaç duyulmadığını göreceksiniz.
B. Canlı Ağ Akışlarını ve Gecikmeyi İzleyin (Hubble CLI)
Geleneksel ağlarda hangi servisin DNS veya TCP gecikmesi yaşadığını görmek için pod içine girip tcpdump çalıştırmak gerekirdi. Cilium ile gelen Hubble, çekirdek seviyesindeki eBPF olaylarını dinleyerek L3’ten L7’ye (HTTP yanıt kodları ve milisaniye bazlı gecikmeler dahil) canlı teleometri sunar: