deniz.in

Piyasalar

Hava durumu

Hava durumu yükleniyor

· kaynak dev.to (home feed)

E-ticarette ödeme zaman aşımlarına 900 req/s'de CPU değil, Docker'ın varsayılan ağ yapısı neden oldu

dev.to'da yayımlanan bir vaka analizi, 900 req/s'deki ödeme gecikmesi artışını (2,1 saniyeye çıkış) Docker'ın varsayılan bridge'ine, conntrack tükenmesine ve bağlantı başına DNS sorgularına bağlıyor — uygulama kodu değişmeden çözüldü.

E-ticarette ödeme zaman aşımlarına 900 req/s'de CPU değil, Docker'ın varsayılan ağ yapısı neden oldu

Yük altında ödeme nasıl kırıldı

dev.to'da yayımlanan bir vaka analizi, konteynerleştirilmiş bir e-ticaret pazaryerinin, trafik saniyede 900 isteğe yaklaşırken ödeme p95 gecikmesinin 280 milisaniyeden 2,1 saniyenin üzerine nasıl fırladığını ele alıyor. İş yükü, yaklaşık bir yıl önce konteynerleştirilmiş ve Docker Compose ile tek bir ana makinede çalışan bir PHP monolitiydi: uygulama container'larının yanı sıra Redis, bir worker kuyruğu ve bir search sidecar'ı, hepsi yönetilen bir load balancer arkasındaydı. Site yaklaşık 40.000 günlük aktif kullanıcıya hizmet veriyordu ve flash satışlar site donmasına dair talepler açmaya başlamadan önce altı ay boyunca kararlıydı.

dev.to yazısına göre ekibin ilk tepkisi, CPU veya bellek baskısı varsayımıyla daha fazla uygulama container'ı eklemek oldu. Bu durumu daha da kötüleştirdi. Olay boyunca Postgres sorgu süreleri 8 ila 14 milisaniye arasında kaldı ve veritabanı da böylece dışlandı. Gerçek darboğaz, container'lar arasındaki ağ katmanındaydı; yazarların da belirttiği gibi, varsayılan araçlar bu katmana neredeyse hiç görünürlük sunmuyor.

Birbirini besleyen üç ağ arızası

Denetim, her biri tek başına ölümcül olmayan üç sorun buldu:

  • Varsayılan bridge ek maliyeti: Container'lar arasındaki her atlama (uygulamadan Redis'e, uygulamadan Postgres'e, uygulamadan search'e) Docker'ın varsayılan bridge'indeki userland proxy'den geçiyordu ve atlama başına yaklaşık 1,8 milisaniye ekliyordu. İstek başına üç-dört iç çağrı ve 900 req/s ile bu maliyet hızla birikti.
  • Conntrack tablosu tükenmesi: nf_conntrack_max hâlâ çekirdek varsayılanı olan 65.536'daydı. Kısa ömürlü Redis ve Postgres bağlantıları, artış sırasında tablo dolana kadar girdileri hızla tüketiyor, çekirdek sessizce paket düşürüyor ve syslog hiçbir işe yarar yere gönderilmediği için bu fark edilmiyordu.
  • DNS çözümleme yükü: Docker'ın 127.0.0.11 adresindeki gömülü DNS'i, uygulama sonuçları önbelleğe almak yerine her yeni bağlantıda servis adlarını yeniden çözüyor ve bağlantı dalgalanması altında sorgular birbirinin kuyruğuna girmeye başlıyordu.

Yazarlar, bunların hiçbirinin staging ortamındaki on test kullanıcıyla ortaya çıkmadığını, ama hepsinin aynı anda 900 gerçek kullanıcıyla ortaya çıktığının altını çiziyor.

Neden Kubernetes'e yönelmediler

