Çoğu Kubernetes cluster'ında farklı node'lardaki pod'lar arasındaki trafik şifrelenmiyor. Lab cluster'ımın bir node'unda paket yakaladım ve iki pod arasındaki HTTP isteklerini düz metin olarak okudum. Bunun bilinen cevabı service mesh, service mesh de genelde her pod'a fazladan bir proxy container demek. Bu yazıda sidecar'sız service mesh'in ne olduğuna, neyi çözdüğüne ve ne kazandırdığına bakıyorum; sonra Cilium'un mesh işlerini tek bir sidecar olmadan yaptığı lab'ı gösteriyorum: şifreleme, kimlik, HTTP görünürlüğü ve traffic split. Birkaç yerde patladı; gerçek mesajlarıyla sonda.
Sidecar'sız service mesh nedir?
Service mesh, cluster'daki servisler için dört işi uygulamaların yerine üstlenen bir katman:
- Şifreleme: servisler arası trafik ağda okunamaz.
- Kimlik: bir servis sadece tanıdığı servislerden gelen isteği kabul eder.
- Görünürlük: hangi servisin hangisini çağırdığını method, path ve status kodu ile görürsünüz.
- Trafik yönetimi: trafiği sürümler arasında bölmek, header'a göre yönlendirmek.
Klasik yol (Istio, Linkerd) her uygulama container'ının yanına bir proxy sidecar container'ı koyuyor. Bütün trafik ondan geçiyor.
Sidecar'sız mesh aynı işleri pod başına değil node başına bir kez yapıyor. Cilium'da şifreleme Linux kernel'inde WireGuard ile oluyor, HTTP seviyesindeki işler node başına tek bir Envoy'dan (cilium-envoy) geçiyor, kimlik de SPIRE üzerinden SPIFFE ile geliyor. Pod'lar olduğu gibi kalıyor.
Neyi çözüyor?
Düz metin trafik. Varsayılan olarak node'lar arası pod trafiğini hiçbir şey şifrelemiyor. Ağı görebilen herkes (ele geçirilmiş bir node, aynalanmış bir switch portu, bulut ağında bir dinleme noktası) okuyabiliyor. Lab'da tek bir node'daki tek bir yakalama GET / HTTP/1.1, Host header'ı ve 200 OK'i gösterdi.
Sidecar'ın maliyeti. Sidecar her pod'da bir container daha demek: her deploy'da enjekte ediliyor, her pod'da bellek ve CPU istiyor, mesh güncellenince yeniden başlatılması gerekiyor, her kubectl exec "hangi container?" diye soruyor. Birçok ekip için mesh'i hiç açmamalarının sebebi bu maliyet.
"Kim kimi çağırdı?" Mesh olmadan ağ sadece IP ve port görüyor. Bir şey 500 döndüğünde hangi çağıranın, hangi path'in, ne kadar sürdüğünü göremiyorsunuz.
Cilium'un sidecar'sız mesh'i ne kazandırıyor?
| Kazanım | Pratikte anlamı | Lab'da |
|---|---|---|
| Uygulamaya dokunmadan şifreleme | Node'lar arası WireGuard, kernel'de; pod'ların haberi yok | Yakalamada okunur HTTP satırı: önce 6, sonra 0 |
| Küçük bir anahtar | İki Helm değeri ve agent restart'ı | Üç node'da 31 sn |
| Servisler arası kimlik kontrolü | authentication.mode: required olan bir policy; agent'lar SPIFFE el sıkışması yapıyor |
Intruder pod'u zaman aşımına düştü; client ~1 sn'lik el sıkışmadan sonra 200 aldı |
| HTTP görünürlüğü | L7 kuralı trafiği node'daki Envoy'dan geçiriyor; Hubble method, path, status gösteriyor | İstek başına GET /headers → 200 2ms |
| Standart traffic split | GAMMA: parent'ı Service olan bir Gateway API HTTPRoute'u |
90/10'da 50 istek: 46 / 4 |
| Pod başına ek bileşen yok | Envoy ve SPIRE agent node başına çalışıyor | 0 sidecar |
Karar vermeden önce neyi almadığınızı da bilin:
| İhtiyaç | Cilium 1.20 |
|---|---|
| Retry, timeout, circuit breaking | Sadece ham Envoy konfigürasyonu ile (CiliumEnvoyConfig), az doğrulama |
| Pod içinde sertifika (uygulama seviyesinde mTLS) | Yok. Şifreleme node'da oluyor; pod hiç sertifika görmüyor |
| Cluster'lar arası kimlik | Yok. Mutual auth 1.20'de Cluster Mesh ile çalışmıyor |
| Uzun vadede mutual auth | 1.20'de deprecated, 1.21'de kaldırıldı. Upgrade notları yerine ztunnel transparent encryption'ı gösteriyor |
Mutual auth'u yine de gösteriyorum, çünkü policy modeli (authentication.mode: required) yerine gelen yapıda da karşınıza çıkacak.
Nasıl çalışıyor?

