Yandex.Cloud'da ağ yük dengeleyicisinin mimarisi

Yandex.Cloud'da ağ yük dengeleyicisinin mimarisi
Merhaba ben Sergey Elantsev, geliştiriyorum ağ yük dengeleyici Yandex.Cloud'da. Daha önce, Yandex portalı için L7 dengeleyicinin geliştirilmesine liderlik ediyordum - meslektaşlarım ne yaparsam yapayım bunun bir dengeleyici olduğu konusunda şaka yapıyordu. Habr okuyucularına bulut platformundaki yükü nasıl yöneteceklerini, bu hedefe ulaşmak için ideal araç olarak neyi gördüğümüzü ve bu aracı oluşturma yolunda nasıl ilerlediğimizi anlatacağım.

Öncelikle bazı terimleri tanıtalım:

  • VIP (Sanal IP) - dengeleyici IP adresi
  • Sunucu, arka uç, örnek - bir uygulamayı çalıştıran sanal makine
  • RIP (Gerçek IP) - sunucu IP adresi
  • Healthcheck - sunucunun hazır olup olmadığını kontrol etme
  • Erişilebilirlik Alanı, A'dan Z'ye - bir veri merkezinde yalıtılmış altyapı
  • Bölge - farklı AZ'lerin birleşimi

Yük dengeleyiciler üç ana görevi çözer: dengelemeyi kendisi gerçekleştirir, hizmetin hata toleransını artırır ve ölçeklendirmeyi basitleştirir. Otomatik trafik yönetimi yoluyla hata toleransı sağlanır: dengeleyici, uygulamanın durumunu izler ve canlılık kontrolünü geçemeyen örnekleri dengelemenin dışında bırakır. Ölçeklendirme, yükün örnekler arasında eşit şekilde dağıtılmasının yanı sıra bulut sunucularının listesinin anında güncellenmesiyle sağlanır. Dengeleme yeterince tekdüze değilse bazı bulut sunucuları kapasite sınırlarını aşan bir yük alacak ve hizmet daha az güvenilir hale gelecektir.

Bir yük dengeleyici genellikle üzerinde çalıştığı OSI modelindeki protokol katmanına göre sınıflandırılır. Cloud Balancer, dördüncü katman olan L4'e karşılık gelen TCP düzeyinde çalışır.

Bulut dengeleyici mimarisine genel bir bakışa geçelim. Detay seviyesini kademeli olarak artıracağız. Dengeleyici bileşenlerini üç sınıfa ayırıyoruz. Yapılandırma düzlemi sınıfı, kullanıcı etkileşiminden sorumludur ve sistemin hedef durumunu saklar. Kontrol düzlemi, sistemin mevcut durumunu saklar ve trafiği istemcilerden örneklerinize iletmekten doğrudan sorumlu olan veri düzlemi sınıfındaki sistemleri yönetir.

Veri düzlemi

Trafik, sınır yönlendiricileri adı verilen pahalı cihazlara ulaşıyor. Arıza toleransını artırmak için, bu tür birkaç cihaz tek bir veri merkezinde aynı anda çalışır. Daha sonra trafik, istemciler için BGP aracılığıyla tüm AZ'lere herhangi bir yayın IP adresini duyuran dengeleyicilere gider. 

Yandex.Cloud'da ağ yük dengeleyicisinin mimarisi

Trafik ECMP üzerinden iletilir - bu, hedefe giden eşit derecede iyi birkaç rotanın olabildiği (bizim durumumuzda hedef, hedef IP adresi olacaktır) ve paketlerin bunlardan herhangi biri boyunca gönderilebildiği bir yönlendirme stratejisidir. Ayrıca aşağıdaki şemaya göre çeşitli kullanılabilirlik bölgelerinde çalışmayı da destekliyoruz: her bölgede bir adresin reklamını yapıyoruz, trafik en yakın olana gidiyor ve sınırlarını aşmıyor. Gönderinin ilerleyen kısımlarında trafiğe ne olduğuna daha ayrıntılı olarak bakacağız.