Yazı, Kubernetes'e geçişi açıkça dışarıda bırakıyor; bunun hiçbir şeyi çözmeyeceğini savunuyor: Kubernetes'in kendi başına aynı sorunların benzerleri var — CNI eklentisi seçimi, kube-proxy modu ve CoreDNS önbelleğe alması dahil. Kök nedeni ele almadan orkestratör değiştirmek, sorunu yalnızca taşıyor. Ekip ayrıca, daha az iç çağrı yapmak için uygulamayı yeniden yazmayı da reddetti; gerekçe olarak çağrı deseninin normal olduğunu ve ağ katmanının bunu verimli şekilde ele alması gerektiğini, tersinin değil, gösterdiler.

Dört katmanda çözüm

Birincisi, çekirdek ayarı: nf_conntrack_max 262.144'e yükseltildi, established TCP zaman aşımı 600 saniyeye ayarlandı, tcp_tw_reuse etkinleştirildi ve somaxconn 4.096'ya çıkarıldı; bunlar bir sysctl yapılandırma dosyasıyla uygulandı. Dolan bir conntrack tablosu sessizce başarısız olduğu için conntrack tablosu kullanımı da izlemeye eklendi.

İkincisi, iç trafik varsayılan bridge'den alınarak container'lar arası iletişimin etkinleştirildiği, 9.000 baytlık MTU'lu ve kendi alt ağı olan kullanıcı tanımlı bir bridge ağına taşındı. Uygulama container'ları, Redis ve search sidecar'ı bu ağa katıldı; böylece east-west trafiği Docker'ın varsayılan NAT yolundan kaçındı. Genel internete açık load balancer ayrı bir ağda kaldığı için dışsal saldırı yüzeyi değişmedi.

Üçüncüsü, bir dnsmasq sidecar'ı yerel DNS önbelleklemesi sağladı; yukarı akışı Docker'ın çözümleyicisine yönlendiriyordu, 1.000 girdilik önbellek ve 10 saniyelik yerel TTL ile. Bu TTL, artış sırasındaki bağlantı dalgalanmasını emmek için seçildi ama değiştirilen container'ların yine de hızlıca yakalanmasını sağlıyordu — yazarlar, yeniden başlatılan bir container'ın bir TTL penceresi içinde doğru çözüldüğünü özellikle doğruladı.

Dördüncüsü, Postgres önüne transaction pooling modunda PgBouncer eklendi; en fazla 2.000 istemci bağlantısı ve varsayılan 50 havuz boyutuyla yapılandırıldı. Bu adım bilinçli olarak en sona bırakıldı: daha az kısa ömürlü bağlantı, daha az conntrack girdisi ve daha az DNS sorgusu demek olduğundan, havuzlama ancak ağ katmanı kendisi sağlamlaştıktan sonra en çok yararını sağlıyor. Vaka analizi, tüm çözümün hiçbir uygulama kodu değişikliği gerektirmediğini vurguluyor.

Neden önemli

Bu olay, arıza modunun çoğu ekibin izlediği metrikler için görünmez olması nedeniyle yararlı bir şablon. Ödeme yolu bozulurken CPU, bellek ve veritabanı gecikmesi sağlıklı görünüyordu ve içgüdüsel çare — container'ları yatayda büyütmek — conntrack ve DNS'ten geçecek daha fazla kısa ömürlü bağlantı üreterek sorunu fiilen derinleştirdi. dev.to yazısındaki daha geniş ders: konteynerleştirilmiş bir uygulama yalnızca yük altında yavaşlıyorsa, kapasite eklemeden önce conntrack kullanımını, trafiğinizin gerçekte kullandığı bridge sürücüsünü ve DNS çözümleme davranışını kontrol edin. Yazı ayrıca, eşdeğer tuzakların orada farklı adlarla bulunduğundan, Kubernetes'i bir panzehir olarak görme eğilimine de karşı çıkıyor. Son olarak, proaktif olarak kapatmaya değer bir izleme boşluğunu ortaya koyuyor: dolu bir conntrack tablosundan kaynaklanan çekirdek düzeyindeki paket düşürmeleri bariz bir hata üretmez, yalnızca sessizlik üretir.

  • #docker
  • #networking
  • #containers
  • #devops
  • #performance