Büyük ölçekli bir Web arama motoru mimarisi nasıl başarıyla oluşturulur?

Web arama motoru çok karmaşıktır Ürünümüz, performans ve gecikme açısından çok zorlu gereksinimleri olan dağıtılmış bir sistemdir. Ayrıca bu sistemin işleyişi de çok pahalıdır, çok fazla insan gücü ve tabii ki çok para gerektirir.

Bu makale, kullandığımız bazı teknoloji yığınlarını ve aldığımız bazı seçimler ve kararları inceleyecektir.

Yazar | Cliqz

Çevirmen | Crescent Moon, sorumlu editör | Guo Rui

Üretildi | CSDN (ID: CSDNnews)

Aşağıdaki çeviridir:

Bu yazıda, harici ve dahili kullanıcıları memnun etmek için yıllarca yinelendikten sonra özel arama ürünlerimizi sistematik olarak tanıtacağız.

Birçok tanınmış açık kaynak teknolojisinin ve bulut yerel teknolojisinin bir kombinasyonunu kullanıyoruz ve bu teknolojiler zorlu testlerden geçmiştir. Açık kaynak kodlu veya ticari sistemlerden çözüm bulamayan alanlar için ancak derinlemesine araştırma yapıp sistemi sıfırdan yazabiliriz. Bu yöntem, mevcut ölçeğimize çok uygundur.

Sorumluluk Reddi: Bu makale yalnızca sistemin mevcut durumunu açıklar. Tabii ki, orijinal sistemde durum böyle değildi. Yıllar içinde, çeşitli mimarileri benimsedik ve maliyet, trafik ve veri boyutu gibi kısıtlamaları dikkate almaya devam ediyoruz. Ancak bu makale bir arama motoru oluşturmak için bir rehber değil, sadece şu anda kullandığımız sistemdir. Gartner bir keresinde şöyle demişti:

"Erken optimizasyon, tüm kötülüklerin köküdür."

Bu cümleye tamamen katılıyoruz. Herkese içtenlikle tüm malzemeleri tencereye atmamalarını tavsiye ederiz. Ancak bunları birer birer koymak zorunda değilsiniz, ancak adım adım karmaşıklığı artırarak.

Arama motoru deneyimi açılır menüsü ve SERP

Cliqz'in arama motorunun iki tür müşterisi vardır, farklı ihtiyaçları vardır.

Arama ipuçları

Tarayıcıdaki Cliqz açılır menüsü

Tarayıcının adres çubuğunda arama yapabilirsiniz ve arama sonuçları bir açılır menüde görüntülenir. Bu tür bir arama çok az sonuç gerektirir (genellikle 3), ancak gecikme gereksinimleri çok zordur (genellikle 150 milisaniye içinde), aksi takdirde kullanıcı deneyimini etkileyecektir.

SERP'de ara

Cliqz arama motoru sonuç sayfası beta.cliqz.com

Web'de bir arama yapın ve iyi bilinen bir arama sonucu sayfası görüntüleyin. Burada, aramanın derinliği sınırsızdır, ancak açılır menü ile karşılaştırıldığında, gecikme için daha düşük gereksinimleri vardır (yalnızca 1000 milisaniye içinde).

Tam otomatik ve neredeyse gerçek zamanlı arama

"Bayern Münih" gibi bir sorgu düşünün. Bu sorgu çok yaygın görünüyor, ancak sistemimizdeki birkaç hizmeti kullanacak. Bu sorgunun amacını düşünürseniz, kullanıcıların şunları isteyebileceğini göreceksiniz:

Bayern Münih kulübünü inceleyin (bu durumda Wikipedia'da küçük bir pencere görüntülemek faydalı olabilir)

  • Bilet rezervasyonu yapmak, alışveriş yapmak veya resmi hayran olarak kaydolmak istiyorsanız (resmi web sitesini görüntüleyin)

  • Kulüp ile ilgili haberleri öğrenmek ister misiniz?

  • Maçtan önceki oyunla ilgili haberler

  • Gerçek zamanlı skorlar, gerçek zamanlı güncellemeler veya yorumlar gibi oyundaki bilgiler

  • Maçtan sonra analiz

  • Kulübün iç durumu, transfer dönemindeki aktiviteler, yeni antrenörlerin işe alınması gibi sezon sonrası bilgiler.

  • Eski web sayfalarını ve içeriğini, kulüp geçmişini, geçmiş maç kayıtlarını vb. Arayın.

Bu niyetlerin "ilgili sayfalar" tarafından özetlenmekten çok uzak olduğunu fark edebilirsiniz. Bu bilgi sadece anlamsal olarak değil, aynı zamanda zamanla da ilgilidir. Aramanın zaman hassasiyeti, kullanıcı deneyimi için çok önemlidir.

Makul bir kullanıcı deneyimi sağlamak için, bu bilgiler farklı bilgi kaynakları tarafından sağlanmalı ve neredeyse gerçek zamanlı bir şekilde aranabilir bir dizine dönüştürülmelidir. Tüm modellerin, dizinlerin ve ilgili dosyaların güncel olduğundan emin olmamız gerekir (örneğin, yüklenen görüntü mevcut olayı yansıtmalı ve başlık ve içerik devam eden olaya göre herhangi bir zamanda güncellenmelidir). Büyük ölçekli koşullarda, tüm bunlar zor görünse de, her zaman en son bilgileri kullanıcılara iletmemiz gerektiği konusunda ısrar ediyoruz. Bu konsept, tüm sistem mimarimizin temelini oluşturur.

Cliqz'in veri işleme ve hizmet platformu, çok katmanlı bir Lambda mimarisi kullanır. Mimari, içerik indekslemenin yakınlığına göre üç katmana ayrılmıştır, yani:

Gerçek zamanlıya yakın dizin

  • Tam otomatik, Kafka (üretici, tüketici ve akış işlemcisi), Cassandra, Granne ve RocksDB tarafından sağlanır

  • Cassandra indeks bilgilerini birden çok tabloda depolar. Farklı tablolardaki kayıtların farklı yaşam süresi (TTL) vardır, böylece veri daha sonra yeniden dizine alındığında depolama alanı temizlenebilir.

  • Bu bileşen, aynı zamanda, farklı boyutlardaki hareketli pencerelerdeki eğilimleri bulmaya yardımcı olabilecek eğilimlere veya popülerliğe dayalı sıralamadan da sorumludur. Bu işlev, KafkaStreams tarafından sağlanan akış işleme işlevini kullanır

  • Bu teknolojiler, arama sonuçlarındaki en son içerik, en popüler haberler vb. Dahil olmak üzere ürün özellikleri oluşturur.

Kayar pencereye göre haftalık veya toplu indeks

  • Son 60 güne ait içeriğe göre

  • Her hafta dizinleri yeniden oluşturun (Jenkins üzerinde uçtan-uca otomatik ardışık düzenindeki toplu işleri kullanarak)

  • Arama sonuçlarının kalitesini iyileştirmek için en son verilere dayalı olarak makine öğrenimi ve veri hattı gerçekleştirin

  • Yeni makine öğrenimi modellerini ve algoritma değişikliklerini test etmek ve prototipler oluşturmak için az miktarda veri kullanmak için iyi bir çerçeveye sahip olun ve tüm veriler üzerinde uçtan uca deneylerin yüksek maliyetinden kaçının

  • Map-Reduce ve Spark'a dayalı toplu iş akışı yönetimi uygulamak için Luigi'yi kullanın ve geçmişe dönük yönetim için Jenkins Pipeline'ı kullanın

  • Keyvi, Cassandra, qpick ve Granne kullanarak hizmet sağlayın

Tam parti indeksi

  • Tüm verilere göre

  • Dizini iki ayda bir yeniden oluşturun

  • Luigi tarafından yönetilen MapReduce ve Spark'a dayalı toplu işleme iş akışı

  • Büyük veri kümelerinde büyük ölçekli makine öğrenimi modellerini eğitmek için kullanılır. Örneğin, sorgu ve kelime gömme, yaklaşık en yakın komşu modeli, dil modeli vb.

  • Keyvi, Cassandra, qpick ve Granne kullanarak hizmet sağlayın

SERP'deki aramayla ilgili içeriğin büyük bir kısmından neredeyse gerçek zamanlı indekslemenin ve haftalık indekslemenin sorumlu olduğunu belirtmekte fayda var. Diğer arama motorları, tarihsel içerikten ziyade bir konunun en son içeriğine daha fazla vurgu yapan benzer bir yaklaşımı benimsemiştir. Toplu iş dizini, zamandan bağımsız sorguları, uzun kuyruklu sorguları ve nadir içerik, tarihsel içerik veya bağlamsal olarak zorlu sorguları işlemekten sorumludur. Bu üçünün kombinasyonu bize yeterli sonuç sağlayabilir, bu nedenle Cliqz arama bugün olduğu şeyi başardı. Tüm sistemler tüm sorguları yanıtlayabilir, ancak nihai sonuç, tüm dizinlerdeki sonuçların bir karışımıdır.

Dağıtım tarihsel bağlam

"Yalnızca bir aracı ne zaman kullanmamanız gerektiğini anladığınızda gerçekten ustalaşabilirsiniz." - Kelsey Hightower

En başından beri, kendi altyapımızı oluşturmak yerine arama hizmetleri sağlamak için bulut hizmeti sağlayıcılarını kullanmaya odaklandık. Son on yılda bulut hizmetleri endüstri standardı haline geldi.Bulut hizmetleri, kendi başına veri merkezleri kurmakla karşılaştırıldığında, karmaşıklık ve kaynak gereksinimleri açısından çok büyük avantajlara sahip ve kullanımı çok uygun. Miktar kadar ödeyebilirsiniz. Bizim için AWS çok kullanışlıdır, kendi makinelerimizi ve altyapımızı yönetmemize gerek yoktur. AWS olmasaydı, şu anda olduğumuz şeyi elde etmek için çok fazla enerji harcamamız gerekirdi. (Bununla birlikte, AWS çok kullanışlı olmasına rağmen aynı zamanda çok pahalıdır. Bu makale maliyetleri düşürmenin bazı yollarını tanıtacak ancak büyük ölçekli durumlarda bulut hizmetlerini kullanırken dikkatli olmanızı öneririz.)

Genelde yararlı olabilecek hizmetlerden kaçınırız çünkü bizim ölçeğimizde maliyet kabul edilemez derecede yüksek olabilir. Anlama kolaylığı için, 2014'ten bir örnek vereceğim. O zamanlar, karşılaştığımız büyüyen sorunlardan biri, kaynakların nasıl güvenilir bir şekilde tahsis edileceği ve AWS'de uygulamaların nasıl dağıtılacağıydı.

Başlangıçta AWS'de kendi altyapımızı ve yapılandırma yönetimi sistemimizi oluşturmaya çalıştık. Yaklaşımımız, geliştiricilerin daha kolay başlayabilmesi için python'da bir dizi çözüm uygulamaktır. Bu çözüm Fabric projesine dayalıdır ve Boto ile entegre edilmiştir.Yeni bir sunucu oluşturmak ve uygulamayı yapılandırmak yalnızca birkaç satır kod gerektirir. O zamanlar docker henüz yeni başlıyordu.Python paketlerini veya saf metin python dosyalarını doğrudan yayınlamak için geleneksel yöntemi benimsedik.Bu yöntemin bağımlılık yönetiminde büyük zorlukları var. Proje çok ilgi görmesine ve Cliqz'in birçok üründe hizmetleri yönetmek için kullanılmasına rağmen, kütüphane tabanlı altyapı ve konfigürasyon yönetimi yöntemlerinin her zaman bazı eksiklikleri vardır. Küresel durum yönetimi, altyapı değişiklikleri için merkezi kilit, bir proje veya geliştirici tarafından kullanılan bulut kaynaklarını merkezi olarak görüntüleyememe, yalıtılmış kaynakları temizlemek için harici araçlara güvenme, sınırlı işlevlerle yapılandırma yönetimi, geliştiricinin kaynak kullanımını görüntülemek zordur, Kullanıcının ortamı sızıntılar vb., Bu sorunlar rahatsızlığı beraberinde getirir ve işlemi giderek daha karmaşık hale getirir.

Bu yüzden yeni bir dış yönetim çözümü bulmaya karar verdik çünkü bunu kendimiz geliştirmek için yeterli kaynağa sahip değildik. Nihai kararımız, Hashicorp'un Consun, Terraform ve Packer gibi çözümlerinin yanı sıra Ansible ve Salt gibi yapılandırma yönetim araçlarının bir kombinasyonunu kullanmaktı.

Terraform, altyapı yönetimini tanımlamak için mükemmel bir açıklayıcı yöntem kullanır ve bulut yerel alanındaki en son teknolojilerin çoğu bu kavramı benimsemiştir. Bu nedenle, dikkatli bir değerlendirmeden sonra, kumaş tabanlı dağıtım kitaplığımızı terk etmeye ve bunun yerine Terraform'u kullanmaya karar verdik. Teknik artı ve eksilere ek olarak, insan faktörünü de dikkate almalıyız. Bazı ekipler, muhtemelen kaynak yetersizliğinden veya değişimin maliyeti ekipler arasında tutarlı olmadığından, değişikliği kabul etmekte yavaştır. Göçü tamamlamak bir yılımızı aldı.

Terraform'un kullanıma hazır özelliklerinden bazıları daha önce mevcut değildir, örneğin:

  • Merkezi altyapı yönetimi

  • Ayrıntılı planlama, yama ve uygulama desteği

  • Kaynakları kapatmak ve izole edilmiş kaynakları en aza indirmek kolaydır

  • Birden çok bulutu destekleyin

Aynı zamanda, Terraform'u kullanırken bazı zorluklarla da karşılaştık:

  • Karmaşık DSL, genellikle KURU prensibine uymaz

  • Diğer araçlara entegre etmek zor

  • Şablon desteği sınırlıdır ve bazen çok karmaşıktır

  • Hizmet durumu hakkında geri bildirim yok

  • Kolayca geri dönemem

  • Bazı temel işlevlerden yoksundur ve terragrunt gibi bir üçüncü taraf uygulamasına güvenmesi gerekir.

Terraform, Cliqz'de kesinlikle kullanışlıdır ve bugün hala Kubernetes altyapısının çoğunu dağıtmak için kullanıyoruz.

Arama sisteminin karmaşıklığı

Arama sistemine genel bakış

Yıllar içinde, düzinelerce sunucudan oluşan dağıtılmış mimarimiz, monolitik bir mimariye ve son olarak bir mikro hizmet mimarisine geçiş yaptı.

Her hizmetin mevcut kaynak koşullarında en uygun olduğuna inanıyoruz. Örneğin, monolitik mimari kullanılır çünkü gecikmelerin çoğu kümedeki sunucular arasındaki ağ GÇ'sinden kaynaklanır. O sırada AWS, 2 TB belleğe sahip X1 bulut sunucusunu piyasaya sürdü. Mimariyi değiştirmek gecikmeyi etkili bir şekilde azaltabilir ve tabi ki maliyet yükselir. Ve mimarinin bir sonraki yinelemesi maliyete odaklanıyor. Diğer faktörleri etkilemeden her değişkeni azar azar değiştiririz. Bu yöntem çok güzel görünmese de bize çok yakışıyor.

"Mikro hizmet mimarisi tarzı, bir uygulamayı bir dizi küçük hizmete ayırır. Her hizmet kendi sürecinde çalışır ve diğer işlemlerle hafif bir mekanizma (genellikle HTTP kaynak API'si) aracılığıyla iletişim kurar." - Martin Fowler

Teorik olarak, Martin Fowler tarafından verilen mikro hizmetlerin tanımı doğrudur, ancak çok soyuttur. Bizim için bu tanım, mikro hizmetlerin nasıl oluşturulacağını ve segmentlere ayrılacağını açıklamıyor ve asıl mesele bu. Mikro hizmetlerin kullanımı bize şu faydaları sağladı:

  • Ekipler arasında daha iyi modülerlik ve otomasyonun yanı sıra endişelerin ayrılması.

  • Yatay ölçeklendirme ve iş yükü bölümü.

  • Hata izolasyonu, birden çok dil için daha iyi destek.

  • Çok kiracılı, daha iyi güvenlik özellikleri.

  • Daha iyi işletim ve bakım otomasyonu.

Genel mimari ve mikro hizmetlerin yapısı açısından bakıldığında, arka uca bir sorgu isteği gönderildiğinde, istek yolunda birden çok hizmet tetiklenir. Her hizmet bir mikro hizmet olarak kabul edilebilir, çünkü endişeler birbirinden ayrılır, hafif protokoller (REST / GRPC) kullanır ve yatay olarak ölçeklenebilir. Her hizmet bir dizi bağımsız mikro hizmetten oluşur ve bir kalıcılık katmanına sahip olabilir. İstek yolu genellikle şunları içerir:

  • Web Uygulama Katmanı Güvenlik Duvarı (WAF): Uygulama katmanı güvenlik duvarı, yaygın web güvenlik açıklarına karşı savunma yapmak için kullanılır.

  • Yük dengeleyici: istekleri alma, yük dengeleme.

  • Giriş aracısı: yönlendirme, uç gözlemlenebilirlik, keşif, politika yürütme.

  • Eagle: SERP'nin sunucu tarafında oluşturulması.

  • Sigorta: API ağ yönetimi, sonuç birleştirme, uç önbelleğe alma, kimlik doğrulama ve yetkilendirme.

  • Öneriler: sorgu önerileri.

  • Sıralama: Gerçek zamanlıya yakın indeks ve önceden derlenmiş toplu indeks (Lambda mimarisi) ile arama sonuçları sağlayın.

  • Zengin sonuçlar: Hava durumu, gerçek zamanlı puanlar için küçük pencereler ve üçüncü taraf kaynaklardan bilgiler gibi daha zengin bilgiler ekleyin.

  • Bilgi grafiği ve anında yanıtlar: Sorgularla ilgili bilgileri bulun.

  • Konum: Coğrafi konuma dayalı içerik önerisi.

  • Haberler: Tanınmış haber kaynaklarından gerçek zamanlı içerik.

  • Tracker: WhoTracks.me tarafından sağlanan belirli bir alana özgü izleme bilgileri.

  • Resim: Kullanıcı sorgusuyla ilgili görsel sonucu.

Tüm hizmetler, arama sonuçlarının boyutunun işlenmesinden sorumlu olan ve ayrıca trafikteki dalgalanmaya karşı koruma, istek hacmine / CPU / bellek / özel ölçütlere dayalı otomatik ölçeklendirme, uç gibi diğer işlevler sağlayan genel bir API ağ geçidinde düzenlenmiştir. Önbelleğe alma, trafik taklidi ve segmentasyon, A / B testi, mavi-yeşil dağıtım, kanarya sürümü vb.

Docker konteyner ve konteyner düzenleme sistemi

Şimdiye kadar, ürün gereksinimlerinin bir kısmını ve bazı ayrıntıları tanıttık. Nasıl konuşlandırılacağını ve çeşitli çözümlerin eksikliklerini tanıttık. Alınan bu derslerle nihayet Docker'ı tüm hizmetlerin temel bileşeni olarak seçtik. Sanal makineler + kod + bağımlılıkları kullanmak yerine kodu dağıtmak için Docker konteynerlerini kullanmaya başladık. Docker ile kod ve bağımlılıklar bir Docker görüntüsü olarak konteyner havuzuna (ECR) gönderilebilir.

Ancak hizmetler büyümeye devam ettikçe, özellikle üretim ortamında ölçeklendirilmesi gereken durumlarda bu kapsayıcıları yönetmemiz gerekiyor. Zorluklar arasında (1) çok fazla bilgi işlem kaynağı israfı, (2) altyapının karmaşıklığı ve (3) konfigürasyon yönetimi yer alır.

Personel ve bilgi işlem gücü her zaman kıt kaynaklar olmuştur ve bu, sınırlı kaynaklara sahip birçok yeni şirketin karşılaştığı bir ikilemdir. Elbette, verimliliği artırmak için var olan ancak mevcut araçlarla çözülemeyen sorunları çözmeye odaklanmalıyız. Ancak, tekerleği yeniden icat etmek istemiyoruz (bunu yapmak durumu etkili bir şekilde değiştirmedikçe). Açık kaynak yazılımı kullanmaya çok istekliyiz.Açık kaynak, birçok önemli iş problemini çözer.

Kubernetes sürüm 1.0'ın piyasaya sürülmesinden sonra, hemen denemeye başladık. 1.4 sürümüyle birlikte, Kubernetes zaten nispeten kararlıydı ve araçları nispeten olgunlaşmıştı. Üretim ortamının yükünü Kubernetes üzerinde çalıştırmaya başladık. Aynı zamanda Apache Mesos ve Docker Swarm gibi diğer orkestrasyon sistemlerini de büyük projelerde (fetcher gibi) değerlendirdik. Sonunda, her şeyi yönetmek için Kubernetes'i kullanmaya karar verdik, çünkü Kubernetes'in düzenleme ve konfigürasyon yönetimi sorununu çözmek için çok çekici önlemler aldığına dair yeterli kanıt var, ancak diğer çözümler bunu başaramadı. Ayrıca Kubernetes'in güçlü topluluk desteği de vardır.

Kubernetes-Cliqz'in teknoloji yığını

Cliqz tarafından kullanılan açık kaynaklı yazılım

"Açık kaynaklı yazılım dünyayı kazandı!"

Cliqz, genel bir bulut yerel deneyimi sağlamak için birçok açık kaynak yazılım projesine, özellikle de Bulut Yerel Bilişim Vakfı (Cloud Native Computing Foundation) altındaki birçok projeye güveniyor. Kod, blog gönderileri ve Slack gibi diğer kanalları sağlayarak mümkün olduğunca açık kaynak topluluğuna geri veriyoruz. Teknoloji yığınımızda kullanılan temel açık kaynaklı projeleri tanıtalım:

KOPS-Kubernetes Düzenleme

Kapsayıcı düzenlemesi açısından, birden çok bölgede Kubernetes kümelerini yönetmek, küme yaşam döngüsünü ve eklentileri yönetmek için KOPS ve kendi geliştirdiğimiz bazı araçları kullanıyoruz. Justin Santa Barbara ve kops bakımcılarına mükemmel çalışmaları için teşekkürler, bu da k8'lerin kontrol düzlemini ve çalışma düğümlerini çok iyi entegre ediyor. Şu anda, sağlayıcı tarafından yönetilen herhangi bir hizmete güvenmiyoruz çünkü KOPS çok esnektir ve AWS tarafından sağlanan k8s kontrol düzlemi hizmeti EKS hala çok olgunlaşmamış.

KOPS ve kendi kendini yöneten kümeleri kullanmak, kendi hızımızda hareket edebileceğimiz, sorunları derinlemesine inceleyebileceğimiz ve uygulama tarafından gerçekten ihtiyaç duyulan ancak yalnızca belirli bir Kubernetes sürümünde bulunan özellikleri etkinleştirebileceğimiz anlamına gelir. Bulut hizmetlerine güvenirsek, statükoya ulaşmamız daha uzun sürecektir.

Örgü ağ kapsama alanı

Kubernetes'in sistemin tüm bölümlerini soyutlayabildiğini belirtmekte fayda var. Yalnızca bilgi işlem ve depolamayı değil, aynı zamanda ağı da içerir. Kümemiz yüzlerce düğüme kadar büyüyebilir, bu nedenle temel ağ işlevlerini sağlamak ve birden çok düğüme ve hatta birden çok alana yayılan Kapsüller için ağ stratejileri uygulamak üzere bir omurga ağı oluşturmak için bir katman ağı benimsedik. Weave Net'i overlay ağı olarak benimsedik çünkü yönetimi kolay. Ölçek büyüdükçe, AWS VPC CNI ve Calico'ya geçebiliriz çünkü bunlar daha olgun, daha az ağ atlaması ve daha tutarlı yönlendirme ve trafik sağlayabilir. Weave Net, gecikme ve aktarım ortamımızda şu ana kadar iyi performans gösterdi, bu nedenle geçiş yapmak için bir neden yok.

Dümen / Helmfile paketi yönetimi ve sürümü

Başlangıçta paket yönetimi ve Kubernetes manifestinin yayınlanması için dümene (v2) güvendik. Pek çok acı noktası olmasına rağmen, yine de mükemmel bir sürüm yönetimi ve şablon aracı olduğunu düşünüyoruz. Tüm hizmetlerin heml diyagramlarını saklamak için tek bir kod deposu ve paketleme ve dağıtım için chartmuseum projesini kullanıyoruz. Ortama bağımlı değerler, endişelerin ayrılmasını sağlamak için başka bir kod havuzunda saklanacaktır. Bunlar, Helmfile tarafından sağlanan gitOps modu aracılığıyla elde edilir; bu mod, birden çok dümen diyagramının yayın yönetimini gerçekleştirmenin ve diff ve tillerless gibi önemli eklentileri ilişkilendirmenin ve gizli yönetim için SOPS'un kullanılmasının bildirimsel bir yolunu sağlar. Kod havuzunda yapılan değişiklikler, Jenkins'in CI / CD ardışık düzeni aracılığıyla doğrulanacak ve konuşlandırılacaktır.

Tilk / K9s-stressiz yerel Kubernetes geliştirme

Karşılaştığımız sorunlardan biri şudur: Kubernetes'i geliştiricinin geliştirme döngüsüne nasıl dahil edebiliriz. Bazı gereksinimler çok açıktır ve bu, kodun nasıl oluşturulacağı ve kapsayıcıyla nasıl senkronize edileceği ve nasıl hızlı ve iyi yapılacağıdır. Başlangıçta, kaynak kodu değişikliklerini izlemek için dosya sistemi olaylarını kullanan ve ardından konteynere rsync uygulayan basit bir kendi kendine çözüm kullandık. Aynı sorunu çözmeye çalışmak için Google'ın Skaffold ve Microsofts Draft gibi birçok projeyi de denedik. Bizim için en uygun olanı Windmill Engineering'den Tilt (Daniel Bentley sayesinde) Bu ürün mükemmel, iş akışı starlark ile yazılmış Tiltfile tarafından yürütülüyor. Dosya düzenlemeyi izleyebilir, değişiklikleri otomatik olarak uygulayabilir, otomatik olarak gerçek zamanlı olarak konteyner görüntüleri oluşturabilir, küme yapımını kullanabilir, konteyner depolarını ve diğer araçları atlayarak inşaatı hızlandırabilir ve tüm servis bilgilerini tek bir panelde görüntülemenizi sağlayan güzel bir kullanıcı arayüzüne sahiptir. Derinlemesine çalışmak isterseniz, bu k8s bilgilerini K9s (https://github.com/derailed/k9s) adlı bir komut satırı aracına açarak k9s komutlarını etkileşimli bir şekilde çalıştırabilir ve geliştiricileri basitleştirebiliriz. İş akışı. Bugün, k8'lerde çalışan tüm iş yükleri kümeler halinde geliştiriliyor ve birleşik ve hızlı bir deneyim sağlıyor. Katılan her yeni kişinin, çalışmaya başlamak için yalnızca birkaç komuta ihtiyacı var, hepsi dümen / eğme sayesinde / k9s.

Prometheus, AlertManager, Jaeger, Grafana ve Loki-Gözlemlenebilirlik

Prometheus'un izleme programına güveniyoruz ve çeşitli hizmetlerden toplanan metrik verileri toplamak, saymak ve iletmek için bir zaman serisi veritabanı (tsdb) kullanıyoruz. Prometheus, çok iyi bir PromQl sorgulama dili ve alarm servisi Alert Manager sağlar. Jaeger, izleme istatistikleri programının bel kemiğini oluşturur. Prometheus'a benzer bir deneyim sağlamak için yakın zamanda günlük arka ucunu Graylog'dan Loki'ye taşıdık. Tüm bunlar, tüm gözlenebilirlik gereksinimlerini karşılamak için tek bir düzlem sağlamak içindir.Bu verileri grafik çözümü Grafana aracılığıyla yayınlamayı planlıyoruz. Bu hizmetleri düzenlemek için, çok kiracılı Prometheus dağıtımının yaşam döngüsünü yönetmek için Prometheus Operatör projesini kullanıyoruz. Herhangi bir zamanda, altyapının işleyişini anlamak ve bir sorun oluştuğunda sorunu çözmeye hangi hizmetin başlayacağını belirlemek için yüz binlerce zaman serisi verisi alacağız.

Gelecekte, Prometheus'un ölçeklenebilirlik sorununu çözmek için Thanos veya Cortex projesini entegre etmeyi ve geçmiş analiz için küresel bir sorgu görünümü, daha yüksek kullanılabilirlik ve veri yedekleme işlevi sağlamayı planlıyoruz.

Luigi ve Jenkins otomatik veri ardışık düzeni

Veri hattını düzenlemek ve otomatikleştirmek için Luigi ve Jenkins'i kullanıyoruz. Toplu iş EMR'ye gönderilir ve Luigi çok karmaşık bir toplu iş akışı oluşturmaktan sorumludur. Ardından, her bir görevin otomasyonunu ve kaynakların kullanımını kontrol edebilmemiz için bir dizi ETL işlemini tetiklemek için Jenkins'i kullanın.

Toplu işin kodunu paketleyip sürüm numarasını ekledikten sonra, geliştirme ve üretim ortamlarında aynı deneyimi sağlamak için sürüm numarasıyla birlikte docker konteynerine koyuyoruz.

Eklenti projesi

Ayrıca topluluk tarafından geliştirilen diğer birçok projeyi de kullanıyoruz.Eklentiler olarak yayınlanan bu projeler, küme yaşam döngüsünün bir parçasıdır ve üretim ortamında ve geliştirme ortamında dağıtılan hizmetler için ek değer sağlar. İşte kısa bir giriş:

  • Argo iş akışı ve sürekli dağıtım: Bu projeyi toplu işleme görevleri ve sürekli dağıtım için Jenkins için bir yedek olarak değerlendirdik.

  • AWS IAM Authenticator: K8'lerde kullanıcı kimlik doğrulama yönetimi.

  • ChartMuseum: Uzak dümen haritası sağlar.

  • Küme Otomatik Ölçekleyici: Kümedeki otomatik ölçeklendirmeyi yönetin.

  • Otomatik Dikey Bölme Ölçekleyici: Bölmenin dikey ölçeklendirmesini gerektiği gibi veya özel göstergelere göre yönetin.

  • Konsolos: Birçok proje için devlet deposu.

  • Harici DNS: Harici ve dahili erişim elde etmek için DNS kayıtlarını Route53 ile eşleyin.

  • Kube Downscaler: Artık gerekmediğinde dağıtım ve durum setini küçültün.

  • Kube2IAM: Şeffaf proxy, AWS meta verilerine erişimi kısıtlayın ve Kapsül için rol yönetimi sağlayın.

  • Loki / Promtail: günlük gönderme ve istatistikler.

  • Metrics Sunucusu: Gösterge istatistikleri, diğer tüketicilerle arayüz.

  • Nginx Ingress: Dahili ve harici hizmetler için giriş denetleyicisi. API ağ geçidinin işlevselliğini genişletmek için Gloo, Istio giriş ağ geçidi ve Kong gibi diğer giriş denetleyicilerini sürekli olarak değerlendiriyoruz.

  • Prometheus Operatörü: Grafana, Prometheus, AlertManager ve Jaeger dağıtımını hazırlayabilen Prometheus operatör yığını.

  • RBAC Manager: K8s kaynakları için kolayca rol tabanlı erişim kontrolü sağlayabilir.

  • Spot Termination Handler: Düğümleri önceden uyararak ve temizleyerek tek noktalı kesintilerin üstesinden gelin.

  • Istio: Istio'nun ızgarasını, gözlemlenebilirliğini, trafik yönlendirmesini ve diğer işlevleri değerlendiriyoruz. Pek çok fonksiyon için kendi çözümlerimizi yazdık, ancak uzun zamandır bu çözümler sınırlamaları ortaya çıkarmaya başladı, projenin ihtiyaçlarımızı karşılayacağını umuyoruz.

K8'in deneyimi ve zengin topluluk desteğiyle, yalnızca arama işlevleri sağlamak için temel durum bilgisi olmayan hizmetleri yayınlayamıyoruz, aynı zamanda Cassandra, Kafka, Memcached ve RocksDB gibi birden çok bölge ve kümede büyük durum bilgisi olan yükleri çalıştırabiliyoruz. Yüksek kullanılabilirlik ve çoğaltma sağlamak için. Bu yükleri Kubernetes'te yönetmek ve güvenli bir şekilde yürütmek için başka araçlar da geliştirdik.

Yerel geliştirme-uçtan-uca kullanım örnekleri için Tilt'i kullanın

Kullandığımız araçların çoğu yukarıda açıklanmıştır. Burada, bu araçların nasıl kullanılacağını ve daha da önemlisi, bu araçların geliştiricilerin günlük işlerini nasıl etkilediğini tanıtmak için belirli bir örneği birleştirmek istiyorum.

Örnek olarak, arama sonucu sıralamalarını geliştirmekten sorumlu bir mühendisi ele alalım. Önceki iş akışı:

  • Bir örneği başlatmak için özelleştirilmiş bir işletim sistemi görüntüsü kullanın ve ardından örneği ve ilgili kaynakları etiketlemek için sahip bilgilerini kullanın.

  • Kodu örneğe yeniden senkronize edin ve ardından uygulama bağımlılıklarını yükleyin.

  • API Ağ Geçidi ve ön uç gibi diğer hizmetleri nasıl kuracağınızı, bağımlılıkları nasıl yükleyeceğinizi ve dağıtacağınızı öğrenin.

  • Bu hizmetleri birlikte çalışacak şekilde yapılandırın.

  • Sıralama uygulamaları geliştirin.

  • Son olarak, geliştirme tamamlandıktan sonra, örnek sonlandırılmalıdır.

Geliştiricilerin bir dizi işlemi tekrarlaması gerektiği ve ekipteki her yeni mühendisin tüm bunları tekrar etmesi gerektiği görülebilir, bu da geliştirici üretkenliğinin israfıdır. Örnek kaybolursa, tekrarlayın. Dahası, üretim ortamının ve yerel geliştirme ortamının iş akışı biraz farklıdır ve bu bazen tutarsızlıklara yol açar. Bazı insanlar bir sıralama uygulaması geliştirirken başka hizmetler (ön uç gibi) kurmanın gereksiz olduğunu düşünür, ancak buradaki örnek genellik içindir ve eksiksiz bir ürün kurmanın hiçbir zararı yoktur. Ek olarak, ekip büyümeye devam ettikçe, daha fazla bulut kaynağının yaratılması gerekiyor ve kaynak kullanımı gittikçe azalıyor. Mühendisler, bu işlem serisini her gün tekrarlamak istemedikleri için örneği çalışır durumda tutar. Bir mühendis ayrılırsa ve örneği yeterince etiketlenmezse, örneği kapatmanın ve bulut kaynaklarını silmenin güvenli olup olmadığına karar vermek zordur.

İdeal durum, mühendislere, eksiksiz bir SERP ve uygulamaları derecelendirmenin gerektirdiği diğer hizmetleri kurabilen bir yerel geliştirme ortamı kurmak için temel bir şablon sağlamaktır. Bu şablon evrenseldir, uygulamanın yaşam döngüsünü kontrol etmelerine yardımcı olmak için kullanıcılar tarafından oluşturulan diğer kaynaklara benzersiz etiketler ekler. K8'ler, örnek oluşturma ve yönetme gereksinimlerini soyutladığı için (yönetimi merkezileştirmek için KOPS kullanıyoruz), maliyetleri büyük ölçüde azaltan varsayılan değerleri ayarlamak için (çalışma saatleri dışında otomatik olarak küçültülmesi) şablonları kullanıyoruz.

Şimdi, kullanıcının yalnızca kendi yazdığı çapa dikkat etmesi gerekiyor ve aracımız (Docker, Helm ve Tilt'den oluşan) bu dizi iş akışını perde arkasında sihirli bir şekilde tamamlayacak.

Aşağıda, SERP'nin minimum sürümünü ve diğer bağımlı hizmetleri kurmak için gereken hizmetleri açıklayan bir Tiltfile örneği verilmiştir. Bu hizmetleri geliştirme modunda başlatmak için, kullanıcıların yalnızca yukarı eğimi çalıştırması gerekir:

# - * - mod: Python - * - "" "Bu Tiltfile, bir dizi başka mikro hizmete bağlı olan 1 birincil hizmeti yönetir.Ayrıca, geliştirme sırasında yararlı olabilecek bazı ekstra hizmetlerin başlatılmasını kolaylaştırır. İşte bu hizmetlerin ve özelliklerinin hızlı bir özeti: * sıralama : Sıralamayı yönetir * api-ağ geçidi: Ön uç için API Ağ Geçidi * ön uç: SERP için Sunucu Tarafı Oluşturma "" " ###################### Proje varsayılanları ###################### project = "bir-proje" namespace = "bir-isim alanı" chart_name = "bir-proje-grafiği" deploy_path = "../../deploy"charts_path =" {} / charts ".format (deploy_path) chart_path =" {} / {} ". format (charts_path, chart_name) values_path =" {} /some-project/services/development.yaml ".format (deploy_path) secrets_path =" {} /some-project/services/secrets.yaml " .format (deploy_path) secrets_dec_path = "{} /some-project/services/secrets.yaml.dec" .format (deploy_path) chart_version = "XXX" # Tiltfile kitaplığını yükle ("../../ libs / tilt / Tiltfile", "validate_environment") env = validate_environment (proje, ad alanı) # Componentsserving_image = env + "/ bir-depo / hizmetler / bir-proje / hizmet" için Docker depo yolu ##################################### Hizmetler oluşturun ve k8s'e dağıtın ####### ############################## # Değişiklik olması durumunda Tiltfile'ı yeniden çalıştırmak için dümen çizelgesi için geliştirme değerleri dosyasını izleyinwatch_file (values_path) # Docker görüntüleri oluşturun # live_update işlevini kullanmak istiyorsanız, live_update kısmının açıklamasını kaldırın # yani, geliştirme sırasında konteyner yeniden başlatılmaz. Örn: Python hata ayıklamadocker_build kullanma (serve_image, "serve", dockerfile = "./ service / Dockerfile", build_args = {"PIP_INDEX_URL": env, "AWS_REGION": env} #, live_update =) # Yerel dümen depoları listlocal'ı güncelle ("dümen deposu güncellemesi") # Yerel değişiklik olması durumunda eski indirme grafiğini kaldırın ("rm -rf {}". Format (chart_path)) # Secretslocal'in şifresini çöz ("dışa aktarma HELM_TILLER_SILENT = gerçek dümen yeke çalıştırması {} - dümen sırları dec {}". Format (ad alanı, gizli_yol)) # Helm grafiğini standart k8s manifeststemplate_script = "helm fetch {} / {} --version {} --untar --untardir {} dümen şablonu {} --namespace {} --name {} -f {} -f {} ". format (env, chart_name, chart_version, charts_path, chart_path, namespace, env, values_path, secrets_dec_path) yaml_blob = local (template_script) # Sırları temizle dosya yerel ("rm {}". Format (secrets_dec_path)) # K8s manifestolarını dağıtk8s_yaml (yaml_blob) dev_config = read_yaml (değerler_path) # Yönlendirmeye özgü kaynaklar k8s_resource ('{} - {}'. Format (env, 'sıralama'), port_forwards =, new_name = "short-name-1") k8s_resource ('{} - {}'. Format (env , 'bir-proje-2'), new_name = "kısa-isim-2") dev_config.get ('api-ağ geçidi', {}). get ('etkinleştirildi', Yanlış): k8s_resource ('{} - {}'. format (env, 'bir-proje-3'), port_forwards =, new_name = "kısa-ad-3") dev_config.get ('ön uç', {}). get ('etkin', Yanlış): k8s_resource ('{} - {}'. format (env, 'bir-proje-4-1'), port_forwards =, new_name = "short-name-4-1") k8s_resource ('{} - {}'. format (env, 'bir-proje-4-2'), new_name = "kısa-isim-4-2")

Açıklama:

  • Dümen diyagramları esas olarak uygulama paketlemesi ve her sürümün yaşam döngüsünün yönetimi için kullanılır. Dümen şablonunu kullanıyoruz ve şablon için değerler sağlamak için özel yaml kullanıyoruz. Bu şekilde, her sürüm için derinlemesine yapılandırma gerçekleştirebiliriz. Konteynere tahsis edilen kaynakları, her konteynerin bağlanması gereken servisleri, kullanılabilecek portları vb. Kolayca yapılandırabiliriz.

  • Yerel k8s geliştirme ortamını ayarlamak için Eğim artı dümen diyagramını kullanın ve yerel kodu dümen diyagramında tanımlanan hizmetlerle eşleyin. Sağladığı işlevleri kullanarak, sürekli olarak docker konteynerleri oluşturabilir ve uygulamaları k8s'e dağıtabilir veya yerel güncellemeleri gerçekleştirebiliriz (tüm yerel değişiklikleri çalışan konteynerde rsync). Geliştiriciler, geliştirme sırasında hizmet uç noktalarına erişmek için uygulamaları yerel örneklerle eşlemek için bağlantı noktası yönlendirmeyi de kullanabilir. Oluşturulan şablonu dümen diyagramından çıkarmak ve dağıtım için kullanmak için k8s manifestini kullanıyoruz. Bunun nedeni, grafiklerimizin gereksinimlerinin Tilt tarafından sağlanan dümen işlevine tamamen güvenemeyecek kadar karmaşık olmasıdır.

  • Uygulama uç noktasının diğer ekip üyeleriyle paylaşılması gerekiyorsa, dümen diyagramı dahili bir giriş uç noktası oluşturmak için birleşik bir mekanizma sağlayabilir.

  • Grafiğimiz genel dümen grafiği deposu aracılığıyla açıklanır, bu nedenle ister bir üretim ortamı isterse bir geliştirme ortamı olsun, aynı kod setini (sürüm numaralı docker görüntüsü), aynı grafik şablonunu kullanıyoruz, ancak şablondaki değer değil Farklı ihtiyaçlara (dağıtım adı, uç nokta adı, kaynak, kopya vb.) Uyarlamak için aynı.

  • Tüm uygulamalar her uç noktada ve her projede tutarlıdır, böylece ekibe yeni gelenlerin kullanımı çok kolaydır ve bulut kaynaklarının yönetimi de çok kolaydır.

"Teknoloji yeterince gelişmiş olduğu sürece, sihirden farkı yoktur." - Arthur Clark

Ama bu sihrin bir sorunu var. Daha etkili kaynak paylaşımı sayesinde üretkenliği artırır, güvenilirliği artırır ve maliyetleri düşürür. Bununla birlikte, bir şeyler ters gittiğinde, insanların sorunun nerede olduğunu bulması zordur, sorunun kökenini bulmak çok zorlaşır ve bu tür bir hatanın, insanların onu çözmesi sakıncalı olduğunda ortaya çıkması özellikle kolaydır. Dolayısıyla, bu çabalardan gurur duymamıza rağmen, mütevazı bir tavrımızı sürdürüyoruz.

Maliyeti optimize edin

Ucuz altyapı ve İnternet ölçekli arama motorları ikisine birden sahip olamaz. Bunu söyledikten sonra, her zaman para biriktirmenin bir yolu vardır. Maliyetleri optimize etmek için k8 tabanlı mimariyi nasıl kullandığımızı tanıtmama izin verin.

1. Spot örnekleri

AWS spot bulut sunucularına büyük ölçüde güveniyoruz. Bu hizmeti kullanmak için sistemi oluştururken olası hataları göz önünde bulundurmalıyız. Ancak buna değer, çünkü bu örnekler isteğe bağlı örneklerden çok daha ucuz. Ama bizim yaptığımız gibi kendini ayağından vurmamaya dikkat et. Örnekleri tespit etmeye uzun zamandır alışmışızdır, bu yüzden bazen kendi gücümüzü abartarak, olmaması gereken başarısızlıklara yol açarız. Ayrıca, yüksek performanslı sunucuların tüm performansını sıkıştırmayın, aksi takdirde diğer şirketlerle bir teklif savaşına girersiniz. Son olarak, büyük bir NLP / ML konferansından önce asla spot GPU örneklerini kullanmayın.

Spot'un karma bulut sunucusu havuzunu kullanın: Tek seferlik işleri tamamlamak için yalnızca spot bulut sunucuları değil, aynı zamanda hizmet iş yüklerini çalıştırmak için de kullanıyoruz. Harika bir strateji geliştirdik. Kubernetes kaynakları için birden çok kullanılabilirlik bölgesinde dağıtılan bir düğüm havuzu oluşturmak için birden çok örnek türü kullanırız (ancak yapılandırma benzerdir). Spot Termination handler ile birlikte kullanıldığında, olası uzun vadeli aksama sürelerini önlemek için durum bilgisi olmayan iş yüklerini yeni veya boştaki spot düğümlere taşıyabiliriz.

2. Paylaşılan CPU belleği

İş yüklerini tartışırken tamamen Kubernetes'e güvendiğimizden, her hizmetin ne kadar CPU, ne kadar bellek ve kaç replikaya ihtiyaç duyduğunu tartışıyoruz. Bu nedenle, Talep ve Sınırlar eşitse performans garanti edilebilir. Bununla birlikte, İstek düşükse ancak Limit yüksekse (bu, ara sıra iş yükleri için kullanışlıdır), daha fazla kaynak hazırlayabilir ve bir örneğin kaynak kullanımını en üst düzeye çıkarabiliriz (örnekteki boşta kalan kaynakların miktarını azaltabilir).

3. Küme için otomatik ölçekleyici, Kapsülün dikey ve yatay Otomatik Ölçekleyicisi

Kapsül oluşturma ve azaltmayı otomatikleştirmek için küme otomatik genişleticiyi kullanıyoruz ve yalnızca talep arttığında örnekler oluşturuyoruz. Bu şekilde, iş yükü olmadığında yalnızca en az örnek başlatılır ve manuel müdahale gerekmez.

4. Geliştirme ortamında dağıtılan ölçek indirici

down-scalerpod0.Kubernetesmanifest

annotations: downscaler/uptime: Mon-Fri 08:00-19:30 Europe/Berlin

0

5.

reserved instance

Kuberneteskubecostebs

Sculley

KubernetesTensorflowTensorflow

TerraformjinjaKubernetes manifestsKubernetesKubernetesKubernetes

Kubernetes

Hidden Technical Debt in Machine Learning System

  • MLT

  • AWS SageMaker

  • Kubeflow

  • MLFlow

Kubeflow

KubeflowkubeflowTfJobPytorchJobnotebook

CliqzKubeflow

KubeflownotebooknotebooknotebookWebnotebookCPUGPUEBSnotebook0.5CPU1GBnotebook

notebookFairing

KubeflowKatibKFServingKubernetesTFXML

Kubeflow

HydraKubernetes

son sözler

Kelsey Hightower

Cliqz

Cliqz120https://github.com/cliqz-oss/

https://www.0x65.dev/blog/2019-12-14/the-architecture-of-a-large-scale-web-search-engine-circa-2019.html

Bu makale bir CSDN çeviri makalesidir, lütfen yeniden basımın kaynağını belirtin.

IBM All In

GitHub 2000+ Star'ı edinerek, Aliyun'un açık kaynak Alink makine öğrenimi platformu çift 11 veri "oyunundan" nasıl daha iyi performans gösteriyor? | AI teknolojisi ekolojisi

Microsoft bir kişi için bir şirket mi satın alıyor? Sony programlarını kırın, hacker romanları yazın ve sağlam program hayatını izleyin!

Makine öğrenimi proje şablonu: Makine öğrenimi projesinin 6 temel adımı

IBM, Microsoft, Apple, Google, Samsung ... blok zincirindeki teknoloji devleri şimdiden çok şey yaptı!

Kıdemli programcının özeti: Linux sürecini analiz etmek için 6 yöntem, size hepsini anlatacağım

Bugünün refahı: yorum alanı seçildi, çevrimiçi 299 yuan değerinde "2020 AI Geliştiricileri Konferansı" nı alabilirsiniz Bir canlı bilet . Parmaklarınızı hareket ettirin ve söylemek istediklerinizi yazın.

Zhou Hongyi tarafından tüm ağda engellenen 360 amansız general: tabandan gelen bir karşı saldırı, net değeri yüz milyonlarca olan 36 yaşına kadar çalışıyor.
önceki
Gartner'ın 1 numaralı konteyner ürününü kazanan Alibaba Cloud, bulut yerel savaşını kazandı
Sonraki
Huawei, 4188 yuan fiyatıyla P40 serisinin National Bank versiyonunu yayınladı; DJI "en az% 50 işten çıkarmalar ve temizlik planına" yanıt verdi; Firefox 75 yayınlandı
Temelleri zayıf olan programcılara ne oldu?
10 kat HD maliyeti yok! SVG oluşturma
Sıcak arama! Huawei: Bu tür programcılar 1 milyara öncülük ediyor Programcılar: Çok güzel kokulu! Ne düşünüyorsun?
Luo Yonghao: Bu yıl kırk sekiz yaşındayım ve sayısız hataya dayanabilirim; iOS14 sistem düzeyinde bir "küçük program" işlevi başlatabilir; PyCharm'ın yeni sürümü yayınlandı | Geek Headlines
Dikkatli değilseniz, RPC'nin zaman aşımı ayarı çevrimiçi bir kazadır
Excel zayıf! Bu araç günlük çalışmamı 30 dakikada tamamladı ve sıfır temel ile öğrenebilirim
Dijital dönüşüm eş için çok mu zor? AI ve IoT sert vuruşlar yapıyor
Uluslararası öğrenciler yurt dışından dönüşlerini gizleyip düğünlere katılarak yüzlerce insanın karantinaya alınmasına neden oldu
Beihu Bölge Savcılığı: Chenzhou'daki salgının önlenmesi için "güvenlik kapısında" konuşlu
Baharın ilk çarpıcı güzelliği
Bali Şehri, Yizhang İlçesi, Li Zhizhu için çevrimiçi bir anma töreni başlattı
To Top