Yapılandırma düzlemi

 
Yapılandırma düzleminin temel bileşeni, dengeleyicilerle temel işlemlerin gerçekleştirildiği API'dir: örneklerin oluşturulması, silinmesi, kompozisyonunun değiştirilmesi, durum kontrol sonuçlarının alınması vb. Bir yandan bu bir REST API'sidir ve diğer yandan Diğer yandan, Bulut'ta biz sıklıkla gRPC çerçevesini kullanırız, bu nedenle REST'i gRPC'ye "çeviririz" ve ardından yalnızca gRPC'yi kullanırız. Herhangi bir istek, ortak bir Yandex.Cloud çalışanları havuzunda yürütülen bir dizi eş zamanlı olmayan eş zamanlı görevin oluşturulmasına yol açar. Görevler, istenildiği zaman askıya alınabilecek ve daha sonra yeniden başlatılabilecek şekilde yazılmıştır. Bu, operasyonların ölçeklenebilirliğini, tekrarlanabilirliğini ve günlüğe kaydedilmesini sağlar.

Yandex.Cloud'da ağ yük dengeleyicisinin mimarisi

Sonuç olarak, API'den gelen görev, Go'da yazılan dengeleyici hizmet denetleyicisine bir istekte bulunacaktır. Dengeleyicileri ekleyip kaldırabilir, arka uçların ve ayarların kompozisyonunu değiştirebilir. 

Yandex.Cloud'da ağ yük dengeleyicisinin mimarisi

Hizmet, durumunu yakında kullanabileceğiniz dağıtılmış yönetilen bir veritabanı olan Yandex Veritabanında saklar. Zaten yaptığımız gibi Yandex.Cloud'da söyledi, köpek maması konsepti geçerlidir: Hizmetlerimizi kendimiz kullanırsak, müşterilerimiz de bunları kullanmaktan mutluluk duyacaktır. Yandex Veritabanı böyle bir konseptin uygulanmasına bir örnektir. Tüm verilerimizi YDB'de saklıyoruz ve veritabanının bakımını ve ölçeklendirilmesini düşünmemize gerek kalmıyor: bu sorunlar bizim için çözüldü, veritabanını bir hizmet olarak kullanıyoruz.

Dengeleyici kontrolörüne dönelim. Görevi, dengeleyici hakkındaki bilgileri kaydetmek ve sanal makinenin hazır olup olmadığını kontrol etmek için sağlık kontrolü denetleyicisine bir görev göndermektir.

Durum kontrolü denetleyicisi

Denetim kurallarını değiştirme isteklerini alır, bunları YDB'ye kaydeder, görevleri sağlık denetimi düğümleri arasında dağıtır ve sonuçları toplar; bunlar daha sonra veritabanına kaydedilir ve yük dengeleyici denetleyicisine gönderilir. O da aşağıda tartışacağım yük dengeleyici düğüme veri düzlemindeki kümenin kompozisyonunu değiştirme isteği gönderir.

Yandex.Cloud'da ağ yük dengeleyicisinin mimarisi

Sağlık kontrolleri hakkında daha fazla konuşalım. Birkaç sınıfa ayrılabilirler. Denetimlerin farklı başarı kriterleri vardır. TCP kontrollerinin sabit bir süre içinde başarılı bir şekilde bağlantı kurması gerekir. HTTP kontrolleri hem başarılı bir bağlantı hem de 200 durum koduyla bir yanıt gerektirir.

Ayrıca, kontroller eylem sınıfına göre farklılık gösterir - aktif ve pasiftirler. Pasif kontroller herhangi bir özel işlem yapmadan trafikte olup bitenleri izler. Bu, L4'te pek işe yaramıyor çünkü üst düzey protokollerin mantığına bağlı: L4'te işlemin ne kadar sürdüğü veya bağlantının tamamlanmasının iyi mi yoksa kötü mü olduğu hakkında hiçbir bilgi yok. Aktif kontroller, dengeleyicinin her sunucu örneğine istek göndermesini gerektirir.

