Tinder'in Kubernetes'e taşınması

Yazan: Chris O'Brien, Mühendislik Müdürü Chris Thomas, Mühendislik Müdürü | Jinyong Lee, Kıdemli Yazılım Mühendisi | Düzenleyen: Cooper Jackson, Yazılım Mühendisi

Neden

Neredeyse iki yıl önce, Tinder platformunu Kubernetes'e taşımaya karar verdi. Kubernetes, değişmez dağıtım yoluyla Tinder Engineering'i konteynerizasyon ve düşük dokunuşlu operasyona doğru yönlendirmek için bir fırsat verdi. Uygulama derlemesi, dağıtımı ve altyapısı kod olarak tanımlanır.

Ayrıca ölçek ve istikrar sorunlarına da değinmek istiyorduk. Ölçekleme kritik hale geldiğinde, yeni EC2 bulut sunucularının çevrimiçi olmasını beklemek için birkaç dakika bekledik. Dakikalar yerine trafiği saniyeler içinde planlama ve sunma fikri bize cazip geliyordu.

Kolay değildi. 2019'un başlarında geçirdiğimiz göç sırasında, Kubernetes kümemizdeki kritik kitleye ulaştık ve trafik hacmi, küme boyutu ve DNS nedeniyle çeşitli zorluklarla karşılaşmaya başladık. 200 hizmeti taşımak ve toplam 1.000 düğüm, 15.000 kapsül ve 48.000 çalışan konteyner ölçeğinde bir Kubernetes kümesi çalıştırmak için ilginç zorlukları çözdük.

Nasıl

Ocak 2018'den itibaren göç çabalarının çeşitli aşamalarında çalıştık. Tüm hizmetlerimizi kapatarak ve bunları Kubernetes tarafından barındırılan bir dizi hazırlama ortamına dağıtarak başladık. Ekim ayının başından itibaren, yöntemsel olarak tüm eski hizmetlerimizi Kubernetes'e taşımaya başladık. Ertesi yıl Mart ayına kadar göçümüzü sonuçlandırdık ve Tinder Platformu artık sadece Kubernetes üzerinde çalışıyor.

Kubernetes için Bina Resimleri

Kubernetes kümesinde çalışan mikro hizmetler için 30'dan fazla kaynak kodu deposu vardır. Bu depolardaki kod, aynı dil için birden fazla çalışma ortamı ile farklı dillerde (örneğin, Node.js, Java, Scala, Go) yazılmıştır.

Yapım sistemi, tipik olarak bir Dockerfile ve bir dizi kabuk komutundan oluşan her mikro hizmet için tamamen özelleştirilebilir bir “yapı bağlamı” üzerinde çalışacak şekilde tasarlanmıştır. İçerikleri tamamen özelleştirilebilir olsa da, bu yapı bağlamlarının tümü standart bir format izlenerek yazılır. Yapı bağlamlarının standardizasyonu, tek bir yapı sisteminin tüm mikro hizmetleri ele almasını sağlar.

Şekil 1–1 Oluşturucu kapsayıcısı üzerinden standart oluşturma işlemi

Çalışma zamanı ortamları arasında maksimum tutarlılığı sağlamak için, geliştirme ve test aşamasında aynı oluşturma işlemi kullanılır. Bu, platform boyunca tutarlı bir yapı ortamı sağlamak için bir yol tasarlamamız gerektiğinde benzersiz bir zorluk getirdi. Sonuç olarak, tüm oluşturma işlemleri özel bir “Oluşturucu” kabının içinde yürütülür.

Builder konteynerinin uygulanması için bir dizi gelişmiş Docker tekniği gerekiyordu. Bu Oluşturucu kapsayıcısı, Tinder özel havuzlarına erişmek için gereken yerel kullanıcı kimliğini ve sırlarını (örn. SSH anahtarı, AWS kimlik bilgileri vb.) Devralır. Yapı yapılarını depolamanın doğal bir yolunu bulmak için kaynak kodunu içeren yerel dizinleri bağlar. Oluşturucu kapsayıcısı ile ana makine arasında yerleşik yapıların kopyalanmasını ortadan kaldırdığı için bu yaklaşım performansı artırır. Depolanan derleme yapıları, bir daha yapılandırılmadan bir dahaki sefere yeniden kullanılır.

