Bu sitedeki içerik yapay zeka (AI) veya makine çeviri teknolojisi kullanılarak çevrilmiştir ve hatalar içerebilir.

Skip to content

Bellek sızıntılarını izleyerek sunucu kaynak kullanımını iyileştirme

Yaklaşık bir yıl önce, oyun sunucularımızın kapasitesinde bir düşüşe işaret eden ilk belirtileri fark ettik. Başlangıçta, yeterli yedek kapasitemiz olduğu için oyuncular üzerinde neredeyse hiç bir etkisi yoktu, ancak tamponumuz hızla azalıyordu. Bir oyun sunucusu, yeterli CPU ve bellek kaynağına sahip olduğu sürece yeni oyuncuları kabul eder veya yeni oyunlar başlatır. Bu durumda, bir sunucuda barındırılan oyun sayısını sınırlayan bellek eşiğiydi. Bu sorunu giderememek, oyuncu tabanımızı desteklemek için veri merkezlerimizi büyütmek anlamına gelecekti ve bu da yüksek iş gücü ve finansal maliyet gerektirecekti.

Araştırdığımız ilk hipotez, oyunların bellek kullanım modelinin değiştiği yönündeydi. Geliştiricileri, platformun sınırlarını zorlamaya ve ellerindeki kaynakları kullanarak harika ve çığır açan oyunlar yapmaya teşvik ediyoruz. Bunu kontrol etmek kolaydı; tüm oyunların bellek kullanımını toplu olarak inceleyip artış olup olmadığını görebilirdik. Ancak böyle bir durum söz konusu değildi; oyun başına ortalama bellek ve yüzdelik toplama değerleri nispeten sabit kalırken, kapasitemiz giderek azalıyordu.

Her ay terabaytlarca performans ve kaynak kullanım verisi depoluyoruz; bu veriler, bu tür sorunların temel nedenini bulmaya yardımcı olmak için toplanabilir ve filtrelenebilir. Sorunu belirli bir coğrafi bölge, donanım türü veya yazılım sürümüyle sınırlandırmaya çalıştık, ancak ne yazık ki sorun her yerde mevcuttu. Ardından, birkaç oyun sunucusunu rastgele kontrol etmeye ve daha ayrıntılı bir inceleme yapmaya karar verdik. Başlangıçta, Linux sistemlerindeki "boş" bellek kavramı beni yanılttı. Bu o kadar yaygın bir sorundur ki, birisi bellek kategorilerini açıklamak için bir alan adı kaydettirip bir web sitesi kurmuştur: https://www.linuxatemyram.com.

TL;DR belleğinizin çoğunun kullanılması iyi bir şeydir, boş bellek boşa harcanan bellektir. Yalnızca kullanılabilir bellek 0'a yaklaştığında endişelenmemiz gerekir.

Belleği doğru bir şekilde izlediğimizi belirledikten sonra, belleğin nerede kullanıldığını belirlemek için bir dizi deneye başladık. Yaklaşımımız, hedefli bir çözüme ulaşabilmek için belirli bellek alt kategorilerini izlemekti.

(lưu ý: % sử dụng bộ nhớ được tính theo công thức (totalPhysicalMemory - availableMemory) / totalPhysicalMemory)

Kullanılabilir bellek, kabaca MemFree + Active(file) + Inactive(file) + SReclaimable toplamı olarak hesaplanır.

MemFree, kullanılmayan belleği izler.

Active(file) ve Inactive(file), sayfa önbellek belleğini izler. Sayfa önbelleği, disk I/O miktarını azaltmak için erişilen verileri bellekte depolar.

SReclaimable, geri kazanılabilir slab belleğini izler. Slab belleği, çekirdek tarafından yaygın olarak kullanılan başlatılmış nesnelerin önbelleklerini tutmak için kullanılır.

İlk deney, önbellek ayarlarını, özellikle vm.vfs_cache_pressure ve vm.dirty_background_ratio değerlerini değiştirmekti.

vfs_cache_pressure değerinin artırılması, geri kazanılabilir önbellekten nesneleri geri kazanma olasılığımızı artıracaktır. Bu, performansa etki eder (hem önbellek ıskalamalarında hem de serbest bırakılabilir nesneleri bulmak için gereken arama süresinde).

dirty_background_ratio, engelsiz bir şekilde diske yazmaya başladığımızda (kirli olan sayfa önbellek belleğinin) yüzdesidir.

Aşağıdaki değişiklikleri yaptık ve bellek, slabinfo ve cgroups üzerindeki etkilerini gözlemledik.

vm.vfs_cache_pressure= 100 ==&gt; 10000<br>vm.dirty_background_ratio= 10 ==&gt; 5

Sonucu izlemenin müdahalesiz bir yolu, bellek durumunun anlık görüntüsünü alan bir cron işini periyodik olarak çalıştırmaktı. Şunun gibi bir şey:

#!/bin/bash<br>now=`date +%Y-%m-%d.%H:%M`
  # create test dir<br>mkdir -p ~/memtest&nbsp;
# log meminfo<br>sudo cat /proc/meminfo 
  ~/memtest/meminfo_$now
