Nginx ve NodeJS ile Load Balancing

vagrant ile calismaya baslamak

Bugün, "Yük Dengeleme" yani ecnebilerin deyimiyle "Load Balancing" kavramını Nginx ve NodeJS özelinde inceleyeceğiz.

Neden yük dengeleme yapmamız gerektiğine, "bir elin nesi var iki elin sesi var" diyerek iyi bir cevap vermiş atalarımız. Aynen öyle. Eğer web uygulamanız tek bir sunucu üzerinde çalışıyorsa ve tüm yükü bu sunucu çekiyorsa haliyle biraz zorlanacatır. Ancak siz bu yükü, aynı işi yapan birden fazla sunucuya paylaştırırsanız, iş çok daha hızlı tamamlanacaktır.

Senaryomuz şöyle olsun;

Üç adet sunucumuz bulunsun. Sunuculardan biri yük paylaşımının yapılacağı, diğer ikisi ise web uygulamasının bulunacağı sunucular olsun.

Server 1

Nginx web sunucusunun kurulu olacağı ve gelen isteklerin diğer web sunucularına nasıl paylaştırılacağına karar veren sunucumuz.

name: loadbalancer
ip: 192.168.1.11

Server 2

Nodejs web uygulamasının bulunacağı sunucu.

name: web1
ip: 192.168.1.12

Server 3

Nodejs web uygulamasının bulunacağı diğer sunucu.

name: web2
ip: 192.168.1.13

Vagrant Yapılandırması

Öncelikle dizin planımızı şöyle hazırlayalım. Resimdeki gibi üç ayrı dizin oluşturalım ve her birinin içerisine Vagrantfile adında birer dosya oluşturalım.

Sakarya Üniversitesi Javascript Eğitimi

Vagrantfile içeriklerimiz aşağıdaki gibi olsun;

"Server1" için Vagrantfile Tanımlaması

NAME = 'loadbalancer'
IP = "192.168.1.11"

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.hostname = NAME
  config.vm.network "public_network", ip: IP
  config.vm.provider "virtualbox" do |vb|
    vb.customize ["modifyvm", :id, "--memory", '2048']
  end
end

"Server2" için Vagrantfile Tanımlaması

NAME = 'web1'
IP = "192.168.1.12"

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.hostname = NAME
  config.vm.network "public_network", ip: IP
  config.vm.provider "virtualbox" do |vb|
    vb.customize ["modifyvm", :id, "--memory", '2048']
  end
end

"Server3" için Vagrantfile Tanımlaması

NAME = 'web2'
IP = "192.168.1.13"

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.hostname = NAME
  config.vm.network "public_network", ip: IP
  config.vm.provider "virtualbox" do |vb|
    vb.customize ["modifyvm", :id, "--memory", '2048']
  end
end

Vagrantfile içeriklerinde değişien tek şey IP adresleri ve sunucu isimleri oldu. Dilerseniz ek konfigürasyonlar ekleyebilirsiniz elbette.

Şimdi Vagrant sunucularımızı ayağa kaldıralım.

"Server1" için

server1 adlı dizine gidelim ve aşağıdaki komutu çalıştıralım.

vagrant up

"Server2" için

server2 adlı dizine gidelim ve aşağıdaki komutu çalıştıralım.

vagrant up

"Server3" için;

server3 adlı dizine gidelim ve aşağıdaki komutu çalıştıralım.

vagrant up

Sunucu Konfigürasyonları

Load balancer sunucumuz ve uygulama sunucularımız için aşağıdaki konfigürasyonları yapmamız gerekiyor.

"Server2" ve "Server3" için

Aşağıdaki işlemleri server2 ve server3 için ayrı ayrı yapmamız gerekiyor. Bu iki sunucumuzda da aynı web uygulaması bulunacak. Aynı kurulumlar yapılacak. Web uygulaması NodeJS ile çalışacak. Hızlı bir kurulum yapıp test edebilmek adına express ile çalışacağız.

NodeJS Kurulumu

vagrant ssh
sudo apt-get update
sudo apt-get install nodejs npm -y
sudo ln -s /usr/bin/nodejs /usr/bin/node

Express Kurulumu ve Uygulamayı Ayağa Kaldırmak

sudo npm install express-generator -g
sudo mkdir /var/www && cd /var/www
sudo express .
sudo npm install
npm start

Uygulamamızı ayağa kaldırdık. Hemen test edelim. Web tarayıcımızdan, web1 ve web2 sunucularımızda koşturan NodeJS uygulamasına erişelim.

http://192.168.1.12:3000 veya http://192.168.1.13:3000

adreslerine erişmeye çalıştığımızda express'in aşağıdaki karşılama ekranı ile karşılaşacağız. Eğer iki IP adresinde de bu ekran ile karşılaşıyorsak nodejs uygulamasını başarıyla çalıştırmışız demektir.

Express welcome page

Server 1

Aşağıdaki komut ile sunucumuzun komut satırı arayüzüne erişelim.

vagrant ssh

Nginx kurulumunu yapalım.

sudo apt-get install nginx -y

Nginx konfigürasyonunu yapalım.

sudo vi /etc/nginx/sites-available/default

Nginx'in varsayılan ayarlarının bulunduğu dosyayı bir komut satırı editörü olan vi ile açtık. Şimdi bu dosyanın içeriğini tamamen silelim ve aşağıdaki kodları yapıştıralım.