Belirli hizmetler için, Oluşturucu içinde derleme zamanı ortamını çalışma zamanı ortamıyla eşleştirmek için başka bir kap oluşturmamız gerekiyordu (örneğin, Node.js bcrypt kitaplığının yüklenmesi platforma özgü ikili yapay nesneler oluşturur). Derleme zamanı gereksinimleri hizmetler arasında farklılık gösterebilir ve son Dockerfile anında oluşturulur.

Kubernetes Küme Mimarisi ve Geçişi

Küme Boyutlandırma

Amazon EC2 bulut sunucularında otomatik küme sağlama için kube-aws kullanmaya karar verdik. İlk başlarda, her şeyi bir genel düğüm havuzunda çalıştırıyorduk. Kaynakları daha iyi kullanmak için iş yüklerini farklı boyut ve örnek türlerine ayırma ihtiyacını hızla belirledik. Bunun nedeni, daha az sayıda ağır iş parçacığının birlikte çalıştırılmasının bizim için daha fazla sayıda tek iş parçacıklı kapsülle bir arada bulunmalarına izin vermekten daha öngörülebilir performans sonuçları sağlamasıydı.

Biz yerleştik:

  • izleme için m5.4xlarge (Prometheus)
  • Node.js iş yükü için c5.4xlarge (tek iş parçacıklı iş yükü)
  • Java ve Go için c5.2xlarge (çok iş parçacıklı iş yükü)
  • Kontrol düzlemi için c5.4xlarge (3 düğüm)

Göç

Eski altyapımızdan Kubernetes'e geçiş için hazırlık adımlarından biri, mevcut servis-servis iletişimini, belirli bir Sanal Özel Bulut (VPC) alt ağında oluşturulan yeni Elastik Yük Dengeleyicilere (ELB) işaret edecek şekilde değiştirmekti. Bu alt ağ Kubernetes VPC'sine bakıldı. Bu, servis bağımlılıkları için belirli bir sıralamaya bakılmaksızın modülleri ayrıntılı bir şekilde taşımamıza izin verdi.

Bu uç noktalar, her yeni ELB'ye işaret eden CNAME içeren ağırlıklı DNS kayıt kümeleri kullanılarak oluşturuldu. Kesmek için, yeni Kubernetes hizmeti ELB'ye 0 ağırlığında işaret eden yeni bir kayıt ekledik. Daha sonra kayıttaki Canlı Yaşam Süresi'ni (TTL) 0 olarak ayarladık. Daha sonra eski ve yeni ağırlıklar yavaşça sonunda yeni sunucuda% 100 ile sonuçlanır. Kesim tamamlandıktan sonra, TTL daha makul bir şeye ayarlandı.

Java modüllerimiz düşük DNS TTL'yi onurlandırdı, ancak Düğüm uygulamalarımız yapmadı. Mühendislerimizden biri, bağlantı havuzu kodunun bir kısmını, havuzları 60'larda bir yenileyecek bir yöneticiye sarmak için yeniden yazdı. Bu kayda değer bir performans isabeti olmadan bizim için çok iyi çalıştı.

Eğitimler

Ağ Yapı Sınırları

8 Ocak 2019 sabahın erken saatlerinde Tinder Platformu kalıcı bir kesinti yaşadı. O sabahın erken saatlerinde platform gecikmesindeki ilişkisiz artışa yanıt olarak, küme üzerinde kapsül ve düğüm sayıları ölçeklendirildi. Bu, tüm düğümlerimizde ARP önbellek tükenmesine neden oldu.

ARP önbelleğiyle ilgili üç Linux değeri vardır:

Kredi

gc_thresh3 sabit bir başlıktır. “Komşu tablo taşması” günlük girişleri alıyorsanız, bu, ARP önbelleğinin eşzamanlı bir çöp toplama işleminden (GC) sonra bile, komşu girişini depolamak için yeterli alan olmadığını gösterir. Bu durumda, çekirdek paketi tamamen bırakır.