# log slabinfo<br>sudo cat /proc/slabinfo
  ~/memtest/slabinfo_$now&nbsp;
# log cgroups<br>sudo cat /proc/cgroups
  ~/memtest/cgroups_$now&nbsp;

Önbellek baskısı değişikliklerini uyguladıktan ve birkaç saat gözlemledikten sonra analize başladık. Yaklaşık 8 GB boş bellek kazandığımız sonucuna vardık, ancak bu bellek doğrudan sayfa ve disk önbelleklerinden, Active(file) ve Inactive(file) kategorilerinden gelmişti. Bu hayal kırıcı bir sonuçtu; kullanılabilir bellekte önemli bir net artış yoktu, ayrıca bu belleği artık verimli bir şekilde kullanmıyorduk. Belleği başka bir yerden geri kazanmamız gerekiyordu.

Kullanılabilir belleği doğrudan artırmayı başaramayınca, rekabet eden kategorileri azaltmayı denedik. SUnreclaim bellek kategorisinin büyük olduğunu fark ettik; bazı durumlarda birkaç ay içinde 60 GB'a kadar şişiyordu. SUnreclaim kategorisi, bellek baskısı altında geri kazanılamayan, işletim sistemi tarafından nesne havuzları için kullanılan belleği izler. Sorunun ilk işareti, sürekli artan cgroup sayısıydı. Dockerize edilmiş süreçlerimizi çalıştırırken en fazla birkaç yüz cgroup bekliyorduk, ancak YÜZ BİNLERCE cgroup görüyorduk. Şansımıza, Facebook'tan başka bir mühendis olan Roman Gushchin, kısa süre önce çekirdek düzeyinde tam da bu sorunu bulmuş ve düzeltmiş görünüyordu https://patchwork.kernel.org/cover/10943797/. O şöyle diyor:

Temel sorun oldukça basit: bir cgroup'a tahsis edilen herhangi bir sayfa ona bir referans tutar, bu nedenle tüm tahsis edilmiş sayfalar ortadan kalkmadıkça cgroup geri kazanılmamaktadır. Bir slab nesnesi diğer cgroup'lar tarafından aktif olarak kullanılıyorsa, geri kazanılmayacak ve kaynak cgroup'un geri kazanılmasını engelleyecektir.

Bu tam olarak bizim sorunumuz gibi görünüyordu, bu yüzden düzeltmenin geçerliliğini doğrulamak için çekirdek 5.3'ü sabırsızlıkla bekledik.

Önbellek yükü deneyinden bellek izleme komut dosyasını yeniden kullandık, ancak çekirdek deneyi için kontrol ve deney grupları oluşturmak istedik. 2 sunucu rafındaki üretim trafiğini boşalttık, ardından bir rafta çekirdeği 5.3'e yükselttik, diğerinde ise çekirdeği 5.0 olarak bıraktık. Sonra her iki rafı da yeniden başlattık ve tekrar üretim trafiğine açtık. Yaklaşık bir hafta sonra, cgroup'ların ve geri kazanılmayan slab belleğin zaman içinde nasıl değiştiğini izledik. İşte sonuçlar:

Kernel 5.0.0'da cgroups kesintisiz bir şekilde büyüyor ve bir hafta içinde geri kazanılmayan slab bellek 4 GB artarak toplamda 6 GB'a ulaşıyor. Öte yandan, kernel 5.3.7'de cgroups her gün önemli ölçüde azalıyor ve geri kazanılmayan slab belleğin artışı çok yavaş. Bir hafta sonra, geri kazanılmayan slab bellek ~2 GB'dir. Yeni çekirdek ile, geri kazanılmayan slab bellek, birkaç aylık çalışma süresinden sonra bile yaklaşık 4 GB'de sabitlenmiştir.

Çözmek istediğimiz asıl sorun, oyun sunucularımızın zamanla kapasite kaybetmesiydi. Bunun nedeni, sürekli artan geri kazanılmayan slab bellek nedeniyle kullanılabilir belleğin azalmasıydı. Peki, çekirdek düzeltmesi sayesinde bunu nispeten kontrol altına aldıktan sonra, sunucu kapasitesi üzerinde ne gibi bir etki oldu?

Solda, altyapımıza büyük yük bindiren sunucu başına oyun sayısındaki istikrarlı düşüşü görebilirsiniz. 2020 yılının Mart ayı civarında küresel filomuzda 5.3 sürüm çekirdeği devreye aldık ve şu ana kadar yüksek sunucu kapasitesini koruyabildik. Çekirdek düzeltmesi için Roman Gushchin'e ve sorunun araştırılması ve düzeltmenin devreye alınmasında yardımcı olan Andre Tran'a çok teşekkür ederiz.

Ne Roblox Corporation ne de bu blog, herhangi bir şirketi veya hizmeti onaylamakta veya desteklemektedir. Ayrıca, bu blogda yer alan bilgilerin doğruluğu, güvenilirliği veya eksiksizliği konusunda hiçbir garanti veya taahhüt verilmemektedir.

Bu blog yazısı ilk olarak Roblox Tech Blog'da yayınlanmıştır.