Vi editörünü kullanarak dosya üzerinde değişiklik yapabilmek için insert moduna geçmeniz gerekiyor. Klavyenizden i tuşuna basarak bu moda geçebilirsiniz. Insert modundan çıkmak için escape tuşuna basmalısınız. Herhangi satırı komple simek için, insert modunda değilken d tuşuna ardarda iki kere basmalısınız. Dosyayı kaydedip çıkmak için ise yine insert modunda değilken :wq yazıp enter tuşuna basmanız gerekiyor. Kaydetmeden çıkmak için ise :q!. Daha fazlası şurada.

upstream servers {
  server 192.168.1.12:3000;
  server 192.168.1.13:3000;
}

server {
  listen 80;
  location / {
      proxy_pass http://servers;
  }
}

Yukarıdaki kodun upstream kısmında bulunan IP adresleri web uygulamalarımızın çalıştığı sunucunun ip adresini ve dinlediği portu belirtmekte.

Kaydedip çıkalım ve Nginx'i yeniden başlatalım.

sudo service nginx reload

Şimdi web tarayıcımıza gidelim loadbalancer sunucumuzun IP adresine erişmeye çalışalım.

http://192.168.1.11/

Express welcome page

Eğer bu ekran ile karşılaşıyorsanız, hayırlı olsun nur topu gibi bir load balancer'ınız oldu.

"Yok arkadaş ben inanmadım, kanıtla" dersen de şöyle bir şey yapabiliriz. Web uygulamamızın koştuğu iki ayrı sunucu vardı. Bu sunucuların döndüğü cevabı değiştirelim ve tekrar test edelim. Göreceksiniz ki her refresh yapışınızda farklı sunucuya yönlendirileceksiniz.

Server 2 için (web1)

vagrant ssh
sudo vi routes/index.js

Aşağıdaki satırı,

res.render('index', { title: 'Express' });

Aşağıdaki satır ile değiştirelim.

res.render('index', { title: 'Served from Web1' });

Server 3 için (web2)

vagrant ssh
sudo vi routes/index.js

Aşağıdaki satırı,

res.render('index', { title: 'Express' });

Aşağıdaki satır ile değiştirelim.

res.render('index', { title: 'Served from Web2' });

Şimdi http://192.168.1.11/ adresine gidin ve sayfayı hızlıca yenileyin. Göreceksiniz ki en müsait sunucu hangisi ise ona yönlendirileceksiniz.

served from web1 server1

served from web1 server2

Performans testleri

Şimdi load balancerdan önce ve sonraki performans durumuna bakalım.

Apache Benchmark aracı ile testimizi yapalım.

n: Gerçekleştirilecek istek sayısı
c: Bir seferde gerçekleştirilecek birden çok istek sayısı.

Load balancer olmadan

ab -n 2000 -c 100 http://192.168.1.12:3000/

Sonuç

Server Software:
Server Hostname:        192.168.1.12
Server Port:            3000

Document Path:          /
Document Length:        197 bytes

Concurrency Level:      100
Time taken for tests:   16.932 seconds
Complete requests:      2000
Failed requests:        0
Total transferred:      786000 bytes
HTML transferred:       394000 bytes
Requests per second:    118.12 [#/sec] (mean)
Time per request:       846.616 [ms] (mean)
Time per request:       8.466 [ms] (mean, across all concurrent requests)
Transfer rate:          45.33 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   1.4      0      10
Processing:    55  826 138.7    819    1171
Waiting:       55  825 138.7    819    1171
Total:         57  827 138.1    820    1172

Percentage of the requests served within a certain time (ms)
  50%    820
  66%    857
  75%    894
  80%    916
  90%    954
  95%   1017
  98%   1086
  99%   1154
 100%   1172 (longest request)

Load balancer ile

ab -n 2000 -c 100 http://192.168.1.11/

Sonuç

Server Software:        nginx/1.4.6
Server Hostname:        192.168.1.11
Server Port:            80

Document Path:          /
Document Length:        197 bytes

Concurrency Level:      100
Time taken for tests:   12.773 seconds
Complete requests:      2000
Failed requests:        0
Total transferred:      846000 bytes
HTML transferred:       394000 bytes
Requests per second:    156.59 [#/sec] (mean)
Time per request:       638.629 [ms] (mean)
Time per request:       6.386 [ms] (mean, across all concurrent requests)
Transfer rate:          64.68 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   1.9      0      19
Processing:    39  622 182.3    592    1024
Waiting:       39  622 182.3    592    1024
Total:         41  623 182.1    595    1024

Percentage of the requests served within a certain time (ms)
  50%    595
  66%    672
  75%    743
  80%    821
  90%    900
  95%    938
  98%    967
  99%    974
 100%   1024 (longest request)

Geçen Zaman

Test sonuçlarına baktığımızda 2000 isteğin karşılanması için;

Load balancer olmadan: 16.932sn

Load balancer ile: 12.773sn

Saniyede karşılanan istek sayısı

Load balancer olmadan: 118.12

Load balancer ile: 156.59

Fark ortada. Toplam 2000 istekte 4 saniyenin biraz üzerinde kârımız var ve saniyede 38 tane fazladan istek işlemiş olduk.