Flannel'i Kubernetes'teki ağ yapısı olarak kullanıyoruz. Paketler VXLAN üzerinden iletilir. VXLAN, Katman 3 ağı üzerinden Katman 2 katman şemasıdır. Katman 2 ağ segmentlerini genişletmek için bir yol sağlamak üzere MAC Adresi Kullanıcı İçi Datagram Protokolü (UDP içinde MAC) kapsülleme kullanır. Fiziksel veri merkezi ağı üzerindeki aktarım protokolü IP artı UDP'dir.

Şekil 2–1 Pazen diyagramı (kredi)

Şekil 2–2 VXLAN Paketi (kredi)

Her Kubernetes çalışan düğümü kendi / 24 sanal adres alanını daha büyük / 9 bloktan ayırır. Her düğüm için bu, 1 yol tablosu girişi, 1 ARP tablosu girişi (pazen.1 arabiriminde) ve 1 yönlendirme veritabanı (FDB) girişi ile sonuçlanır. Bunlar, çalışan düğüm ilk kez başlatıldığında veya her yeni düğüm keşfedildiğinde eklenir.

Ek olarak, düğüm-pod (veya kapsül-pod) iletişimi nihayetinde eth0 arabirimi üzerinden akar (yukarıdaki Flanel diyagramında gösterilmiştir). Bu, karşılık gelen her düğüm kaynağı ve düğüm hedefi için ARP tablosunda ek bir girişe neden olacaktır.

Çevremizde bu tür iletişim çok yaygındır. Kubernetes hizmet nesnelerimiz için bir ELB oluşturulur ve Kubernetes her düğümü ELB'ye kaydeder. ELB kapsülden haberdar değildir ve seçilen düğüm paketin son varış yeri olmayabilir. Bunun nedeni, düğümün ELB'den paketi aldığı zaman, hizmet için iptables kurallarını değerlendirmesi ve başka bir düğümde rastgele bir bölme seçmesidir.

Kesinti sırasında, kümede toplam 605 düğüm vardı. Yukarıda belirtilen nedenlerden dolayı, bu varsayılan gc_thresh3 değerini korumak için yeterliydi. Bu gerçekleştiğinde, yalnızca paketler düşürülmez, aynı zamanda sanal adres alanının tüm Pazen / 24'leri ARP tablosunda eksik olur. Düğümden kapsüle iletişim ve DNS aramaları başarısız. (DNS, bu makalenin ilerleyen kısımlarında daha ayrıntılı olarak açıklanacağı gibi küme içinde barındırılmaktadır.)

Çözümlemek için, gc_thresh1, gc_thresh2 ve gc_thresh3 değerleri yükseltilir ve eksik ağların yeniden kaydedilmesi için Flannel yeniden başlatılmalıdır.

DNS'yi Beklenmedik Şekilde Çalıştırma

Taşımamızı kolaylaştırmak için, hizmetlerimiz için eski ve Kubernetes'e trafik şekillendirmesini ve artımlı kesmeyi kolaylaştırmak için DNS'yi yoğun bir şekilde kullandık. İlişkili Route53 RecordSets üzerinde nispeten düşük TTL değerleri belirledik. Eski altyapımızı EC2 bulut sunucularında çalıştırdığımızda, çözümleyici yapılandırmamız Amazon'un DNS'sine işaret etti. Bunu kabul ettik ve hizmetlerimiz ve Amazon'un hizmetleri (ör. DynamoDB) için nispeten düşük bir TTL maliyeti büyük ölçüde fark edilmedi.

Kubernetes'e giderek daha fazla hizmet sunarken, kendimizi saniyede 250.000 istek yanıtlayan bir DNS hizmeti çalıştırırken bulduk. Uygulamalarımızda aralıklı ve etkili DNS arama zaman aşımlarıyla karşılaşıyorduk. Bu, kapsamlı bir ayarlama çabasına ve bir DNS sağlayıcısının, bir kerede 120 çekirdeği tüketen 1.000 kapsüle ulaştığı bir CoreDNS dağıtımına geçmesine rağmen meydana geldi.

