YARP Load Balancing İle Yatay Ölçeklendirme(Horizontally Scaling-Scaling Out)
Merhaba,
Biliyorsunuz ki, bir yazılımdan beklenen mühim davranışlardan biri sürdürülebilirliktir. Tek bir sunucuda çalıştırılan yazılımlar artan sayıda kullanıcılara hizmet verirken kaynakların yetenek sınırlarına ulaşıldığında istemsizce performans düşmesi, yavaş yanıt süreleri, trafikte dalgalanmalar ve hatta tamamen kesinti durumları söz konusu olabilmektedir. İşte bu durumlara istinaden load balancing davranışı ile uygulamalar ölçeklendirilerek bu zorlukların üstesinden gelinebilmektedir. Bizler bu içeriğimizde YARP(Yet Another Reverse Proxy) kütüphanesi ile load balancing davranışının nasıl sağlanacağını ve bunun yanında yük testi aracı olan K6’nın nasıl kullanılacağını inceleyecek ve bu temel hat üzerinden genel anlamda konuyu detaylandırıyor olacağız. O halde buyurun başlayalım…
Öncelikle yazılımdaki ölçeklendirmenin temellerini ele alarak, yani ölçeklendirme türlerini hatırlayarak başlayalım. Malumunuz, ölçeklendirmede Scale-Up ve Scale-Out olmak üzere iki yaklaşım söz konusudur. Kısaca bu yaklaşımları izah etmemiz gerekirse eğer; Scale-Up, donanımsal kaynak artırımıyken Scale-Out ise sistemdeki sunucu sayısının niceliksel olarak artırılmasıdır.
Konuya dair daha teferruatlı bilgiyi önceden klavyeye almış olduğumuz Load Balancing(Yük Dengeleme) ve Load Balancer(Yük Dengeleyici) Nedir? başlıklı makalemizden inceleyebilirsiniz.
YARP kütüphanesi ile Scale-Out yaklaşımında ölçeklendirmeyi gayet efektif bir şekilde uygulayabilmekteyiz. Bunun için Yarp.ReverseProxy kütüphanesi eşliğinde önceki YARP İle Microservice’ler de API Gateway Implementasyonu başlıklı makalemizde anlatılan temel yapılandırmaların sağlanması gerekmektedir.
Ayrıca load balancing için aşağıdaki gibi bir yapılandırma yeterlidir.
appsettings.json yapılandırması;
{
.
.
.
"ReverseProxy": {
"Routes": {
"API1-Route": {
"ClusterId": "API1-Cluster",
"Match": {
"Path": "{**catch-all}"
},
"Transforms": [
{
"PathPattern": "{**catch-all}"
}
]
}
},
"Clusters": {
"API1-Cluster": {
"LoadBalancingPolicy": "RoundRobin",
"Destinations": {
"destination1": {
"Address": "https://localhost:7177"
},
"destination2": {
"Address": "https://localhost:7178"
},
"destination3": {
"Address": "https://localhost:7179"
}
}
}
}
}
}
In-Memory yapılandırması;
.
.
.
builder.Services.AddReverseProxy()
.LoadFromMemory(new List<RouteConfig>()
{
new()
{
RouteId = "API1-Route",
ClusterId = "API1-Cluster",
Match = new(){ Path = "{**catch-all}" },
Transforms = new List<Dictionary<string, string>>()
{
new()
{
{ "PathPattern", "{**catch-all}" }
}
}
}
},
new List<ClusterConfig>
{
new()
{
ClusterId = "API1-Cluster",
LoadBalancingPolicy = "RoundRobin",
Destinations = new Dictionary<string, DestinationConfig>()
{
{ "destination1", new(){ Address = "https://localhost:7177" } },
{ "destination2", new(){ Address = "https://localhost:7178" } },
{ "destination3", new(){ Address = "https://localhost:7179" } },
}
}
});
.
.
.
Şimdi burada yapılan konfigürasyonun izahına gelirsek eğer API1-Cluster isimli cluster’da üç adet destination tanımlanmıştır. İşte bu destination’larda ki adreslere LoadBalancingPolicy özelliğinde belirtilen RoundRobin algoritmasında load balancing davranışı uygulanacaktır.
Yukarıdaki satırlarda referans edilen(bknz: Load Balancing(Yük Dengeleme) ve Load Balancer(Yük Dengeleyici) Nedir?) makalemizden de biliyorsunuz ki load balancing’in request’leri dağıtabilme algoritması birden fazladır. YARP’da, bu algoritmaların birçoğu ortak olsa da aşağıdaki gibi kendine özel olarak davranış sergileyenleri de mevcuttur;
- Round Robin (Dönüşümlü) – RoundRobin
Gelen istekler sırayla hedefler arasında dolaşarak dağıtılır. Her bir istek, bir sonraki hedefe yönlendirilir. Haliyle hedefler arasında eşit bir şekilde yük dağıtımı için oldukça basit ve etkili bir algoritmadır. - Least Connections (En Az Bağlantı) – LeastRequests
Mevcut bağlantı sayısına göre hedefler arasında bir seçim yapılır ve en az bağlantıya sahip olan hedefe gelen istek yönlendirilir. Bu algoritma ile hedeflerin mevcut yük durumları dikkate alınarak daha dengeli bir dağıtım sağlanmaktadır. - Random (Rastgele) – Random
Gelen istekler herhangi bir kriter gözetmeksizin rastgele bir şekilde hedefler arasında dağıtılır. - First Alphabetical (İlk Alfabetik) – FirstAlphabetical
Hedeflerin isimleri alfabetik olarak sıralanır ve gelen istekler yükü dikkate almaksızın bu sıraya göre dağıtılır. - Power Of Two Choices (İki Seçimin Gücü) – PowerOfTwoChoices
Bazen Ramdom algoritması gelen isteği çok yoğun olan bir sunucuya yönlendirebilir. Bu istemeyeceğimiz bir durumdur. Haliyle Random algoritmasının bu yaratabileceği kötü durumu önlemek için Least Connections algoritmasının mantığından kısmi olarak yararlanmak isteyebiliriz. Power Of Two Choices ile Random algoritmasıyla iki hedef seçilmekte ve ardından Least Connections mantığında bu iki sunucudan yoğunluğu az olana istek yönlendirilmektedir.YARP kütüphanesinin default algoritması Power Of Two Choices’dır.
Test Edelim
Şimdi yaptığımız bu çalışmayı test edebilmek için öncelikle local’de bir API uygulamasını farklı instance’larla ayağa kaldırarak başlayalım. Bunun için dileyenler Docker’dan istifade edebilirler. Lakin ben, farklı port’lara göre ayarlanmış uygun profiller oluşturarak uygulamayı terminal üzerinden dotnet cli aracılığıyla ayağa kaldırmaya çalışacağım. Dolayısıyla oluşturulan herhangi bir Asp.NET Core uygulamasının ‘launchSettings.json’ dosyasında aşağıdaki gibi profilleri kullanabiliriz;
{
.
.
.
"profiles": {
.
.
.
"https1": {
"commandName": "Project",
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:7177;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https2": {
"commandName": "Project",
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:7178;http://localhost:5001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https3": {
"commandName": "Project",
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:7179;http://localhost:5002",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
.
.
.
}
}
Buradaki adreslere dikkat ederseniz load balancing yapılandırmasında kullandığımız adreslerle birebir uyuşmaktadırlar. Uygulamanın içeriği için de aşağıdaki gibi bir çalışma kâfidir diyebiliriz;
internal class Program
{
private static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => args[0]);
app.Run();
}
}
Evet, burada da görüldüğü üzere uygulama ayağa kaldırılırken ‘Main’ metodunda aldığı parametre değerini direkt olarak ‘/’ endpoint’inde geriye döndürmektedir. Haliyle bu değeri dotnet cli talimatı sürecinde vermemiz gerekmektedir. Dolayısıyla bu uygulamanın yukarıda oluşturulan profiller ile birlikte aşağıdaki talimatlar eşliğinde üç farklı instance’ını ayağa kaldıralım;
dotnet run '1. server' --launch-profile https1dotnet run '2. server' --launch-profile https2dotnet run '3. server' --launch-profile https3
Görüldüğü üzere her bir talimat farklı bir ‘Main’ değeri ile belirtilen profil ayarlarında uygulama instance’ını ayağa kaldırmaktadır.
Artık bizler direkt YARP ile geliştirdiğimiz load balancer uygulamasını çalıştırarak yukarıdaki konfigürasyona göre RoundRobin algoritmasında gelen istekleri bu instance’lara dağıtabiliriz.
K6 İle Performans Testi Gerçekleştirelim
Bizler, yazılım süreçlerinde yapılandırdığımız load balancing davranışlarını test edebilmek için yukarıdaki gibi manuel ve basit seviyeli bir süreçten ziyade K6 gibi modern, geliştirici dostu ve kompleks test araçlarından istifade etmeyi tercih ederiz. K6, web uygulamalarının performans testlerinde kullanılan open source bir test aracıdır. Basit ve esnek bir kullanıcı arayüzüne sahip olsa da karmaşık senaryoları simüle edebilmek için kullanılabilmektedir. Bu araç sayesinde uygulamamızdaki kullanıcı trafiğini belirlediğimiz maksimum şartlara göre simüle edecek ve uygulamanın bu şartlardaki davranışlarını ölçümleyerek, değerlendirmede bulunacağız.
Tabi öncelikle K6’nın kurulumunu gerçekleştirmek gerekmektedir. K6’yı kurabilmek için birkaç yöntem olsa da bizler Windows Package Manager üzerinden kurulumu gerçekleştireceğiz. Bunun için PowerShell üzerinden
winget install k6 --source winget talimatının verilmesi yeterlidir.
Bu kurulumdan sonra herhangi bir terminal ya da PowerShell ekranında k6 komutunu yazdığımızda bir sonuç alamıyorsanız eğer yükleyici üzerinden de K6’yı kurmayı deneyebilirsiniz.
Kurulumdan sonra aşağıdaki gibi basit bir K6 script’i ile yük testini gerçekleştirebiliriz;
import http from 'k6/http';
export const options = {
stages: [
{ duration: '10s', target: 20 }, // İlk 10 saniye boyunca 20 istek
{ duration: '10s', target: 50 }, // Sonraki 10 saniye boyunca 50 istek
{ duration: '1m5s', target: 1000 }, // 1 dakika 5 saniye boyunca 1000 istek
],
};
export default function () {
// GET isteği yapalım
const response = http.get('https://localhost:7180');
// Yanıtı kontrol edelim
//if (response.status === 200) {
// console.log(`İstek başarılı👍 Yanıt:`, response.body);
//} else {
// console.log('İstek başarısız👎 Durum kodu:', response.status);
//}
}
Bu script’i k6 run --out json=result.json .\k6-test.js talimatıyla çalıştırdığımızda aşağıdaki ekran görüntüsünde olduğu gibi test süreci başlayacak ve netice raporlanacaktır. Tabi burada test sürecinde örnekte de görüldüğü üzere console’a çıktılarda bulunabilir ya da sadece testin raporlanmasını bekleyebiliriz.
Evet, yapılan test neticesinde elde edilen sonuçları incelersek eğer tarafımıza aşağıdaki gibi bir rapor sunulmuştur.
Bu raporda bizler için önemli metrikleri değerlendirirsek eğer;
- data_received : Sunucudan alınan toplam veri miktarı 2.2 MB.
- data_sent : Sunucuya gönderilen toplam veri miktarı 858 KB.
- http_req_duration : İsteklerin ortalama işlenme süreleri 3.79s.
- http_req_receiving : Yanıtların ortalama alınma süreleri 5.36s.
şeklinde bir yorumda bulunulabilir.
Nihai olarak;
Bu içeriğimizde YARP ile web uygulamalarında performans artışı ve etkili bir ölçeklenebilirlik sağlayacak olan load balancing davranışını incelemiş ve özelliklerini değerlendirmiş bulunuyoruz.
İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…
Not : Örnek çalışmaya aşağıdaki github adresinden erişebilirsiniz.
https://github.com/gncyyldz/Load_Balancing_Scale_Out_With_YARP