Çoğu yük dengeleyici, canlılık kontrollerini kendileri gerçekleştirir. Cloud olarak ölçeklenebilirliği artırmak için sistemin bu kısımlarını ayırmaya karar verdik. Bu yaklaşım, hizmete yapılan durum denetimi isteklerinin sayısını korurken dengeleyici sayısını artırmamıza olanak tanıyacaktır. Denetimler, denetim hedeflerinin parçalandığı ve çoğaltıldığı ayrı durum denetimi düğümleri tarafından gerçekleştirilir. Başarısız olabileceği için tek bir ana bilgisayardan kontrol yapamazsınız. O zaman kontrol ettiği örneklerin durumunu alamayacağız. En az üç sağlık kontrolü düğümünden gelen örneklerden herhangi biri üzerinde kontroller gerçekleştiririz. Tutarlı karma algoritmaları kullanarak düğümler arasındaki kontrollerin amaçlarını parçalara ayırıyoruz.

Yandex.Cloud'da ağ yük dengeleyicisinin mimarisi

Dengeleme ve sağlık kontrolünü ayırmak sorunlara yol açabilir. Durum denetimi düğümü (şu anda trafiğe hizmet vermeyen) dengeleyiciyi atlayarak örneğe istekte bulunursa, garip bir durum ortaya çıkar: kaynak canlı görünüyor, ancak trafik ona ulaşmayacak. Bu sorunu şu şekilde çözüyoruz: dengeleyiciler aracılığıyla durum kontrolü trafiğini başlatmamız garanti ediliyor. Başka bir deyişle, istemcilerden ve sağlık denetimlerinden gelen trafiği içeren paketleri taşıma şeması minimum düzeyde farklılık gösterir: her iki durumda da paketler, onları hedef kaynaklara teslim edecek olan dengeleyicilere ulaşacaktır.

Aradaki fark, müşterilerin VIP'ye istekte bulunması, sağlık kontrollerinin ise her bir RIP'e istekte bulunmasıdır. Burada ilginç bir sorun ortaya çıkıyor: Kullanıcılarımıza gri IP ağlarında kaynak oluşturma fırsatı veriyoruz. Hizmetlerini dengeleyicilerin arkasına gizleyen iki farklı bulut sahibinin olduğunu düşünelim. Her birinin 10.0.0.1/24 alt ağında aynı adreslere sahip kaynakları vardır. Onları bir şekilde ayırt edebilmeniz gerekiyor ve burada Yandex.Cloud sanal ağının yapısına dalmanız gerekiyor. Daha fazla ayrıntıyı öğrenmek daha iyidir about:cloud olayından videoağın çok katmanlı olması ve alt ağ kimliğiyle ayırt edilebilen tünellere sahip olması artık bizim için önemli.

Sağlık kontrolü düğümleri, sözde IPv6 adreslerini kullanarak dengeleyicilerle iletişim kurar. Yarı adres, içine gömülü bir IPv6 adresi ve kullanıcı alt ağ kimliğine sahip bir IPv4 adresidir. Trafik, IPv4 kaynak adresini alan, IPv6'yı IPv4 ile değiştiren ve paketi kullanıcının ağına gönderen dengeleyiciye ulaşır.

Ters trafik de aynı şekilde gider: dengeleyici, hedefin sağlık denetleyicilerinden gelen gri bir ağ olduğunu görür ve IPv4'ü IPv6'ya dönüştürür.

VPP - veri düzleminin kalbi

Dengeleyici, Cisco'nun ağ trafiğinin toplu işlenmesine yönelik bir çerçevesi olan Vektör Paket İşleme (VPP) teknolojisi kullanılarak uygulanır. Bizim durumumuzda çerçeve, kullanıcı alanı ağ cihazı yönetimi kitaplığı olan Veri Düzlemi Geliştirme Kiti (DPDK) üzerinde çalışır. Bu, yüksek paket işleme performansı sağlar: çekirdekte çok daha az kesinti meydana gelir ve çekirdek alanı ile kullanıcı alanı arasında bağlam geçişi yoktur. 