Diğer olası nedenleri ve çözümleri araştırırken, Linux paket filtreleme çerçevesi net filtresini etkileyen bir yarış durumunu açıklayan bir makale bulduk. Gördüğümüz DNS zaman aşımlarının yanı sıra, Pazen arayüzünde artmış bir insert_failed sayacıyla birlikte makalenin bulgularıyla aynı hizaya geldi.

Sorun Kaynak ve Hedef Ağ Adresi Çevirisi (SNAT ve DNAT) sırasında ve sonradan bağlantı tablosuna eklenmesi sırasında oluşur. Dahili olarak tartışılan ve topluluk tarafından önerilen bir çözüm, DNS'yi çalışan düğümün kendisine taşımaktı. Bu durumda:

  • Trafik zorunlu olarak düğümde kaldığı için SNAT gerekli değildir. Eth0 arayüzü üzerinden iletilmesine gerek yoktur.
  • Hedef IP, düğüm için yerel olduğundan ve iptables kuralları başına rastgele seçilen bir kapsül olmadığından DNAT gerekli değildir.

Bu yaklaşımla ilerlemeye karar verdik. CoreDNS, Kubernetes'te DaemonSet olarak konuşlandırıldı ve kubelet - cluster-dns komut bayrağını yapılandırarak düğümün yerel DNS sunucusunu her kapsülün resolv.conf dosyasına enjekte ettik. Geçici çözüm, DNS zaman aşımları için etkili oldu.

Bununla birlikte, bırakılan paketleri ve Flannel arayüzünün insert_failed sayaç artışını görüyoruz. Bu, yukarıdaki geçici çözümden sonra bile devam edecektir, çünkü DNS trafiği için yalnızca SNAT ve / veya DNAT'ı önledik. Yarış durumu diğer trafik türleri için de geçerli olacaktır. Neyse ki, paketlerimizin çoğu TCP'dir ve koşul oluştuğunda paketler başarıyla yeniden iletilir. Tüm trafik türleri için uzun vadeli bir düzeltme, halen tartıştığımız bir konudur.

Daha İyi Yük Dengeleme Sağlamak için Elçiyi Kullanmak

Arka uç hizmetlerimizi Kubernetes'e geçirirken, baklalar arasındaki dengesiz yükten muzdarip olmaya başladık. HTTP Keepalive nedeniyle, ELB bağlantılarının her bir haddeleme dağıtımının ilk hazır kapsüllerine yapıştığını keşfettik, bu nedenle çoğu trafik mevcut kapsüllerin küçük bir yüzdesi üzerinden aktı. Denediğimiz ilk azaltmalardan biri, en kötü suçlular için yeni dağıtımlarda% 100 MaxSurge kullanmaktı. Bu, bazı büyük dağıtımlarda marjinal olarak etkili ve uzun vadede sürdürülebilir değildi.

Kullandığımız bir diğer azaltıcı etken, kritik hizmetler üzerindeki kaynak isteklerini yapay olarak şişirmekti, böylece konumlandırılmış kapsüller diğer ağır kapsüller ile birlikte daha fazla boş alana sahip olacaktı. Bu aynı zamanda kaynak israfı nedeniyle uzun vadede makul olmayacaktı ve Düğüm uygulamalarımız tek iş parçacıklıydı ve böylece 1 çekirdekte etkili bir şekilde kapatıldı. Açık olan tek çözüm daha iyi yük dengeleme kullanmaktı.

Dahili olarak Envoy'u değerlendirmek istiyorduk. Bu bize çok sınırlı bir şekilde yerleştirme ve anında fayda sağlama şansı verdi. Envoy, büyük hizmet odaklı mimariler için tasarlanmış açık kaynaklı, yüksek performanslı bir Katman 7 proxy'dir. Otomatik denemeler, devre kesme ve küresel hız sınırlama dahil olmak üzere gelişmiş yük dengeleme tekniklerini uygulayabilir.

