為了避免服務單點,也為了負載均衡,我們會加一層 Nginx 層。這個 Nginx 層要有多於一台機器,不然它自身也成為一個單點。
最初加 Nginx 層會變成這樣:
schaepher.com
+
|
+-------+
|
v
+---+---+ +-------+
| | | |
| Nginx | | Nginx |
| | | |
+--+----+ +---+---+
| |
+---------+---+-------------+-+-----------+
| | | |
v v v v
+---+----+ +---+----+ +---+----+ +---+----+
| | | | | | | |
| Real | | Real | | Real | | Real |
| Server | | Server | | Server | | Server |
| | | | | | | |
+--------+ +--------+ +--------+ +--------+
如果有一台 Real Server 發生故障,則 Nginx 就不會轉發到故障的機器,保證服務正常進行。
但是如果主 Nginx 故障了呢?
故障切換的五種方式
-
客戶端自己配置多個 Nginx IP,故障時自己切換。
優點:
- 后端不需要做調整
缺點:
- 客戶端要自己維護 IP 列表。
- 客戶端要實現一套應對故障的邏輯。
- 不能用於用戶和服務之間,只能用於服務與服務之間。
-
單一服務的情況下,把另一台 Nginx 服務器的 IP 發給用戶,讓用戶訪問這個 IP。
優點:
- 簡單
缺點:
- 只有內部系統用戶才可能接受這種做法。面向外部用戶的時候,外部用戶不會接受。
-
運維人員手動修改 DNS 解析,將域名解析到另一台 Nginx 服務器。或者程序定期檢測,發現有問題就自動發起修改 DNS 解析的請求。
解決了什么問題?
- 用戶需要手動修改 hosts 或者需要切換 URL 的問題。
沒有解決什么問題?
- 在 DNS 解析生效之前,服務完全不可用。
- 客戶端會緩存 DNS 解析,也要等客戶端緩存過期。
-
將域名解析到所有 Nginx 服務器。程序定期做檢測,當發現有問題的時候發起請求,讓 DNS 將故障的 IP 移除掉。
DNSPod 自帶這個功能
解決了什么問題?
- 在 DNS 解析生效之前,服務完全不可用的問題。
沒有解決什么問題?
- 仍然有部分用戶無法訪問服務。
-
使用虛 IP(Virtual IP Address,以下稱為 VIPA)。域名固定解析到這個 IP,當 VIPA 所在服務器故障時,讓 VIPA 自動漂移到另一台服務器。
解決了什么問題?
- 不需要修改 DNS 解析,秒級別的生效延遲。
- 用戶無感知。
帶來了什么問題?
- 多了一個故障點,即使得 VIPA 自動漂移的那個程序,如 Keepalived。
- 需要額外申請一個 IP 作為 VIPA 。
- 涉及存儲的時候,由於切換速度很快,可能會導致數據不一致。
VIP 是什么
全稱為 VIPA(Virtual IP Address),虛擬 IP 地址。
通常一個 IP 只能綁定在某台機器的一個網卡上。一旦這台機器故障,根據 IP 找到這台機器的請求就會得不到響應。
正常
schaepher.com
192.168.1.101
+
|
|
|
v
+---------------+ +---------------+
| | | |
| 192.168.1.101 | | 192.168.1.102 |
| | | |
+---------------+ +---------------+
故障
schaepher.com
192.168.1.101
+
+
+-+ 不通
|
v
故障
+---------------+ +---------------+
| | | |
| 192.168.1.101 | | 192.168.1.102 |
| | | |
+---------------+ +---------------+
而 VIPA 雖然最終也會配置在一台機器上,但一旦這台機器故障,備用機器就會把這個 IP 搶過去配置到網卡上(VIPA 漂移到備機)。這樣可以在 IP 不變的情況下,讓請求轉移到正常的機器,減少服務故障時間。
正常
schaepher.com
192.168.1.100(VIPA)
+
|
|
|
v
+---------------+ +---------------+
| 192.168.1.100 | | |
| 192.168.1.101 | | 192.168.1.102 |
| | | |
+---------------+ +---------------+
故障
schaepher.com
192.168.1.100(VIPA)
+
|
+-------------------------+
|
v
故障
+---------------+ +---------------+
| | | 192.168.1.100 |
| 192.168.1.101 | | 192.168.1.102 |
| | | |
+---------------+ +---------------+
而 Keepalive 則是實現 VIPA 漂移的一種工具。
另一種是比較復雜的 Heartbeat。
Keepalived
Keepalived 實現 VIPA 漂移的基礎是 VRRP 協議。
VRRP 最早用於路由器,將多台路由器設備虛擬成一台路由器。每台設備都有一個角色。角色有兩種 Master 和 Backup。Master 會持有虛 IP。每台設備都有一個權重,權重最高的正常設備會被選舉為 Master。
現在 Keepalived 實現了 VRRP 協議,使得可以將其用在其他設備上。
將 Keepalived 安裝在一組設備上,它們之間會互相通信來檢測狀態。如果發現 Master 超過一定時間沒有反應,則重新選舉 Master。
Master 可能直接故障沒有反應,也可能只是服務出現問題。如果是后一種情況,需要主動關閉 Keepalived 進程。
以下展示配置文件。
IP | 所屬 | 角色 |
---|---|---|
192.168.1.100 | Master | |
192.168.1.101 | 主機 A | Master |
192.168.1.102 | 主機 B | Backup |
192.168.1.101 - 主機 A - Master
/etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
script_user root
router_id LVS_DEVEL
vrrp_skip_check_adv_addr
vrrp_strict # 嚴格模式使得無法使用 unicast_peer 配置,好處是無需指定其他機器的 IP
vrrp_garp_interval 0
vrrp_gna_interval 0
}
vrrp_script chk_http_port {
script "/etc/keepalived/chk_nginx.sh" # 檢測當前機器的服務是否故障,如果故障則關閉 keepalived
interval 2
weight -5
fall 2
rise 1
}
vrrp_instance VI_1 {
state MASTER # 主備配置不一致
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass aaaaaaa # 主備該配置必須一樣
}
virtual_ipaddress {
192.168.1.100
}
track_script {
chk_http_port # 在 vrrp_script 定義的名字
}
notify_master "/etc/keepalived/notify.sh 192.168.1.100 master" # 當這台機器成為 Master 時發送通知
notify_backup "/etc/keepalived/notify.sh 192.168.1.100 backup"
notify_fault "/etc/keepalived/notify.sh 192.168.1.100 fault"
}
192.168.1.102 - 主機 B - Backup
只需改兩個配置的值
/etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
script_user root
router_id LVS_DEVEL
vrrp_skip_check_adv_addr
vrrp_strict
vrrp_garp_interval 0
vrrp_gna_interval 0
}
vrrp_script chk_http_port {
script "/etc/keepalived/chk_nginx.sh"
interval 2
weight -5
fall 2
rise 1
}
vrrp_instance VI_1 {
state BACKUP # 主備配置不一致
interface eth0
virtual_router_id 51
priority 90 # 備機權重比主機低
advert_int 1
nopreempt # 防止爭搶 VIPA,只有 state 設置為 BACKUP 才能使用該選項
authentication {
auth_type PASS
auth_pass aaaaaaa
}
virtual_ipaddress {
192.168.1.100
}
track_script {
chk_http_port
}
notify_master "/etc/keepalived/notify.sh 192.168.1.100 master" # 當這台機器成為 Master 時發送通知
notify_backup "/etc/keepalived/notify.sh 192.168.1.100 backup"
notify_fault "/etc/keepalived/notify.sh 192.168.1.100 fault"
}
/etc/keepalived/chk_nginx.sh
#!/bin/bash
if [[ -z "$(ps aux | grep "nginx: master process" | grep -v grep)" ]]; then
systemctl restart nginx # 嘗試重啟 nginx
sleep 5
if [[ -z "$(ps aux | grep "nginx: master process" | grep -v grep)" ]]; then
# 啟動失敗則關閉 keepalived,觸發 VIPA 漂移
systemctl stop keepalived
fi
fi
/etc/keepalived/notify.sh
#!/bin/bash
# contact=xxx.schaepher.com
notify() {
vip=$1
role=$2
mailSubject="$(hostname) to be ${role}: ${vip} floating"
mailBody="$(date '+%F %H:%M:%S'): vrrp transition, $(hostname changed to be ${role})"
# echo ${mailBody} | mail -s "${mailSubject}" "${contact}"
echo ${mailSubject} >> /var/log/keepalived.mail
echo ${mailBody} >> /var/log/keepalived.mail
echo "" >> /var/log/keepalived.mail
}
notify $1 $2
擴展
-
提高利用率
由於一個 VIPA 只能配置在一台機器上,如果共有兩台機器,則浪費了 50% 的資源。
如果要提高資源的利用率,可以再申請一個 VIPA。把備機配置為 Master,把主機配置為 Backup。 -
VIPA 爭搶
由於 nopreempt 只能配置在 BACKUP 上,如果 state 為 MASTER 的機器故障並恢復,則會把 VIPA 搶過去。
整個過程是:- 主機 A 故障
- VIPA 漂移到主機 B
- 主機 A 恢復
- VIPA 漂移到主機 A
這樣就會導致第四步多漂移了一次。而漂移可能會對服務有很短暫的影響。如果希望主機 A 恢復后,仍然讓主機 B 持有 VIPA,則要在主機的 Keepalived 啟動之前修改配置中的 state,改為 BACKUP。
-
避免丟包
在 VIPA 漂移到備機之間,短暫的時間內數據包仍然會發送到主機。如果主機能夠連上,則可以使用防火牆將數據包轉發到備機。然后停止 Keepalived 。iptables -F iptables -t nat -I PREROUTING -i eth0 -j DNAT --to-destination 192.168.1.102 iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE
-
數據庫的問題
數據庫如果使用雙主,在 VIPA 切換的時候,數據可能未同步完成,可能會造成自增 ID 沖突。
可以配置 Keepalived 等一段時間后再發送 ARP 請求,以此等待同步完成。
配置項是:vrrp_garp_master_delay 10
表示延遲 10 秒返送。