- Şifreleme: her Cilium agent bir WireGuard arayüzü (
cilium_wg0) açıyor ve public key'ini Kubernetes üzerinden paylaşıyor. Başka bir node'a giden pod trafiği bu arayüzden, şifreli olarak, UDP 51871 portunda gidiyor. - Kimlik: Cilium her pod grubuna label'larından türeyen sayısal bir kimlik veriyor zaten. Mutual auth açıkken operator bu kimlikleri SPIRE'a kaydediyor; iki kimlik arasındaki ilk paket geçmeden önce iki taraftaki agent'lar birbirinin SPIFFE sertifikasını kontrol ediyor.
- Görünürlük ve traffic split: bir L7 kuralı ya da bir
HTTPRoutetrafiği node'dakicilium-envoy'dan geçiriyor; Envoy her isteği Hubble'a bildiriyor, ağırlık ve header eşleşmelerini uyguluyor.
Lab: ne kurdum
Her şey bir MacBook'ta kind üzerinde (Colima, 4 CPU / 8 GB, WireGuard modüllü 6.8 kernel): Kubernetes 1.37, Cilium 1.20.2, Cilium chart'ından SPIRE 1.15.3, Gateway API v1.6.1. İki podinfo sürümü bir worker node'da; client ve intruder pod'ları diğerinde, böylece trafikleri node'lar arasından geçmek zorunda. Aynı node'daki pod'lar hiç WireGuard'dan geçmiyor. Lab'ın tamamı son koşuda yaklaşık 4 dakika sürdü.
Adım 1: Trafiği oku, sonra şifrele
Önce şifresiz yakalama. podinfo node'unda tcpdump'lı küçük bir hostNetwork pod'u, client üç istek gönderirken eth0'ı dinledi. 80 paketin içinde 6 okunur HTTP satırı vardı: istek satırı, Host ve User-Agent header'ları ve cevap status'u.

Sonra iki Helm değeri (encryption.enabled: true, encryption.type: wireguard) ve Cilium agent'larının restart'ı. 31 saniye sonra her agent, diğer her node için birer tane olmak üzere 2 peer ile WireGuard bildirdi.

Aynı yakalama bundan sonra: sadece 51871 portunda UDP paketleri, 80 pakette 0 okunur HTTP satırı. Uygulamalar değişmedi, Cilium agent'ları dışında hiçbir pod yeniden başlamadı.

Adım 2: Kimlik yoksa giriş yok
SPIRE, Cilium chart'ıyla geliyor. Server sağlıklıydı, üç node agent'ı da doğrulanmıştı ve operator podinfo'nun Cilium kimliğini bir SPIFFE ID olarak kaydetmişti.

Sonra podinfo'ya bir policy: client'ı kabul et ve kimlik doğrulaması iste. Policy'nin izin vermediği intruder zaman aşımına düştü. Client 200 aldı, ama ilk istek yaklaşık bir saniye sürdü: bu iki agent arasındaki el sıkışma. Hubble düşürülen SYN paketlerini Authentication required ile gösterdi, geçen akışlarda da auth_type: SPIRE vardı.

İki istek, iki kez birer saniye bekleme: podinfo v1 ve v2 farklı label'lar taşıyor, yani iki ayrı kimlik ve her biri kendi ilk el sıkışmasını ödüyor. Tek bir Deployment'ın on replikası tek kimlik, tek el sıkışma olurdu.
Adım 3: Sidecar olmadan HTTP'yi gör
9898 portunda tüm HTTP'ye izin veren bir L7 kuralı, o trafiği node'daki Envoy'dan geçirmeye yetiyor. Bundan sonra Hubble her isteği method, URL, status ve süre ile gösterdi.

Adım 4: GAMMA ile cluster içinde traffic split
GAMMA, Gateway API'nin cluster içi trafik için olan kısmı: parent'ı Gateway yerine bir Service olan HTTPRoute. 90 ve 10 ağırlıkla 50 isteğin 46'sı v1'e, 4'ü v2'ye gitti (önceki koşularda 44/6 ve 45/5). İkinci bir kuralla x-canary: true header'lı istekler v2'ye gitti.

Son kontrol