VPP daha da ileri giderek paketleri gruplar halinde birleştirerek sistemden daha fazla performans elde edilmesini sağlar. Performans kazanımları, modern işlemcilerdeki önbelleklerin agresif kullanımından kaynaklanmaktadır. Hem veri önbellekleri (paketler "vektörler" halinde işlenir, veriler birbirine yakındır) hem de talimat önbellekleri kullanılır: VPP'de paket işleme, düğümleri aynı görevi gerçekleştiren işlevleri içeren bir grafiği takip eder.

Örneğin, IP paketlerinin VPP'de işlenmesi şu sırayla gerçekleşir: önce paket başlıkları ayrıştırma düğümünde ayrıştırılır ve ardından paketleri yönlendirme tablolarına göre daha da ileten düğüme gönderilir.

Biraz sert. VPP'nin yazarları, işlemci önbelleklerinin kullanımındaki tavizlere müsamaha göstermezler, bu nedenle bir paket vektörünü işlemek için kullanılan tipik kod, manuel vektörleştirmeyi içerir: "kuyrukta dört paketimiz var" gibi bir durumun işlendiği bir işleme döngüsü vardır, sonra iki için aynı, sonra - bir için. Önceden getirme talimatları genellikle sonraki yinelemelerde verilere erişimi hızlandırmak amacıyla verileri önbelleklere yüklemek için kullanılır.

n_left_from = frame->n_vectors;
while (n_left_from > 0)
{
    vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
    // ...
    while (n_left_from >= 4 && n_left_to_next >= 2)
    {
        // processing multiple packets at once
        u32 next0 = SAMPLE_NEXT_INTERFACE_OUTPUT;
        u32 next1 = SAMPLE_NEXT_INTERFACE_OUTPUT;
        // ...
        /* Prefetch next iteration. */
        {
            vlib_buffer_t *p2, *p3;

            p2 = vlib_get_buffer (vm, from[2]);
            p3 = vlib_get_buffer (vm, from[3]);

            vlib_prefetch_buffer_header (p2, LOAD);
            vlib_prefetch_buffer_header (p3, LOAD);

            CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
            CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
        }
        // actually process data
        /* verify speculative enqueues, maybe switch current next frame */
        vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
                to_next, n_left_to_next,
                bi0, bi1, next0, next1);
    }

    while (n_left_from > 0 && n_left_to_next > 0)
    {
        // processing packets by one
    }

    // processed batch
    vlib_put_next_frame (vm, node, next_index, n_left_to_next);
}

Dolayısıyla Healthcheck'ler IPv6 üzerinden VPP ile konuşur ve bu da onları IPv4'e dönüştürür. Bu, algoritmik NAT dediğimiz grafikteki bir düğüm tarafından yapılır. Ters trafik (ve IPv6'dan IPv4'e dönüşüm) için aynı algoritmik NAT düğümü vardır.

Yandex.Cloud'da ağ yük dengeleyicisinin mimarisi

Dengeleyici istemcilerinden gelen doğrudan trafik, dengelemeyi kendisi gerçekleştiren grafik düğümlerinden geçer. 

Yandex.Cloud'da ağ yük dengeleyicisinin mimarisi

İlk düğüm yapışkan oturumlardır. Karmasını saklar 5 demet yerleşik oturumlar için. 5-demet, bilginin iletildiği istemcinin adresini ve bağlantı noktasını, trafiği almak için kullanılabilen kaynakların adresini ve bağlantı noktalarını ve ayrıca ağ protokolünü içerir. 

