Kubernetes'te giden trafik ve proxy: pod env, sidecar, egress ağ geçidi ve NO_PROXY'nin rolü
Makale içeriği
- Giriş: "normal sunucudaki gibi" yapılandırma kümede neden çalışmaz
- Temeller: pod'un giden trafiği fiziksel olarak nasıl işler
- Proxy'nin konulabileceği üç düzey: konteyner, sidecar, egress ağ geçidi
- Manifestoda ortam değişkenleri: http_proxy, https_proxy, no_proxy ve doğru no_proxy olmadan küme neden bozulur
- Bu değişkenlerin almadığı yerler: kubelet, kendi yapılandırması olan imajlar, sdk'lar
- Sırlar: proxy kimlik bilgileri manifestoya değil secret'a
- Sidecar yaklaşımı: ne zaman haklıdır ve proxy'nin yanı sıra ne sağlar
- Küme egress ağ geçidi: tek çıkış noktası
- Kümede teşhis: pod'dan giden ip nasıl kontrol edilir ve arızalar nereye bakılır
- Tipik hatalar
- Araçlar ve kaynaklar
- Vakalar ve sonuçlar
- Devreye alma kontrol listesi
- Sss
- Sonuç
Giriş: "Normal sunucudaki gibi" yapılandırma kümede neden çalışmaz
Sıradan bir sanal makinede proxy kurulumu beş dakika sürer. Profilinize HTTP_PROXY yazarsınız, birkaç adresi istisnalara eklersiniz, servisi yeniden başlatırsınız ve kahvenizi içmeye gidersiniz. Kubernetes'te bu senaryo farklı biter: servislerin yarısı aniden 502 döner, health-check pod'ları bozmaya başlar ve ödeme servisi yanındaki pod'a ulaşamadığından şikayet eder. Üstelik uğruna her şeyi kurduğunuz dış istekler bir şekilde çalışmaya devam eder.
Neden böyle? Çünkü kümede tek bir süreç aynı anda iki ağ dünyasında yaşar. Birincisi dış internet: partner API'leri, pazar yerleri, kazıma veri kaynakları, genel servisler. İkincisi kümenin iç ağı: ClusterIP üzerinden diğer servisler, Kubernetes API sunucusu, DNS, veritabanları, kuyruklar, metrikler. Sıradan bir sunucuda ikinci dünya neredeyse boştur, bu yüzden global proxy değişkeni hiçbir şeyi bozmaz. Kümede ise ikinci dünya devasadır ve payments.default.svc.cluster.local gibi bir adrese gelen isteği alan herhangi bir proxy bunu çözemez. Proxy dışarıda durur ve sizin iç DNS'inizden hiç haberi yoktur.
Buna Kubernetes'in kendine özgü yönlerini de ekleyelim. Pod'un ortam değişkenleri kubelet'i ve container runtime'ı etkilemez. İmajlar çoğu zaman ortamı yok sayan kendi yapılandırmalarını getirir. Farklı dillerin SDK'ları proxy istisnasının ne demek olduğunu farklı anlar. Ve tüm bunlar, pod'un giden IP'sinin zaten dış dünyaya hiç görünmediği gerçeğinin gölgesinde gerçekleşir: dışarıya düğümün adresi veya bulutun NAT ağ geçidi çıkar.
Bu kılavuzda Kubernetes'in giden trafiğini "katman katman" inceleyeceğiz: paket pod'dan fiziksel olarak nasıl çıkar, proxy tam olarak nereye sıkıştırılabilir, NO_PROXY kümede neden bir formaliteden kritik mimari unsuruna dönüşür, kimlik bilgileri güvenle nasıl aktarılır, sidecar ne zaman haklıdır ne zaman gereksizdir ve arızalar tahmin yürütmeden nasıl hata ayıklanır. Ortam değişkenlerinin temel sözdizimini tekrar anlatmayacağız — burada yalnızca küme özellikleri var. Tüm manifesto parçaları çalışır durumda, temel olarak alabilirsiniz.
Temeller: pod'un giden trafiği fiziksel olarak nasıl işler
Proxy kurmadan önce, paketin onsuz hangi yoldan gittiğini anlamak faydalıdır. Aksi halde teşhis tahmin oyununa dönüşür.
Konteynerden düğüme
Her pod, pod CIDR'inden kendi IP'sini alır, örneğin 10.244.3.17. Pod içinde tüm konteynerler aynı ağ ad alanını paylaşır: tek IP, tek arayüz kümesi, ortak localhost. Bu, geri döneceğimiz sidecar yaklaşımı için kilit bir gerçektir. Konteynerden çıkan paket, pod'un sanal arayüzü üzerinden düğüme ulaşır ve orada CNI eklentisi onunla ilgilenir. Sonrasında iki senaryo vardır.
- Hedef kümenin içinde. Uygulama bir servisin ClusterIP'sine (örneğin 10.96.14.2) başvurursa, kube-proxy veya CNI'nin eBPF veri düzlemi hedef adresi belirli bir backend pod'unun IP'siyle değiştirir. Trafik overlay üzerinden veya doğrudan L3 üzerinden ilgili düğüme gider ve kümeyi asla terk etmez.
- Hedef dışarıda. Adres ne pod CIDR'ine ne de service CIDR'ine aitse, paket düğümün varsayılan rotası üzerinden çıkar. Düğümden çıkışta neredeyse her zaman SNAT devreye girer: kaynak adresi 10.244.3.17, düğümün IP'siyle değiştirilir. Bulutlarda bunun üzerinde bir NAT ağ geçidi çalışabilir ve o zaman dış dünya zaten onun adresini görür.
Buradan ilk pratik çıkarım: dış servis pod'un IP'sini asla görmez. Düğümün IP'sini veya bulut NAT'ını görür. Düğüm otomatik ölçeklendirmeniz varsa, giden IP'ler onlarla birlikte değişir. Bu yüzden kümede ayrılmış bir egress düğümü olmadan proxy'de IP adresine göre yetkilendirme kötü çalışır — buna aşağıda değineceğiz.
Küme içinde DNS
Pod'lar küme DNS'ini (genellikle CoreDNS) kullanır. Pod'un resolv.conf dosyasında arama alanları tanımlıdır: namespace.svc.cluster.local, svc.cluster.local, cluster.local. Bu sayede payments adı payments.default.svc.cluster.local olarak çözülür. Dış proxy'nin böyle bir şemadan haberi yoktur. payments host'una bir istek alır, bunu genel DNS üzerinden çözmeye çalışır ve hata döner. Bu, NO_PROXY'nin kümede neden tek başına duran herhangi bir sunucudan daha önemli olduğunun temel nedenidir.
Kümede "proxy kurmak" aslında ne demek
Mühendisler bu ifadenin altında üç farklı şeyi anlar ve bunları karıştırmak tehlikelidir:
- Belirli bir uygulamayı, diğer servislere dokunmadan, bir proxy sunucusu üzerinden (örneğin Proxeon'un ayrılmış adresleri üzerinden) dışarı çıkarmak.
- Tüm küme veya bir parçası için kararlı ve öngörülebilir bir giden IP sağlamak — kurumsal ağ, uyumluluk, denetim görevi.
- Dış trafiğin yönetimini merkezileştirmek: günlükleme, limitler, tek çıkış noktası.
İlk görev pod düzeyinde çözülür. İkinci ve üçüncü küme düzeyinde. Düzeyleri karıştırmak, konuşacağımız sorunların çoğunu doğurur.
Proxy'nin konulabileceği üç düzey: konteyner, sidecar, egress ağ geçidi
Kümeyi bir ofis binası gibi düşünün. Proxy'yi her çalışana şahsen verebilirsiniz (konteyner), her çalışma masasının yanına bir yardımcı oturtabilirsiniz (sidecar) veya binanın çıkışına tek bir turnike koyabilirsiniz (egress ağ geçidi). Her şemanın bir bedeli var.
Düzey 1: konteynerin kendisi
Uygulama proxy adresini ortam değişkenleri, yapılandırma dosyası veya başlatma bayrakları üzerinden alır ve trafiği kendi yönlendirir. En basit ve en yaygın yöntem budur.
Artıları:
- Sıfır ek altyapı: değişiklikler yalnızca Deployment manifestosunda.
- Granülerlik: bir servise proxy gerekir, komşusuna gerekmez.
- Proxy protokolü "anlar": HTTP proxy hedef host'u görür, günlükleyebilir, kurallar uygulayabilir.
Eksileri:
- Yalnızca kod veya kütüphane ayarlara saygı duyuyorsa çalışır. Java, bazı Node.js istemcileri, bazı gRPC uygulamaları ayrı yapılandırma gerektirir.
- Doğru NO_PROXY'ye tam bağımlılık: tek bir hata küme içi iletişimi bozar.
- Proxy kimlik bilgileri süreç ortamına düşer ve konteynerde exec çalıştırabilen herkes erişebilir.
- Proxy rotasyonu veya adres değişikliği pod'ların yeniden oluşturulmasını gerektirir.
Düzey 2: sidecar konteyner
Aynı pod'da uygulamanın yanında ikinci bir konteyner çalışır — yerel bir forward proxy. Uygulama trafiği localhost:3128'e gönderir (veya iptables kuralları onu yakalar) ve sidecar nereye nasıl göndereceğine karar verir: hangi upstream proxy üzerinden, hangi yetkilendirmeyle, hangi zaman aşımıyla.
Artıları:
- Uygulama dış kimlik bilgilerini bilmez: yalnızca sidecar'da yaşarlar.
- Tek mantık noktası: rotasyon, tekrarlar, adres havuzu seçimi, metrikler — uygulama kodu değiştirilmeden.
- iptables üzerinden şeffaf mod, HTTP_PROXY'yi yok sayan kütüphanelerle bile çalışır.
- Proxy'leme mantığını güncellemek tek bir sidecar imajını güncellemektir.
Eksileri:
- Her pod'da ek konteyner: bellek, CPU, izlenecek bir nesne daha.
- Şeffaf yakalama, init konteynerinde NET_ADMIN gerektirir ve bu her yerde güvenlik politikaları tarafından izin verilmez.
- Hata ayıklaması daha zor: bozulabilecek iki yer.
Düzey 3: küme egress ağ geçidi
Ayrılmış bir pod (veya düğüm) kümesi tek çıkış noktası olur. Diğer pod'lar ya açıkça ağ geçidinin iç Service'i üzerinden gider ya da ağ politikaları ve yönlendirme ile buna zorlanır. Ağ geçidi dışarıya doğrudan ya da harici bir proxy üzerinden çıkar.
Artıları:
- Tüm küme için tek kararlı giden IP (veya küçük bir küme). Proxy sağlayıcı tarafında IP'ye göre yetkilendirme ve partner beyaz listeleri için ideal.
- Merkezi günlükler, limitler, denetim.
- Uygulamalar hiçbir kimlik bilgisi saklamaz: yalnızca ağ geçidinin iç adresini görürler.
- Dış adreslerin rotasyonu tek yerde olur.
Eksileri:
- Tek arıza noktası: ağ geçidi yüksek erişilebilir olmalı (birden fazla replika, PodDisruptionBudget, anti-affinity).
- Ek bir ağ atlaması ve potansiyel verim darboğazı.
- İyi düşünülmüş NetworkPolicy gerektirir, aksi halde ağ geçidini atlayan doğrudan bir "delik" kalır.
- Granülerlik daha zor: farklı ekiplerin farklı dış adreslere ihtiyacı varsa birden fazla ağ geçidi kurmak gerekir.
Nasıl seçilir
Tasarımda kullandığımız pratik kural:
- Bir-iki uygulama, kontrol edilebilir kod, standart HTTP istemcileri — konteyner düzeyi.
- Farklı yığınlara sahip çok sayıda servis, rotasyon ve tekrar gereksinimleri, kimlik bilgilerini dağıtma isteksizliği — sidecar.
- Uyumluluk gereksinimleri, IP'ye göre yetkilendirme, tüm dış trafiğin denetimi, onlarca ekip — egress ağ geçidi, sıklıkla ince mantık için sidecar ile birlikte.
Dikkat edin: düzeyler birbirini dışlamaz. Tipik olgun bir şema, zorunlu sınır olarak egress ağ geçidi artı özel durum gerektiğinde ortam değişkenleri veya sidecar'dır.
Manifestoda ortam değişkenleri: HTTP_PROXY, HTTPS_PROXY, NO_PROXY ve doğru NO_PROXY olmadan küme neden bozulur
Değişkenlerin sözdizimini zaten biliyorsunuz. Bizi ilgilendiren, pod'da onlara tam olarak ne olduğu ve standart NO_PROXY değeri "localhost,127.0.0.1"in burada neden feci şekilde yetersiz olduğudur.
Bir arızanın anatomisi
Zinciri inceleyelim. orders servisi payments'ı http://payments:8080/charge adresinden çağırır. Ortamda HTTP_PROXY tanımlıdır. HTTP istemcisi hedef host'a bakar, NO_PROXY'de bulamaz ve payments ile TCP bağlantısı kurmak yerine proxy ile bağlantı açar ve ona GET http://payments:8080/charge isteğini gönderir. Proxy, payments adını kendi resolver'ı üzerinden çözmeye çalışır. Böyle bir ad yok. Proxy 502 veya 503 döner. Servisiniz proxy'den hata alır ama günlüklerde bu payments'tan hata olarak görünür. Ekip tamamen sağlıklı olan payments'ı düzeltmeye gider.
Daha sinsi ikinci bir varyant da vardır. Servis ClusterIP'ye doğrudan 10.96.14.2 gibi bir adresle başvurursa, proxy özel adrese bir istek alır. Bazı proxy'ler bu tür istekleri anında reddeder, diğerleri kendi ağında 10.96.14.2'ye bağlanmaya çalışır. En kötü durumda orada o adrese sahip başka bir host vardır.
Üçüncü varyant Kubernetes'in kendisiyle ilgilidir. API sunucusuyla konuşan istemci kütüphaneleri (operator'lar, denetleyiciler, leader election'lı uygulamalar) kubernetes.default.svc adresini ya da KUBERNETES_SERVICE_HOST değişkenindeki IP'yi kullanır. İstisna olmadan bunlar da proxy üzerinden gider ve control plane ile bağlantıyı kaybeder.
Bir pod'un NO_PROXY'sinde olması gerekenler
Başlangıç noktası olarak önerdiğimiz minimum küme. Kendi CIDR'lerinizi koyun — bunları küme yapılandırmasından veya servis ek açıklamalarından öğrenebilirsiniz:
- localhost, 127.0.0.1, ::1 — sidecar ile bağlantı dahil yerel adresler.
- .svc, .svc.cluster.local, .cluster.local — tüm servis adları. Baştaki nokta "herhangi bir alt alan adı" anlamına gelir.
- kubernetes.default.svc, kubernetes.default.svc.cluster.local — API istemcileri için açıkça.
- Service CIDR, örneğin 10.96.0.0/12 — ClusterIP'ye doğrudan başvurular.
- Pod CIDR, örneğin 10.244.0.0/16 — headless servisler ve pod-to-pod başvurular.
- Node CIDR — uygulama düğüm portlarına (metrikler, hostNetwork servisleri) gidiyorsa.
- 169.254.169.254 — kullanılıyorsa bulut meta verileri.
- Kurumsal iç alan adları, örneğin .corp.local, kümeden doğrudan rota varsa.
- İç veritabanları ve kuyrukların adresleri, küme dışında ama özel ağdaysa.
Herkesin bilmediği tuzak: CIDR'yi her istemci desteklemez
İşte gerçek küme özelliği burada başlar. NO_PROXY biçimi standartlaştırılmamıştır. Her kütüphane onu kendi yöntemiyle ayrıştırır:
- Go (net/http) CIDR notasyonunu, baştaki noktalı sonekleri ve tek tek host'ları anlar. En iyi durum budur ve Kubernetes araçlarının çoğu Go ile yazılmıştır.
- curl CIDR'yi yalnızca 7.86 sürümünden itibaren destekler. Eski dağıtımlara dayalı imajlarda 10.96.0.0/12 kaydı basitçe yok sayılır.
- Python (urllib, requests) CIDR'yi hiç anlamaz. Karşılaştırma dize sonekine göre yapılır. 10.96.0.0/12 kaydı asla işe yaramaz; 10.96. kaydı ise yalnızca bu dizeyle biten host'lar için sonek benzeri eşleşme olarak çalışır, yani o da yardımcı olmaz.
- Node.js yerleşik HTTP istemcisi ortam değişkenlerini tamamen yok sayar. undici'de ProxyAgent veya üçüncü taraf bir agent gerekir, NO_PROXY'nin nasıl yorumlanacağına da o karar verir.
- Java *.svc.cluster.local gibi wildcard sözdizimi ve dikey çizgi ayırıcıyla kendi http.nonProxyHosts değişkenini kullanır.
Pratik çıkarım: kümede dil hayvanat bahçesi varsa, CIDR'ye göre iç trafiği hariç tutmanın tek güvenilir yolu istemcinin NO_PROXY'sine güvenmemek, bunu ayrıştırıcısı sizin kontrolünüzde olan sidecar veya ağ geçidi düzeyinde çözmektir. DNS adları için nokta içeren sonekler neredeyse her yerde çalışır, bu yüzden uygulamalarda servislere ClusterIP yerine adlarla gitmek gerekir.
Değişkenlerin büyük-küçük harf durumu
Düzenli olarak saatlerce hata ayıklamaya neden olan bir başka nokta. curl http_proxy'yi yalnızca küçük harfle okur: büyük harfli HTTP_PROXY, CGI ile ilgili tarihsel bir güvenlik açığı nedeniyle kasıtlı olarak yok sayılır. Ancak curl, HTTPS_PROXY ve NO_PROXY'yi her iki harf durumunda da kabul eder. Go, Python ve .NET her iki varyantı da okur. Bu yüzden manifestoda her zaman iki biçimi de tanımlarız — ucuzdur ve bütün bir sorun sınıfını ortadan kaldırır.
Çalışan parça: NO_PROXY'li ConfigMap ve Deployment
NO_PROXY'yi namespace düzeyinde bir ConfigMap'e taşıyın. Böylece listeyi yirmi Deployment yerine tek bir yerde değiştirirsiniz:
apiVersion: v1
kind: ConfigMap
metadata:
name: egress-proxy-common
namespace: crawlers
data:
NO_PROXY: "localhost,127.0.0.1,::1,.svc,.svc.cluster.local,.cluster.local,kubernetes.default.svc,10.96.0.0/12,10.244.0.0/16,192.168.10.0/24,169.254.169.254,.corp.local"
no_proxy: "localhost,127.0.0.1,::1,.svc,.svc.cluster.local,.cluster.local,kubernetes.default.svc,10.96.0.0/12,10.244.0.0/16,192.168.10.0/24,169.254.169.254,.corp.local"Ve onu kullanan Deployment. Proxy adresi burada henüz kimlik bilgileri olmadan — kimlik bilgilerinin nasıl saklanacağını ayrı bir bölümde ele alacağız:
apiVersion: apps/v1
kind: Deployment
metadata:
name: price-collector
namespace: crawlers
spec:
replicas: 3
selector:
matchLabels:
app: price-collector
template:
metadata:
labels:
app: price-collector
spec:
containers:
- name: app
image: registry.internal/price-collector:2.7.0
envFrom:
- configMapRef:
name: egress-proxy-common
env:
- name: HTTP_PROXY
value: "http://proxy-host.example:8080"
- name: http_proxy
value: "http://proxy-host.example:8080"
- name: HTTPS_PROXY
value: "http://proxy-host.example:8080"
- name: https_proxy
value: "http://proxy-host.example:8080"
readinessProbe:
httpGet:
path: /healthz
port: 8080HTTPS_PROXY'deki şemaya dikkat edin: çoğu zaman bu http://'dir, çünkü istemci proxy'nin kendisine normal TCP üzerinden bağlanır ve HTTPS istekleri için CONNECT tünelini içeride kurar. Buradaki https:// değeri, proxy'nin kendisine kadar TLS anlamına gelir ve bunu ne tüm istemciler ne de tüm proxy sunucuları destekler. Proxeon panelinde tarifenizin hangi bağlantı türünü sağladığını doğrulayın.
Probe'un proxy üzerinden gitmediğinden emin olun
Liveness ve readiness probe'ları uygulama değil kubelet yürütür, bu yüzden ortam değişkenlerini görmezler ve proxy'leme onlara zarar vermez. Ancak uygulamanız /healthz işleyicisinin içinde bağımlılıkları iç adresler üzerinden kendisi çağırıyorsa, bu çağrılar HTTP_PROXY'ye takılır. Klasik "proxy ekledikten sonra pod'lar readiness'ı geçemez oldu" semptomu tam olarak böyle görünür.
Bu değişkenlerin almadığı yerler: kubelet, kendi yapılandırması olan imajlar, SDK'lar
Pod'un ortam değişkenleri uygulama sürecinin içinde yaşar. Sürecin dışındaki veya ortamı kasıtlı olarak yok sayan her şey ayrı yapılandırma gerektirir. Üç grubu inceleyelim.
Kubelet ve container runtime
Bir kümeyi proxy arkasına taşırken en sık sorulan soru: "İmajlar neden çekilmiyor, oysa Deployment'a HTTP_PROXY yazdık?" Cevap: imajları pod değil, düğümdeki container runtime (containerd veya CRI-O) kubelet'in komutuyla indirir. Bunlar systemd üzerinden çalışan sistem servisleridir ve yalnızca kendi ortamlarını okurlar.
Yapılandırma, her düğümde systemd birimi için drop-in dosyasıyla yapılır. containerd için bu şöyle görünür (/etc/systemd/system/containerd.service.d/http-proxy.conf dosyası):
[Service]
Environment="HTTP_PROXY=http://proxy-host.example:8080"
Environment="HTTPS_PROXY=http://proxy-host.example:8080"
Environment="NO_PROXY=localhost,127.0.0.1,10.96.0.0/12,10.244.0.0/16,192.168.10.0/24,.svc,.cluster.local,registry.internal"Sonrasında — systemctl daemon-reload ve systemctl restart containerd. NO_PROXY'de iç registry mutlaka bulunmalıdır, aksi halde runtime kendi imajlarınızı almak için dış proxy üzerinden gider. Kubelet için ayrı drop-in daha seyrek gerekir — yalnızca kendisi bulut API'lerine veya dış webhook'lara başvuruyorsa.
Yönetilen kümelerde düğümlere erişim olmayabilir, bu durumda runtime yapılandırması bulut sağlayıcının mekanizmalarıyla veya konakta yapılandırmayı düzenleyen ayrıcalıklı bir DaemonSet ile yapılır. İkinci seçenek güvenlik ekibiyle mutabık kalınması gereken bir uzlaşmadır.
Kendi yapılandırması olan imajlar
Birçok hazır imaj, ortamı geçersiz kılan veya yok sayan bir yapılandırma dosyası getirir:
- JVM uygulamaları. Java HTTP_PROXY'yi okumaz. Sistem özellikleri gerekir. Bunları, JVM'in başlatma betikleri değiştirilmeden aldığı JAVA_TOOL_OPTIONS üzerinden geçirmek uygundur:
env:
- name: JAVA_TOOL_OPTIONS
value: "-Dhttp.proxyHost=proxy-host.example -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy-host.example -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts=localhost|127.0.0.1|*.svc|*.svc.cluster.local|*.cluster.local|10.*|192.168.*"- Kendi HTTP istemci yapılandırması olan imajlar (bazı izleme ajanları, konnektörler, ETL araçları) proxy'yi kendi YAML veya TOML'inden okur. Ortamı görürler ama kullanmazlar. İmajın belgelerini okuyun ve proxy_url gibi anahtarları arayın.
- Uygulama içinde sabit kodlanmış DNS çözümleyicisi olan imajlar, adlara göre NO_PROXY fikrinin kendisini bozar: uygulama adı kendisi çözer, IP'yi alır ve zaten sizin yalnızca alan adı sonekleri olduğunuz listede IP'yi karşılaştırır.
- Değişkenleri geçersiz kılan entrypoint betikleri. Daha seyrek görülür ama kontrol etmeye değer: kubectl exec pod -- cat /proc/1/environ | tr sıfır baytını satır sonuna ana sürecin gerçek ortamını gösterecektir.
Ayrı SDK'lar ve protokoller
Buradaki liste uzun, en sık görülenleri verelim:
- gRPC. Birçok uygulama HTTP_PROXY'yi yalnızca CONNECT üzerinden ve kısıtlamalarla destekler; bir kısmı NO_PROXY'yi yok sayar. Küme içi gRPC trafiği için bu iki kat tehlikelidir.
- Node.js. Yerleşik fetch ve http/https modülü ortamı okumaz. Açık bir agent gerekir. axios kütüphanesi HTTP_PROXY'yi bağımlılığı üzerinden okurken yerleşik fetch okumaz. Bir uygulamada iki istemci farklı davranabilir.
- Bulut SDK'ları. Çoğu HTTP_PROXY'ye saygı duyar, ancak meta veri endpoint'ine (169.254.169.254) başvuru istisnalarda olmalıdır, aksi halde geçici kimlik bilgilerini alma bozulur.
- WebSocket ve ham TCP. Veritabanı, mesaj aracısı, Redis istemcileri kendi protokolleriyle TCP üzerinden çalışır ve HTTP proxy'den hiç geçmez. Böyle trafik mutlaka proxy üzerinden gitmek zorundaysa SOCKS modu veya sidecar düzeyinde şeffaf yakalama gerekir.
- Uygulamalardaki kendi DNS istemcileri (ayrı bir kütüphane üzerinden çözümleme) proxy ile etkileşime girmez ve doğrudan UDP 53 üzerinden gider. Bu normaldir ama NetworkPolicy yazarken unutulmamalıdır.
Bölümün çıkarımı: proxy'yi bir servise çıkarmadan önce, servisin giden bağlantılarının protokoller ve kütüphaneler bazında haritasını çıkarın. Envanterle geçirilen yarım saat, bir günlük hata ayıklamadan tasarruf ettirir.
Sırlar: proxy kimlik bilgileri manifestoya değil Secret'a
Deployment gövdesinde kullanıcı adı ve parolalı proxy adresi, saatini bekleyen bir sızıntıdır. Manifestolar git'te yaşar, CI günlüklerine düşer, kubectl get deploy -o yaml ile namespace'i okuma yetkisi olan herkese görünür. Doğru yapalım.
Varyant 1: Secret'ta tam proxy URL'i
En basit şema: Secret'ta kimlik bilgileriyle hazır dize saklanır, uygulama onu ortam değişkeni olarak alır. Sırrı git dışında oluşturuyoruz (CI değişkenleri, harici sır yöneticisi veya elle):
kubectl -n crawlers create secret generic egress-proxy-creds --from-literal=HTTP_PROXY="http://user123:s3cretPass@proxy-host.example:8080" --from-literal=HTTPS_PROXY="http://user123:s3cretPass@proxy-host.example:8080" --from-literal=http_proxy="http://user123:s3cretPass@proxy-host.example:8080" --from-literal=https_proxy="http://user123:s3cretPass@proxy-host.example:8080"Deployment'ta açık env yerine Secret ve NO_PROXY'li ConfigMap'e referans:
containers:
- name: app
image: registry.internal/price-collector:2.7.0
envFrom:
- configMapRef:
name: egress-proxy-common
- secretRef:
name: egress-proxy-credsÖnemli detay: kullanıcı adı veya parola özel karakterler (@, :, /, %) içeriyorsa URL kodlaması gerekir. @ işareti içeren ve kodlanmamış bir parola URL ayrıştırmasını bozar ve istemci yanlış host'a bağlanmaya çalışır. Proxeon panelinde erişim oluştururken özel karakter içermeyen bir parola seçmek veya hazır URL'yi kopyalamak pratiktir.
Varyant 2: ayrı alanlar ve konteynerde URL birleştirme
Kullanıcı adı ve parola ayrı saklandığında (örneğin harici bir sır deposundan senkronize edilirse), URL entrypoint'te veya uygulamanın kendisinde birleştirilir:
env:
- name: PROXY_USER
valueFrom:
secretKeyRef:
name: egress-proxy-creds
key: username
- name: PROXY_PASS
valueFrom:
secretKeyRef:
name: egress-proxy-creds
key: password
- name: PROXY_HOST
value: "proxy-host.example:8080"Bu varyant parola rotasyonu için daha uygundur: dört hazır dize yerine tek bir Secret anahtarı değişir.
Varyant 3: değişken yerine dosya
Süreç ortam değişkenleri /proc'tan okunur ve sıklıkla crash dump'larına ve hata raporlarına düşer. Güvenlik politikası katıysa, Secret'ı 0400 izinleriyle dosya olarak bağlayın ve uygulamada veya sidecar'da okuyun:
volumes:
- name: proxy-creds
secret:
secretName: egress-proxy-creds
defaultMode: 0400
containers:
- name: app
volumeMounts:
- name: proxy-creds
mountPath: /var/run/secrets/proxy
readOnly: trueVaryant 4: kimlik bilgilerini tamamen kaldırmak
Kararlı giden IP'ye sahip bir egress ağ geçidiniz varsa, proxy'de yetkilendirmeyi kullanıcı adı paroladan IP beyaz listesine çevirebilirsiniz. Bu durumda kümede hiçbir gizli şey saklanmaz: pod'lar ağ geçidinin iç Service'ine gider, ağ geçidi sabit bir adresten çıkar, sağlayıcı trafiği adrese göre geçirir. Proxeon her iki yetkilendirme modunu destekler ve ayrılmış egress'li kümeler için genellikle IP modunu daha güvenli ve işletmesi kolay olduğu için öneriyoruz.
Secret çevresindeki hijyen
- etcd'de Secret'ın at rest şifrelemesini açın — çoğu kendi kurulumunda varsayılan olarak kapalıdır.
- RBAC'ı sınırlayın: namespace sırlarını okuma yetkisi yalnızca gerçekten ihtiyaç duyan servis hesaplarında olmalı. Secret'ı envFrom ile alan uygulama, Secret nesnesini API üzerinden okumamalıdır.
- Ortamı günlüklemeyin. Çerçevenin debug modunda başlangıçta env yazdırmadığını kontrol edin.
- Rotasyon üzerinde anlaşın. Proxy parolasını değiştirmek rutin bir işlem olmalı: Secret'ı güncellediniz, rollout restart yaptınız, eski parolayı kapattınız.
- envFrom kullanıldığında Secret güncellemesinin çalışan pod'ların ortamını değiştirmediğini unutmayın. Rollout restart gerekir. Dosya olarak bağlandığında kubelet içeriği bir-iki dakika içinde günceller ama uygulama dosyayı yeniden okuyabilmelidir.
Sidecar yaklaşımı: ne zaman haklıdır ve proxy'nin yanı sıra ne sağlar
Egress bağlamında sidecar, pod'daki yerel bir proxy konteyneridir; uygulamanın trafiğini alır ve dış proxy üzerinden ileri gönderir. "Kütüphane HTTP_PROXY'ye saygı duyuyor mu" sorusunu "sidecar trafiği yakalayabiliyor mu" sorusuna çevirir ve ikinci soruyu yanıtlamak daha kolaydır.
Sidecar ne zaman haklıdır
- Uygulama, proxy yönetiminin zahmetli olduğu bir yığında yazılmışsa (onlarca farklı istemcili JVM, fetch ve axios karışımı Node.js, C++ ile legacy).
- Kod değiştirmeden zamanlamaya veya hatalara göre adres rotasyonu gerekiyorsa: sidecar birkaç Proxeon proxy'sinden oluşan havuzu tutar ve kendi geçiş yapar.
- Kimlik bilgilerini uygulamadan çıkarmak istiyorsanız: parolayı bilen tek kişi sidecar'dır, uygulama yetkilendirmesiz localhost'a gider.
- Servis için dış dünyaya dönük tek tip tekrar, zaman aşımı ve istek limiti politikası gerekiyorsa.
- Yalnızca HTTP değil başka trafik de gerekiyorsa: SOCKS modu veya şeffaf TCP yakalama.
Sidecar ne zaman gereksizdir
- Tek HTTP istemcili Go veya Python servisi. Ortam değişkenleri yeter.
- Kaynak limitleri sıkıysa: binlerce küçük pod, ekstra 30–50 MB sidecar başına tonlarca belleğe dönüşür.
- Zaten gerekli mantığa sahip bir egress ağ geçidi varsa ve bunu her pod'da kopyalamanın anlamı yoksa.
Yerel sidecar konteynerleri
Kubernetes 1.29 sürümünden itibaren yerel sidecar mekanizması ortaya çıktı ve 1.33'e kadar kararlı hale geldi: restartPolicy: Always olan bir init konteyneri. Böyle bir konteyner ana konteynerden önce başlar, pod'un tüm yaşamı boyunca yaşar ve ana konteynerden sonra düzgünce sona erer. Bu eski bir sıkıntıyı çözer: eskiden uygulama proxy'den önce başlayıp ilk isteklerde connection refused alabiliyordu ve sidecar'lı bir Job asla tamamlanmıyordu çünkü sidecar çalışmaya devam ediyordu.
Çalışan parça: sidecar proxy'li pod
Burada sidecar küçük bir forward proxy'dir (varsayımsal egress-sidecar imajı), 127.0.0.1:3128'i dinler, upstream kimlik bilgilerini Secret'tan okur ve hangi adreslerin doğrudan geçmesi gerektiğini bilir:
apiVersion: apps/v1
kind: Deployment
metadata:
name: feed-importer
namespace: integrations
spec:
replicas: 2
selector:
matchLabels:
app: feed-importer
template:
metadata:
labels:
app: feed-importer
spec:
initContainers:
- name: egress-sidecar
image: registry.internal/egress-sidecar:1.3.0
restartPolicy: Always
env:
- name: LISTEN_ADDR
value: "127.0.0.1:3128"
- name: DIRECT_CIDRS
value: "10.96.0.0/12,10.244.0.0/16,192.168.10.0/24"
- name: DIRECT_SUFFIXES
value: ".svc,.cluster.local,.corp.local"
envFrom:
- secretRef:
name: egress-upstream-creds
resources:
requests:
cpu: 20m
memory: 32Mi
limits:
memory: 96Mi
readinessProbe:
tcpSocket:
port: 3128
containers:
- name: app
image: registry.internal/feed-importer:4.1.0
envFrom:
- configMapRef:
name: egress-proxy-common
env:
- name: HTTP_PROXY
value: "http://127.0.0.1:3128"
- name: HTTPS_PROXY
value: "http://127.0.0.1:3128"
- name: http_proxy
value: "http://127.0.0.1:3128"
- name: https_proxy
value: "http://127.0.0.1:3128"Dikkat: uygulamanın NO_PROXY'si hâlâ gerekli. Sidecar iç trafiği doğrudan geçirebilir ama neden onu localhost'ta gereksiz bir atlamadan geçirelim? Ayrıca sidecar'da CIDR ayrıştırması sizin kontrolünüzde, uygulamada ise değil. Burada çift koruma yerindedir.
iptables üzerinden şeffaf mod
Uygulama ortamı tamamen yok sayıyorsa, iç ağlar ve sidecar'ın kendi trafiği hariç pod'un tüm giden TCP trafiğini sidecar portuna yönlendiren bir init konteyneri eklenir. Sidecar bu durumda şeffaf proxy modunda çalışmalı ve özgün hedef adresini geri yüklemelidir. Init konteynerinin NET_ADMIN yeteneğine ihtiyacı vardır ve sidecar'ın kendi trafiğinin döngüye girmemesi için ayrı bir UID altında çalışması gerekir:
initContainers:
- name: egress-redirect
image: registry.internal/egress-init:1.0.0
securityContext:
capabilities:
add: ["NET_ADMIN"]
command: ["sh", "-c"]
args:
- |
iptables -t nat -N EGRESS_REDIRECT
iptables -t nat -A EGRESS_REDIRECT -d 10.96.0.0/12 -j RETURN
iptables -t nat -A EGRESS_REDIRECT -d 10.244.0.0/16 -j RETURN
iptables -t nat -A EGRESS_REDIRECT -d 127.0.0.0/8 -j RETURN
iptables -t nat -A EGRESS_REDIRECT -m owner --uid-owner 1337 -j RETURN
iptables -t nat -A EGRESS_REDIRECT -p tcp -j REDIRECT --to-ports 15001
iptables -t nat -A OUTPUT -p tcp -j EGRESS_REDIRECTBurada 1337 sidecar'ın çalıştığı UID, 15001 ise şeffaf portudur. Bu, service mesh'lerin trafiği yakalamak için kullandığı ilkedir. Kümede zaten bir mesh varsa, büyük olasılıkla egress yetenekleri yeterli olacak ve ayrı bir sidecar gerekmeyecektir.
Sidecar proxy'nin yanı sıra ne sağlar
- Giden trafik metrikleri hedef host, yanıt kodu, süre bazında: uygulama bunları bilmez, sidecar her şeyi görür.
- Circuit breaker harici API'lere: bir dizi hatada sidecar partneri dövmeyi bırakır ve hızlı ret verir.
- Adres havuzu ve rotasyon: yapılandırmada Proxeon proxy listesi, round-robin veya 407/403 hatasına göre geçiş.
- Yerel DNS önbelleği dış adlar için, CoreDNS yükünü azaltır.
- Hız sınırlama hedef başına, partner API limitlerine uymak ve onların kısıtlamalarına takılmamak için.
Küme egress ağ geçidi: tek çıkış noktası
Egress ağ geçidi farklı ölçekte bir görevi çözer: "bu servis proxy üzerinden nasıl gitsin" değil, "küme dünyaya hangi adreslerden çıkıyor". Belirli bir CNI'ye bağlı olmadan pratik bir uygulamayı inceleyelim.
Mimari
- Sabit dış IP'li bir düğüm grubu ayrılır (bulutta — sabitlenmiş genel adresli veya ayrı bir NAT ağ geçidi arkasında). Düğümler node-role: egress etiketi ve taint alır ki yabancı pod'lar onlara yerleşmesin.
- Bu düğümlerde birkaç replikalı ve anti-affinity'li bir forward proxy Deployment'ı kurulur. Önüne ClusterIP tipi bir Service konur.
- Uygulama pod'ları bu Service'i işaret eden HTTP_PROXY alır. NO_PROXY'leri iç ağları içerir.
- NetworkPolicy, uygulama pod'larına DNS ve ağ geçidi Service'ine trafik dışında küme dışına herhangi bir egress'i yasaklar.
- Ağ geçidi kendisi dışarıya ya doğrudan çıkar (o zaman onun IP'si kümenin giden adresidir) ya da ağ geçidinin IP'sine göre yetkilendirmeli harici bir Proxeon proxy üzerinden çıkar.
Çalışan parça: ağ geçidi Deployment'ı ve NetworkPolicy
apiVersion: apps/v1
kind: Deployment
metadata:
name: egress-gateway
namespace: egress
spec:
replicas: 3
selector:
matchLabels:
app: egress-gateway
template:
metadata:
labels:
app: egress-gateway
spec:
nodeSelector:
node-role: egress
tolerations:
- key: dedicated
value: egress
effect: NoSchedule
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: egress-gateway
topologyKey: kubernetes.io/hostname
containers:
- name: proxy
image: registry.internal/egress-gateway:2.0.1
ports:
- containerPort: 3128
envFrom:
- secretRef:
name: egress-upstream-creds
---
apiVersion: v1
kind: Service
metadata:
name: egress-gateway
namespace: egress
spec:
selector:
app: egress-gateway
ports:
- port: 3128
targetPort: 3128Uygulama namespace'i için doğrudan internet çıkışını kapatan politika:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: egress-only-via-gateway
namespace: crawlers
spec:
podSelector: {}
policyTypes: ["Egress"]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: egress
podSelector:
matchLabels:
app: egress-gateway
ports:
- protocol: TCP
port: 3128
- to:
- ipBlock:
cidr: 10.244.0.0/16
- ipBlock:
cidr: 10.96.0.0/12Böyle bir politika ağ geçidini öneriden zorunluluğa çevirir: unutulmuş HTTP_PROXY'li bir pod onu atlayıp dışarı çıkmaz, sadece zaman aşımı alır. Bu, prodüksiyonda haftalar sonra değil test ortamında dakikalar içinde tespit edilir.
Yüksek erişilebilirlik ve kapasite
- Farklı düğümlerde en az üç replika ve minAvailable: 2 ile PodDisruptionBudget.
- Bağlantı izleme: açık olanların sayısı, CONNECT kurulum süresi, upstream'den gelen 5xx oranı.
- Tek bir istemci pod'undan eşzamanlı bağlantı limiti, böylece tek bir arızalı servis ağ geçidini tıkamaz.
- Ağ geçidi düğümlerinde conntrack hesabı: saniyede on binlerce bağlantıda tablo taşar ve gizemli paket kayıpları yaşarsınız. nf_conntrack_max'i önceden artırın.
Kümede teşhis: pod'dan giden IP nasıl kontrol edilir ve arızalar nereye bakılır
Kümede proxy sorunları sinsidir; çünkü semptom nedenden uzakta ortaya çıkar. Aşağıda birkaç adımda arızayı yerelleştirmenizi sağlayan bir metodoloji var.
Adım 1: ortamın gerçekten uygulandığından emin olun
kubectl -n crawlers exec deploy/price-collector -- env | grep -i proxyHer iki harf durumunu, NO_PROXY'nin eksiksizliğini, fazladan boşluk olmadığını arayın. Uygulama bir entrypoint betiği üzerinden başlıyorsa, ek olarak ana sürecin ortamını /proc/1/environ üzerinden kontrol edin — yeni bir kabuk başlatan exec'in gösterdiğinden farklı olabilir.
Adım 2: giden IP'yi kontrol edin
Kümede veya şirketin harici bir host'unda istemcinin adresini ve başlıkları döndüren basit bir echo servisi kurun. Sonra pod'dan:
kubectl -n crawlers exec deploy/price-collector -- curl -s https://echo.yourcompany.example/ip
kubectl -n crawlers exec deploy/price-collector -- curl -s --noproxy '*' https://echo.yourcompany.example/ipİlk komut ortam kurallarına göre gider, ikincisi proxy'yi zorla atlar. Adresler aynıysa — proxy kullanılmıyor demektir. İlki proxy sağlayıcısının IP'sini, ikincisi düğümün veya bulut NAT'ının IP'sini döndürüyorsa şema çalışıyor. Bu aynı zamanda Proxeon panelinin size verdiği adresi değil, düğüm adresini gördüğünüzden emin olmanın tek güvenilir yoludur.
Adım 3: proxy ile diyaloğa istemcinin gözünden bakın
kubectl -n crawlers exec deploy/price-collector -- curl -sv https://api.partner.example/status 2>&1 | head -30Çıktıda CONNECT satırını ve proxy'nin ona verdiği yanıt kodunu arayın. Tipik sinyaller:
- 407 Proxy Authentication Required — kimlik bilgileri ulaşmadı veya yanlış. Parolanın URL kodlamasını ve proxy'nin tam olarak Basic yetkilendirme beklediğini kontrol edin.
- CONNECT'te proxy'den 403 — hedef host veya port proxy politikası tarafından yasaklanmış ya da IP yetkilendirmesinde IP'niz beyaz listede değil.
- CONNECT'te 502/503 — proxy hedefe ulaşamadı. Hedef içerideyse bu "NO_PROXY'de delik" teşhisidir.
- Proxy'nin kendisine Connection refused — yanlış adres, port veya NetworkPolicy ağ geçidine trafiği geçirmiyor.
- Yanıtsız zaman aşımı — çoğu zaman ağ politikası veya eksik rota. DNS'in hiç çözülüp çözülmediğini kontrol edin.
Adım 4: küme içi çağrıları kontrol edin
kubectl -n crawlers exec deploy/price-collector -- curl -sv http://payments.default.svc.cluster.local:8080/healthz 2>&1 | grep -iE "connected|proxy|HTTP/"Çıktıda ClusterIP'ye doğrudan bağlantı yerine proxy'ye başvuru varsa, NO_PROXY bu adresi kapsamamış demektir. Aynısını kısa ad payments ve servis IP'si için de tekrarlayın — üçü de doğrudan gitmeli.
Adım 5: araçları olmayan imajlar için ephemeral konteyner
Minimal imajlarda ne curl ne de nslookup vardır. Pod'un ağ ad alanına bir hata ayıklama konteyneri bağlayan kubectl debug kullanın:
kubectl -n crawlers debug -it pod/price-collector-7d9f4-x2k8q --image=registry.internal/netdebug:latest --target=app -- shİçeride aynı IP'ler, aynı resolv.conf ve localhost'taki aynı sidecar mevcuttur. Yalnızca uygulamanın ortamı buraya miras kalmaz: değişkenleri ana sürecin /proc/PID/environ'undan kontrol edin.
Adım 6: günlükler ve olaylar
- Sidecar veya ağ geçidi günlükleri: upstream yanıt kodları, CONNECT süresi, hangi host'lara başvuruldu. İç adresi dışarı gönderme girişimi tam burada görünür.
- Pod olayları: kubectl describe pod, ImagePullBackOff gösterecektir — container runtime'ın proxy için yapılandırılmadığının veya registry'nin onun NO_PROXY'sinde olmadığının işareti.
- Debug modunda CoreDNS günlükleri: dış adlarda NXDOMAIN artışı, uygulamanın dış host'ları kendisi çözdüğünü, oysa onları proxy'ye vermesi gerektiğini söyler.
- Egress düğümlerinde conntrack metrikleri: nf_conntrack_count'un limite yaklaşması, kayıpların habercisidir.
Adım 7: proxy sorununu hedef sorunundan ayırın
Aynı 403 hem proxy'den hem hedef siteden gelebilir. Ayırt etmede yanıttaki Via başlığı veya sağlayıcıya özgü başlıklar ve --noproxy ile istek karşılaştırması yardımcı olur. Proxy üzerinden 403, doğrudan 200 ise — sorun proxy ayarlarında veya hedef kaynağın bu havuzun adreslerinden gelen trafiği kısıtlamasında. Her iki durumda da 403 ise — sorun isteğin kendisindedir.
Tipik hatalar
En sık basılan tırmıkları bir araya toplayalım. Çoğunu zaten inceledik, burada hızlı kontrol için özet:
- NO_PROXY sunucudan kopyalanmış. localhost,127.0.0.1 değeri kümede servis iletişimini garantili bozar. .svc, .cluster.local, service ve pod CIDR gerekir.
- Python veya eski curl'de NO_PROXY'de CIDR'ye bahis. Kütüphane kaydı sessizce yok sayar, ClusterIP'ye trafik proxy üzerinden gider. DNS adlarıyla gidin ve sonekleri kullanın.
- Yalnızca büyük harfli değişkenler. curl HTTP_PROXY'yi büyük harfle okumaz. Her iki biçimi de tanımlayın.
- Manifestoda kimlik bilgileri. Parola git'te ve kubectl get -o yaml çıktısında. Yalnızca Secret, tercihen ağ geçidi üzerinden IP yetkilendirmesi.
- Özel karakterli parola URL kodlaması olmadan. İstemci URL'yi yanlış ayrıştırır, var olmayan bir host'a connection refused alırsınız.
- kubelet ve containerd'ın pod env'ini alacağını sanmak. İmajlar çekilmez, herkes Deployment'ta sorun arar oysa düğümlerde systemd drop-in gerekir.
- Runtime'ın NO_PROXY'sinde unutulan registry. İç registry erişilemez olur çünkü containerd onu dış proxy üzerinden almaya gider.
- Java ile HTTP_PROXY. JVM bu değişkenleri okumaz. JAVA_TOOL_OPTIONS üzerinden sistem özellikleri gerekir.
- Sidecar'ı sıradan konteyner olarak koymak, restartPolicy: Always'lı yerel init yerine. Uygulama proxy'den önce başlar ve ilk isteklerde düşer, Job asla tamamlanmaz.
- NetworkPolicy'siz egress ağ geçidi. Ağ geçidi var ama pod'ların yarısı onu atlayıp gidiyor ve bunu partner bilinmeyen IP listesi gönderdiğinde öğreniyorsunuz.
- Secret güncellendi ama rollout restart yapılmadı. Eski pod'lar panelde zaten kapattığınız eski parolayla çalışmaya devam eder.
- Bulut meta verilerinin yok sayılması. 169.254.169.254 NO_PROXY'de yoksa — bulut SDK'sı geçici anahtarları alamaz ve uygulama gizemli yetkilendirme hatalarıyla düşer.
- Tüm replikaların tek proxy adresini ezmesi. Otuz replika tek çıkış adresini döver, partner API limitlerle yanıtlar. Yükü sidecar veya ağ geçidi üzerinden adres havuzuna dağıtın.
Araçlar ve kaynaklar
Kümede giden trafikle çalışırken elinizin altında bulundurmanız gereken set.
Yerleşik Kubernetes araçları
- kubectl exec ve kubectl debug — minimal imajlar için ephemeral konteynerler dahil pod içinde ortam ve ağ bağlantılarını kontrol etme.
- kubectl describe pod ve olaylar — ImagePullBackOff, probe başarısızlıkları, secret bağlama hataları.
- NetworkPolicy — ağ geçidi üzerinden çıkışa zorlama ve "kim atlayıp gidiyor" hipotezlerini test etme.
- Yerel sidecar konteynerleri (initContainers'da restartPolicy: Always) — proxy yardımcısının doğru yaşam döngüsü.
Ağ teşhisi
- curl, dig, tcpdump, ss, iptables içeren hata ayıklama imajı — minimal bir dağıtım üzerine kendinizinkini derleyin ve iç registry'de tutun.
- İstemcinin IP'sini ve başlıkları döndüren kendi echo servisiniz — giden adresi görmenin en dürüst yolu.
- Düğümde pod IP'sine göre filtrelenmiş tcpdump — tam olarak hangi adrese bağlantı açıldığını görmeniz gerektiğinde.
- Debug modunda CoreDNS günlükleri — kimin neyi çözdüğünü anlamak için.
Sır ve yapılandırma yönetimi
- Küme Secret'ına senkronize eden harici sır yöneticisi — proxy kimlik bilgileri git'te yaşamasın ve merkezi olarak dönsün.
- NO_PROXY için ortak ConfigMap'li Kustomize veya Helm — namespace'teki tüm Deployment'lar için tek gerçek.
- HTTP_PROXY'li her pod'da NO_PROXY varlığını kontrol eden kabul politikaları (admission denetleyicileri) — "sunucudan" kopyala-yapıştırına karşı otomatik koruma.
Proxy sağlayıcı
Proxy tarafında üç şey önemlidir: IP'ye göre yetkilendirme imkanı (ağ geçidi şeması için), sabitleme süresi net olan kararlı adresler ve erişimleri yönetmek için bir API ki parola rotasyonu CI'dan otomatikleştirilebilsin. Proxeon'da bu mekanizmalar hem panelde hem API üzerinden mevcuttur ve sağlayıcıyı bağlantı dizelerini elle kopyalamak yerine GitOps sürecine entegre etmenizi sağlar.
Vakalar ve sonuçlar
Vaka 1: ETL platformu ve "bozulan" iç servisler
Veri ekibi, pazar yeri katalog toplayıcılarının ayrılmış adresler üzerinden gitmesi için yirmi Deployment'a HTTP_PROXY ekledi. NO_PROXY'yi sunucu belgelerinden kopyaladılar: localhost ve 127.0.0.1. Bir saat içinde iç servisler arasında 5xx oranı %0,1'den %34'e yükseldi, üç servisin readiness probe'ları dalgalanmaya başladı. Ortak ConfigMap'e .svc, .cluster.local ve her iki CIDR eklendikten ve rollout restart yapıldıktan sonra hatalar beş dakikada kayboldu. Ek bir etki: NO_PROXY'yi ConfigMap'e taşımak, bir sonraki liste değişikliği süresini yarım günden (yirmi manifo düzenleme) tek commit'e indirdi.
Vaka 2: JVM entegrasyonları ve sidecar
Tek bir uygulamada yedi farklı HTTP istemcisi olan Java'lı bir finans entegratörü (bir kısmı proxy sistem özelliklerini okumayan kütüphaneler üzerinden). Her şeyi JAVA_TOOL_OPTIONS üzerinden yapılandırma girişimi yedi istemciden beşini kapsadı. Çözüm — iptables üzerinden şeffaf yakalamalı yerel sidecar oldu. Yedi istemci de kod değişikliği olmadan proxy üzerinden gitti, kimlik bilgileri uygulamadan kayboldu ve giden trafik metrikleri servisin tarihinde ilk kez ortaya çıktı. Maliyet: pod başına 40 MB bellek, on iki replikada önemsiz çıktı.
Vaka 3: egress ağ geçidi ve IP ile yetkilendirme
Otomatik ölçeklendirmeli altmış düğümlü bir küme, IP beyaz listesi gerektiren birkaç partner API'sine başvuruyordu. Her düğüm ölçeklendirmesi giden adres kümesini değiştiriyordu, partnerler trafiği engelliyordu. Üç ayrılmış düğümde sabit adresli bir egress ağ geçidi kuruldu, tüm uygulama namespace'lerinde NetworkPolicy uygulandı ve Proxeon proxy'de yetkilendirme IP moduna alındı. Sonuç: "partner bizi yine engelledi" olaylarının sayısı ayda üç-dörtten çeyrekte sıfıra düştü ve kümeden tüm proxy parolalı Secret'lar kayboldu. Yan sonuç: ağ geçidinin merkezi günlükleri, kimsenin haberi olmayan dış API'lere giden iki servisi ortaya çıkardı.
Vaka 4: containerd ve gizemli ImagePullBackOff
Küme zorunlu proxy'li bir ağa taşındıktan sonra HTTP_PROXY tanımlı pod'lar çalışıyordu ama yeni düğümler tek bir imajı bile çekemiyordu. Neden — containerd için drop-in dosyasının eksikliği. Proxy'li dosya ve NO_PROXY'ye iç registry eklendikten sonra sorun çözüldü. Ders, düğümlerin bootstrap betiğine kontrol olarak eklendi: kümeye katılmadan önce düğüm, containerd'ın iç ve dış registry'den bir test imajı çekebildiğini doğrular.
Devreye alma kontrol listesi
Proxy'yi prodüksiyon servisine çıkarmadan önce listeyi gözden geçirin. Her madde birinin gece nöbetindeki olayıdır.
Hazırlık
- Servisin giden bağlantı haritası çıkarıldı: dış host'lar, iç servisler, veritabanları, kuyruklar, bulut endpoint'leri, protokoller.
- Kümenin service CIDR, pod CIDR, node CIDR'i ve şirketin iç alan adları biliniyor.
- Uygulamanın hangi HTTP istemcilerini kullandığı ve her birinin NO_PROXY'yi nasıl yorumladığı (CIDR, sonekler, harf durumu) kontrol edildi.
- Düzey seçildi: konteyner, sidecar veya ağ geçidi. Karar belgelendi.
Yapılandırma
- NO_PROXY namespace ConfigMap'ine taşındı ve şunları içeriyor: localhost, 127.0.0.1, .svc, .svc.cluster.local, .cluster.local, kubernetes.default.svc, service CIDR, pod CIDR, 169.254.169.254, iç alan adları.
- Değişkenler her iki harf durumunda tanımlı.
- Kimlik bilgileri yalnızca Secret'ta; parola URL kodlu; Secret'lara RBAC sınırlı.
- JVM için nonProxyHosts'lu JAVA_TOOL_OPTIONS tanımlı.
- Sidecar — yerel (restartPolicy: Always'lı initContainers), readinessProbe ve kaynak limitleriyle.
- Ağ geçidi — en az üç replika, anti-affinity, PDB, uygulama namespace'lerinde NetworkPolicy.
- Düğümlerde container runtime ayrıca yapılandırıldı; iç registry onun NO_PROXY'sinde.
Test ortamında doğrulama
- Pod içindeki env beklenen değerleri gösteriyor.
- Echo servisine istek proxy adresini döndürüyor; --noproxy ile düğüm adresini.
- İç servislere kısa ad, tam ad ve ClusterIP ile istekler doğrudan gidiyor (curl -v'de görünüyor).
- Readiness ve liveness en az on dakika boyunca istikrarlı şekilde geçiyor.
- Kubernetes API istemcisi (varsa) control plane'i görüyor.
- Sidecar veya ağ geçidi günlükleri iç adreslere başvuru içermiyor.
Devreye alma ve sonrası
- maxUnavailable %25'i geçmeyen aşamalı rollout; servisler arası 5xx ve proxy'den 407/403 hatalarının izlenmesi.
- Parola rotasyon prosedürü belgelendi: Secret güncelleme, rollout restart, eski erişimi kapatma.
- Proxy'den gelen hata oranı artışı ve egress düğümlerinde conntrack'in limite yaklaşması için alarmlar kuruldu.
- Bir hafta sonra — ağ geçidi günlüklerinin gözden geçirilmesi: beklenmedik dış adresler ortaya çıkmadı mı.
SSS
HTTP_PROXY'yi her Deployment yerine tüm küme için global olarak tanımlayabilir miyim?
Doğrudan bir mekanizma yok: ortam değişkenleri pod spesifikasyonunun parçasıdır. Pratik seçenekler — Helm veya Kustomize aracılığıyla tüm manifestolarda envFrom kullanan ortak bir ConfigMap ya da belirli bir ek açıklamaya sahip pod'lara değişkenleri otomatik ekleyen bir mutating admission denetleyicisi. İkinci yöntem daha güçlü ama dikkat gerektirir: aynı zamanda NO_PROXY'yi de eklemelidir, aksi halde otomasyon servisleri toplu halde bozar.
Neden pod'da curl proxy üzerinden gidiyor ama Python uygulaması gitmiyor, oysa ortam aynı?
Muhtemelen neden harf durumu veya NO_PROXY ayrıştırması. curl http_proxy'yi yalnızca küçük harfle okur, Python her ikisini okur. Diğer yandan Python NO_PROXY'de CIDR'yi anlamaz, güncel curl anlar. Her iki hipotezi de kontrol edin: değişkenleri her iki harf durumunda tanımlayın ve CIDR yerine alan adı soneklerini kullanın.
İç trafik zaten doğrudan kurallı sidecar üzerinden gidiyorsa NO_PROXY'ye gerek var mı?
Teknik olarak onsuz idare edilebilir ama bırakmanızı öneriyoruz. Birincisi, binlerce iç istek için localhost üzerinde fazladan atlama sıfır olmayan gecikme ve sidecar üzerinde yüktür. İkincisi, sidecar düşer veya yeniden başlarsa, doğru NO_PROXY'li uygulama iç servislerle konuşmaya devam eder, olmayan uygulama ise her şeyi kaybeder.
Partnere giden isteğin düğüm adresinden değil, kesinlikle proxy adresinden gittiğini nasıl anlarım?
Tek güvenilir yol — aynı pod'dan ve aynı ortamla istemcinin IP'sini döndüren echo servisine kontrol isteği. Yanıtı Proxeon panelinde erişiminiz için görünen adresle ve --noproxy ile gelen yanıtla karşılaştırın. Partnerin kendi günlükleri de yardımcı olur ama genellikle el altında değildir.
Proxy'ye giden bağlantıyı şifrelemeye değer mi (HTTPS_PROXY'de https:// şeması)?
Hedef sitelere giden HTTPS trafiği için içerik zaten CONNECT tüneli içinde end-to-end şifrelidir, proxy yalnızca hedef host'u görür. Proxy'ye kadar TLS, yetkilendirme kimlik bilgilerini ve host adlarını pod'dan proxy'ye kadar olan segmentte korur. Bu segment güvenilmeyen bir ağdan geçiyorsa proxy'ye kadar TLS haklıdır, ancak hem istemcinin hem proxy'nin bunu desteklediğinden emin olun: bazı kütüphaneler HTTPS_PROXY'de https:// şemasını yok sayar. Küme içinde pod ile egress ağ geçidi arasında genellikle ağ politikaları yeterlidir.
Küme dışına çıkması gereken veritabanı ve mesaj aracısı trafiğiyle ne yapmalıyım?
HTTP proxy'den geçmez — HTTP değildir. Seçenekler: sağlayıcı ve istemci kütüphanesi destekliyorsa SOCKS5 modu; sidecar düzeyinde TCP tünellemeyle şeffaf yakalama; ya da protokol değil yönlendirme düzeyinde çalışan bir egress ağ geçidi. Seçim, tam olarak belirli bir dış IP'ye mi ihtiyacınız olduğuna yoksa merkezi çıkışın yeterli olup olmadığına bağlıdır.
Aynı proxy'yi containerd ve uygulamalar için kullanabilir miyim?
Teknik olarak evet, ama genellikle bunlar farklı işlerdir. Runtime imajları genel registry'lerden çeker ve ona kurumsal çevre proxy'si gerekir. Uygulamalar partner API'lerine gider ve belirli özelliklere sahip ayrılmış adreslere ihtiyaç duyar. Karıştırmak, imaj indirmenin uygulama görevleri için tasarlanmış trafik limitlerini tüketmesine yol açar. Ayırın.
Proxy parolasını kesinti olmadan güvenle nasıl değiştiririm?
Sağlayıcıda ikinci bir erişim açın, Secret'ı yeni kimlik bilgileriyle güncelleyin, aşamalı replika güncellemesiyle rollout restart yapın, günlüklerden 407 olmadığından emin olun ve ancak ondan sonra eski erişimi kapatın. Ağ geçidi üzerinden IP yetkilendirmesi kullanıyorsanız, parola rotasyonu tamamen görev listenizden kalkar.
Proxy'nin yalnızca belirli dış alan adları için kullanılmasını nasıl sınırlarım?
NO_PROXY tersine çalışır — proxy'nin nereye gitmeyeceğini tanımlar ve onun temelinde beyaz liste kurulamaz. Bu tür mantık sidecar veya ağ geçidinde uygulanır: "A listesindeki alan adları — Proxeon adres havuzu üzerinden, geri kalanı doğrudan veya yasak" gibi kurallar. Ayrıca doğrudan internet çıkışının fiziksel olarak var olmaması için NetworkPolicy.
Sonuç
Kubernetes'te giden trafik sunucudakinden detaylarda değil, yapının kendisinde farklıdır: her pod'un iki dünyası vardır — iç ve dış — ve proxy yalnızca ikincisini görmelidir. Diğer her şey bundan kaynaklanır. NO_PROXY bir formaliteden, kümenin tüm iç ağını tanımlaması gereken ve farklı kütüphanelerin onu farklı yorumladığını hesaba katması gereken bir mimari unsura dönüşür. Ortam değişkenleri yalnızca süreç içinde çalışır — kubelet, container runtime ve inatçı SDK'lar ayrı ilgi gerektirir. Kimlik bilgileri manifestolara düşmemeli, ideal olarak IP yetkilendirmeli ağ geçidi sayesinde kümeden tamamen kaybolmalıdır.
Proxy'yi yerleştirmenin üç düzeyi — konteyner, sidecar, egress ağ geçidi — rakip değil, farklı işler için araçlardır. Konteyner düzeyi hızlı ve granülerdir. Sidecar, kütüphane davranışına bağımlılığı kaldırır ve metrikler, rotasyon, tekrarlar ekler. Ağ geçidi kümeye dış dünyada öngörülebilir bir yüz verir ve proxy üzerinden çıkışı öneri değil, ağ politikalarıyla desteklenen bir kural haline getirir.
Bundan sonra ne yapmalı? Envanterle başlayın: servislerinizin giden bağlantılarının haritasını çıkarın ve kaçının iç olduğuna bakın. Sonra NO_PROXY'yi ortak bir ConfigMap'e taşıyın ve teşhis bölümündeki metodolojiyle test ortamında doğrulayın. Kümede birden fazla yığın varsa veya kararlı adres gereksinimleri varsa — bir egress ağ geçidi tasarlayın ve ona IP yetkilendirmeli Proxeon ayrılmış adreslerini bağlayın. Ve devreye alma kontrol listesini yakın tutun: yaşanmayan ilk olayda kendini amorti eder.