Geldiğimiz konfigürasyon, yerel konteyner limanına çarpmak için bir rota ve kümeye sahip her bölmenin yanında bir Envoy sepetinin olmasıydı. Potansiyel basamaklamayı en aza indirmek ve küçük bir patlama yarıçapını korumak için, her bir hizmet için her Kullanılabilirlik Alanında (AZ) bir dağıtım olan bir ön proxy Elçisi bölmeleri filosu kullandık. Bunlar, mühendislerimizden birinin belirli bir hizmet için her AZ'de bir kapsül listesi döndüren küçük bir servis keşif mekanizmasına çarptı.

Hizmetin ön Elçileri daha sonra bu hizmet bulma mekanizmasını bir yukarı akış kümesi ve rotayla kullandılar. Makul zaman aşımlarını yapılandırdık, tüm devre kesici ayarlarını artırdık ve daha sonra geçici arızalara ve sorunsuz dağıtımlara yardımcı olmak için minimum yeniden deneme yapılandırması yaptık. Bu ön Elçi hizmetlerinin her birini bir TCP ELB ile sınırladık. Ana ön proxy katmanımızdaki kalıcılık, belirli Envoy bölmelerine sabitlenmiş olsa bile, yükü çok daha iyi idare edebildiler ve arka uçtaki en az_ talep ile dengelenecek şekilde yapılandırıldılar.

Dağıtımlar için hem uygulamada hem de sepet bölmesinde bir preStop kanca kullandık. Sepet sağlık kontrolü denilen bu kanca, küçük bir uyku ile birlikte, inflight bağlantılarının tamamlanmasına ve boşalmasına izin vermek için biraz zaman vermek için yönetici uç noktası başarısız.

Bu kadar hızlı hareket edebilmemizin bir nedeni, normal Prometheus kurulumumuza kolayca entegre edebildiğimiz zengin metriklerdi. Bu, yapılandırma ayarlarında tekrarladığımız ve trafiği kestiğimizde ne olduğunu tam olarak görmemizi sağladı.

Sonuçlar anında ve açıktı. En dengesiz hizmetlerle başladık ve bu noktada kümemizdeki en önemli oniki hizmetin önünde çalışıyoruz. Bu yıl, daha gelişmiş hizmet keşfi, devre kesilmesi, aykırı algılama, hız sınırlaması ve izlemeyle tam hizmet ağına geçmeyi planlıyoruz.

Şekil 3–1 Kesme ile elçi arasında bir hizmetin CPU yakınsaması

Sonuç

Bu öğrenmeler ve ek araştırmalar sayesinde, büyük Kubernetes kümelerinin nasıl tasarlanacağı, dağıtılacağı ve işletileceği konusunda büyük bilgi sahibi olan güçlü bir şirket içi altyapı ekibi geliştirdik. Tinder'ın tüm mühendislik organizasyonu, uygulamalarını Kubernetes'te nasıl kaplayacakları ve dağıtacakları konusunda bilgi ve deneyime sahiptir.

Eski altyapımızda, ek ölçek gerektiğinde, yeni EC2 bulut sunucularının çevrimiçi olmasını beklemek için birkaç dakika bekledik. Kapsayıcılar artık dakikaların aksine saniyeler içinde trafiği planlar ve sunar. Tek bir EC2 örneğinde birden fazla kabın programlanması, gelişmiş yatay yoğunluk da sağlar. Sonuç olarak, 2019 yılında bir önceki yıla göre EC2 üzerinde önemli maliyet tasarrufu öngörüyoruz.

Neredeyse iki yıl sürdü, ancak göçümüzü Mart 2019'da sonlandırdık. Tinder Platformu, yalnızca 200 hizmet, 1.000 düğüm, 15.000 kapsül ve 48.000 çalışan konteynırdan oluşan bir Kubernetes kümesinde çalışıyor. Altyapı artık operasyon ekiplerimiz için ayrılmış bir görev değil. Bunun yerine, kuruluştaki mühendisler bu sorumluluğu paylaşır ve uygulamalarının kod olarak her şeyle nasıl oluşturulduğunu ve konuşlandırıldığını kontrol eder.