5'li karma, sonraki tutarlı karma düğümünde daha az hesaplama yapmamızın yanı sıra dengeleyicinin arkasındaki kaynak listesi değişikliklerini daha iyi ele almamıza yardımcı olur. Oturumu olmayan bir paket dengeleyiciye ulaştığında tutarlı karma düğümüne gönderilir. Tutarlı karma kullanılarak dengelemenin gerçekleştiği yer burasıdır: mevcut "canlı" kaynaklar listesinden bir kaynak seçiyoruz. Daha sonra paketler, aslında hedef adresin yerini alan ve sağlama toplamlarını yeniden hesaplayan NAT düğümüne gönderilir. Gördüğünüz gibi, işlemci önbelleklerinin verimliliğini artırmak için benzer hesaplamaları gruplandırarak VPP kurallarını takip ediyoruz.

Tutarlı karma

Neden onu seçtik ve hatta nedir? Öncelikle önceki görevi ele alalım - listeden bir kaynak seçmek. 

Yandex.Cloud'da ağ yük dengeleyicisinin mimarisi

Tutarsız hashing ile gelen paketin hash'i hesaplanır ve bu hash'in kaynak sayısına bölünmesiyle kalan kısımla listeden bir kaynak seçilir. Liste değişmediği sürece bu şema iyi çalışır: paketleri her zaman aynı 5'li tuple ile aynı örneğe göndeririz. Örneğin, bazı kaynaklar sağlık kontrollerine yanıt vermeyi durdurursa, karmaların önemli bir kısmı için seçim değişecektir. İstemcinin TCP bağlantıları kesilecektir: daha önce A örneğine ulaşan bir paket, bu paketin oturumuna aşina olmayan B örneğine ulaşmaya başlayabilir.

Tutarlı karma, açıklanan sorunu çözer. Bu kavramı açıklamanın en kolay yolu şudur: Kaynakları karma yoluyla (örneğin, IP:port ile) dağıttığınız bir halkanız olduğunu hayal edin. Bir kaynağın seçilmesi, tekerleğin paketin karması tarafından belirlenen bir açıyla döndürülmesidir.

Yandex.Cloud'da ağ yük dengeleyicisinin mimarisi

Bu, kaynakların bileşimi değiştiğinde trafiğin yeniden dağıtımını en aza indirir. Bir kaynağın silinmesi, tutarlı karma halkasının yalnızca kaynağın bulunduğu kısmını etkileyecektir. Kaynak eklemek aynı zamanda dağıtımı da değiştirir, ancak sabit bir oturum düğümümüz var, bu da önceden oluşturulmuş oturumları yeni kaynaklara geçirmememizi sağlar.

Dengeleyici ile kaynaklar arasındaki trafiği yönlendirmeye ne olduğuna baktık. Şimdi dönüş trafiğine bakalım. Algoritmik NAT aracılığıyla, yani istemci trafiği için ters NAT 44 aracılığıyla ve durum denetimleri trafiği için NAT 46 aracılığıyla, kontrol trafiğiyle aynı modeli izler. Kendi planımıza uyuyoruz: sağlık kontrolü trafiğini ve gerçek kullanıcı trafiğini birleştiriyoruz.

Yük dengeleyici düğümü ve birleştirilmiş bileşenler

VPP'deki dengeleyicilerin ve kaynakların bileşimi, yerel hizmet - yük dengeleyici düğümü tarafından rapor edilir. Yük dengeleyici-denetleyiciden gelen olay akışına abone olur ve mevcut VPP durumu ile denetleyiciden alınan hedef durum arasındaki farkı çizebilir. Kapalı bir sistem elde ediyoruz: API'den gelen olaylar, kaynakların "canlılığını" kontrol etmek için sağlık kontrolü denetleyicisine görevler atayan dengeleyici denetleyicisine gelir. Bu da, görevleri sağlık kontrolü düğümüne atar ve sonuçları toplar, ardından bunları dengeleyici denetleyiciye geri gönderir. Yük dengeleyici düğümü denetleyiciden gelen olaylara abone olur ve VPP'nin durumunu değiştirir. Böyle bir sistemde her hizmet, komşu hizmetler hakkında yalnızca neyin gerekli olduğunu bilir. Bağlantı sayısı sınırlıdır ve farklı segmentleri bağımsız olarak çalıştırma ve ölçeklendirme olanağına sahibiz.