Nerede patladı: 3 gerçek hata
1. SPIRE server 1/2'de kaldı: Starting Server APIs'ten hemen sonra Stopping Server APIs
Chart varsayılanlarıyla SPIRE server hiç hazır olmadı, node agent'ları Init:Error'da bekledi. Log'da gerçek bir hata yoktu. Sebep: server uid 1000 ile çalışıyor, ama socket klasörü root olarak oluşturulan bir host path, bu yüzden socket'i bağlamak sessizce başarısız oluyor (cilium/cilium#40533). Lab'ın values dosyasındaki çözüm server'ı root olarak çalıştırmak (securityContext.runAsUser: 0); chart SPIRE 1.9'dan önce böyle davranıyordu.

2. Ayar açık diyor, agent'lar Encryption: Disabled diyor
Şifreleme açık helm upgrade STATUS: deployed döndü, Cilium ConfigMap'i de enable-wireguard: true diyordu. Üç agent da hâlâ Disabled diyordu. Helm sadece konfigürasyonu yeniden yazıyor; agent'lar onu açılırken okuyor. cilium DaemonSet'ini yeniden başlattıktan sonra (27 sn) üçü de WireGuard'daydı. Hiçbir şey uyarmıyor. Agent'lara bakmazsanız trafiğin şifreli olduğunu sanıyorsunuz, oysa değil.

3. failed to get certificate for local identity: auth testi çok erken koştu
İlk seferinde auth policy'sini SPIRE açılır açılmaz uyguladım, client da zaman aşımına düştü. Operator kimlikleri SPIRE'a kendi takvimiyle kaydediyor ve o anda podinfo'nun kaydı henüz yoktu (Found 0 entries). Üç dakika sonra aynı test geçti. Policy'yi suçlamadan önce SPIRE kaydını kontrol edin.
Bir pratik not: kind node image'ında tcpdump yok. Lab yakalamayı node'a sabitlenmiş, hostNetwork'lü bir nicolaka/netshoot pod'undan yapıyor; o pod aynı eth0'ı görüyor.
Ne zaman kullanılır, ne zaman kullanılmaz
| Durum | Tercih |
|---|---|
| Cilium zaten network plugin'iniz; node'lar arası şifreleme, HTTP görünürlüğü ve basit split istiyorsunuz | Cilium'un sidecar'sız mesh'i. Pod başına yeni bileşen yok. |
| Retry, timeout, fault injection ya da outlier detection her gün lazım | Istio ambient (o da sidecar'sız: node başına ztunnel, L7 için waypoint proxy). |
| Regülasyon uygulamanın içinde sertifika istiyor (uçtan uca mTLS) | Uygulama seviyesinde mTLS olan bir mesh; Cilium node'da şifreliyor. |
| Uzun vadede servis kimliği lazım | ztunnel'i planlayın; Cilium'un SPIFFE mutual auth'u 1.20'de deprecated, 1.21'de kaldırıldı. |
Özet
- Sidecar'sız service mesh şifreleme, kimlik, görünürlük ve trafik yönetimini pod başına değil node başına bir kez yapıyor.
- Cilium ile WireGuard ağdaki okunur HTTP'yi 31 saniyede, uygulamalara dokunmadan 6 satırdan 0'a indirdi.
- SPIFFE mutual auth izni olmayan bir pod'u engelledi; bedeli kimlik başına ilk istekte yaklaşık bir saniye, ve 1.20'de deprecated.
- Bir L7 kuralı Hubble'da HTTP görünürlüğü verdi, GAMMA cluster içi trafiği 90/10'da 46/4 böldü.
helm upgrade'den sonra agent'ları mutlaka kontrol edin: ayar açık görünürken şifreleme kapalı olabilir.
Kaynaklar: Cilium dokümanı Transparent Encryption (WireGuard), Mutual Authentication (Beta), Layer 7 Protocol Visibility ve Gateway API GAMMA support; Cilium 1.20 upgrade notları; cilium/cilium#40533 ve #47132; Gateway API GAMMA girişimi.
Seride sıradaki
Bu, Cilium Uçtan Uca serisinin beşinci yazısı. Sonraki: Cilium Cluster Mesh: kind'da İki Cluster, Global Service ve Cluster'lar Arası Policy. Önceki yazı: Cilium Gateway API Nedir? Neyi Çözer, Kazanımları ve Ayrı Ingress Controller Olmadan Bir Lab.
Bu lab'ın kodu (kind yapılandırması, SPIRE düzeltmeli mesh ve şifreleme values'ları, podinfo/client/intruder manifest'i, iki policy, iki GAMMA route'u, yakalama ve auth script'leri ve Makefile) Buy Me a Coffee'deki Lab Access üyeleriyle paylaşılıyor; üye olduktan sonra repo daveti e-postayla geliyor: https://buymeacoffee.com/miraccanyilmaz/membership