使用 Nginx 做 Load Balancer

當流量達到一定程度時,一台 Web Server 可能會不夠力,或是為了避免 Web Server 掛掉導致服務中斷,這時就需要 Load Balancer 。

而可以做到 Load Balancer 的工具也不少,但如果只是單純要做 Web 的 Load Balance 的話,用 Nginx 內建的 Load Balancer 就相當好用,而且設定上簡單。

實作環境

  • Load Balancer
    • IP:192.168.2.200
    • FQDN:web.dtask.idv.tw
  • Web Server 1
    • IP:192.168.2.201
    • FQDN:web1.dtask.idv.tw
  • Web Server 2
    • IP:192.168.2.202
    • FQDN:web2.dtask.idv.tw

實作

既然這次是要用 Nginx 當 Load Balancer ,所以在 Load Balance 那台當然就一定是要先裝好 Nginx 囉,Web 1、Web 2 要裝 Nginx 或 Apache 都可以,這邊還是以 Nginx 當設定範例,先假設三台都裝好 Nginx 了。

Nginx Load Balancer 有三種模式

  • round-robin:標準(預設)輪詢方式
  • least-connected:當連線進來時會把 Request 導向連線數較少的 Server
  • ip-hash:依據 Client IP 來分配到不同台 Server

以上三種模式也都可以自行設定權重(weight),讓效能較好的 Server 吃比較更多的負載。

Web Server 1 設定

依據自行的環境設定,這邊主要是 server_name 不能是對外的那一個,因為那個需要給 Load Balancer ,但如果此 Server 只會有一個 Web Service 那也可以不設定 server_name。

1
vi /etc/nginx/conf.d/default.conf
1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
server_name web1.dtask.idv.tw;
access_log /var/log/nginx/web1.dtask.idv.tw.access.log main;
error_log /var/log/nginx/web1.dtask.idv.tw.error.log warn;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}

Web Server 2 設定

如果 Web Server 1 一樣,依據自行的環境設定。

1
vi /etc/nginx/conf.d/default.conf
1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
server_name web2.dtask.idv.tw;
access_log /var/log/nginx/web1.dtask.idv.tw.access.log main;
error_log /var/log/nginx/web1.dtask.idv.tw.error.log warn;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}

Load Balancer 設定

1
vi /etc/nginx/conf.d/default.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
upstream myweb {
server web1.dtask.idv.tw weight=3;
server web2.dtask.idv.tw weight=2;
}
server {
listen 443 ssl;
server_name web.dtask.idv.tw;
access_log /var/log/nginx/web.dtask.idv.tw.access.log main;
error_log /var/log/nginx/web.dtask.idv.tw.error.log warn;
ssl_certificate web.dtask.idv.tw.crt;
ssl_certificate_key web.dtask.idv.tw.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
location / {
proxy_pass http://myapp;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

使用 upstream 來設定要做分流的 Server Name、IP,而 weight 則是設定權重,以上面範列表示每五個 Request 裡會有三個跑去 Web 1,有兩個跑去 Web 2,使用此方式來讓效能較好的 Server 吃多一點流量。

而設定檔的 server 區段則跟一般設定 Web Server 幾乎一樣,不一樣的地方則是透過 proxy pass 的方式將 Request 導過去,而導向的網址則是在 upstream 所自訂的名稱 “myapp”。

這邊另外設定三個 proxy_set_header 是因為透過 Load Balancer 重導了,那對 Web 1 以及 Web 2 來說, Client IP 會是 Load Balancer 的 IP ,這樣對於需要抓 Client IP 是錯誤的,所以利用 proxy_set_header 將真正的 Client IP 設定在 Header 。

以上是使用 round-robin 這個預設的方式,如果要使用另外兩個機制的話只要在 upstream 裡面加上一行設定值即可。

least-connected

1
2
3
4
5
upstream myweb {
least_conn;
server web1.dtask.idv.tw weight=3;
server web2.dtask.idv.tw weight=2;
}

ip-hash

1
2
3
4
5
upstream myweb {
least_conn;
server web1.dtask.idv.tw weight=3;
server web2.dtask.idv.tw weight=2;
}

另外正常情況只會有 Load Balancer 那台有設定 DNS 且對外,所以同時需要在 Load Balancer 那台上設定 hosts ,這樣 Nginx 才會知道要導向哪邊,但如果做分流的 Server 只有一個 Web Service ,那也可以在 upstream 直接使用 IP 。

1
vi /etc/hosts

加入

1
2
web1.dtask.idv.tw 192.168.2.201
web2.dtask.idv.tw 192.168.2.202

測試

經過以上簡單的設定後,一個 Load Balancer 機制就完成了,可以使用 ab test 來打 Load Balancer 那台,同時去 Web 1、Web 2 看 access log ,成功的話會發現兩台 Web 會一起分擔 ab 打過來的 Request 。