Yandex.Cloud'da ağ yük dengeleyicisinin mimarisi

Hangi sorunlardan kaçınıldı?

Kontrol düzlemindeki tüm hizmetlerimiz Go'da yazılmıştır ve iyi ölçeklendirme ve güvenilirlik özelliklerine sahiptir. Go'nun dağıtılmış sistemler oluşturmak için birçok açık kaynak kütüphanesi vardır. GRPC'yi aktif olarak kullanıyoruz, tüm bileşenler hizmet keşfinin açık kaynaklı bir uygulamasını içeriyor; hizmetlerimiz birbirlerinin performansını izliyor, kompozisyonlarını dinamik olarak değiştirebiliyor ve bunu GRPC dengelemeyle ilişkilendirdik. Metrikler için ayrıca açık kaynaklı bir çözüm kullanıyoruz. Veri düzleminde iyi bir performans ve büyük bir kaynak rezervi elde ettik: demir ağ kartı yerine VPP'nin performansına güvenebileceğimiz bir stand kurmanın çok zor olduğu ortaya çıktı.

Sorunlar ve çözümleri

Ne bu kadar iyi çalışmadı? Go'nun otomatik bellek yönetimi vardır ancak bellek sızıntıları yine de meydana gelir. Onlarla başa çıkmanın en kolay yolu goroutinleri çalıştırmak ve onları sonlandırmayı unutmamaktır. Paket servis: Go programlarınızın bellek tüketimini izleyin. Genellikle iyi bir gösterge goroutinlerin sayısıdır. Bu hikayenin bir artısı var: Go'da çalışma zamanı verilerini (bellek tüketimi, çalışan goroutinlerin sayısı ve diğer birçok parametre) almak kolaydır.

Ayrıca Go, işlevsel testler için en iyi seçim olmayabilir. Oldukça ayrıntılıdırlar ve "CI'daki her şeyi toplu olarak çalıştırmak" şeklindeki standart yaklaşım onlar için pek uygun değildir. Gerçek şu ki, işlevsel testler daha fazla kaynak tüketiyor ve gerçek zaman aşımlarına neden oluyor. Bu nedenle, CPU birim testleriyle meşgul olduğundan testler başarısız olabilir. Sonuç: Mümkünse “ağır” testleri birim testlerden ayrı olarak yapın. 

Mikro hizmet olay mimarisi tek parçadan daha karmaşıktır: düzinelerce farklı makinede günlük toplamak pek kullanışlı değildir. Sonuç: Mikro hizmetler yapıyorsanız hemen izlemeyi düşünün.

Planlarımız

Dahili bir dengeleyici, bir IPv6 dengeleyici başlatacağız, Kubernetes komut dosyaları için destek ekleyeceğiz, hizmetlerimizi parçalamaya devam edeceğiz (şu anda yalnızca healthcheck-node ve healthcheck-ctrl parçalanmıştır), yeni sağlık kontrolleri ekleyeceğiz ve ayrıca kontrollerin akıllı toplanmasını uygulayacağız. Hizmetlerimizi daha da bağımsız hale getirme olasılığını düşünüyoruz; böylece birbirleriyle doğrudan değil, bir mesaj kuyruğu kullanarak iletişim kurabilirler. Yakın zamanda Bulutta SQS uyumlu bir hizmet ortaya çıktı Yandex Mesaj Sırası.

Son zamanlarda Yandex Load Balancer'ın halka açık sürümü gerçekleşti. Keşfetmek belgeleme dengeleyicileri size uygun bir şekilde yönetin ve projelerinizin hata toleransını artırın!

Kaynak: habr.com

Yorum ekle