Nginx&Keepalived 實現高可用

微笑面對生活發表於2019-02-25

keepalived是什麼

Keepalived 是一種高效能的伺服器高可用或熱備解決方案, Keepalived 可以用來防止伺服器單點故障的發生,通過配合 Nginx 可以實現 web 前端服務的高可用。雖然nginx的抗壓性很強,很少出現當機,但是如果不做熱備,nginx一掛服務都會掛掉,所以熱備是必須的,當然,根據自己的實際業務需求來決定。

keepalived 原理

keepalived是以VRRP協議為實現基礎的,VRRP全稱Virtual Router Redundancy Protocol(虛擬路由冗餘協議)

keepalived是以VRRP協議為實現基礎的,VRRP全稱Virtual Router Redundancy Protocol,即虛擬路由冗餘協議

虛擬路由冗餘協議,可以認為是實現路由器高可用的協議,即將N臺提供相同功能的路由器組成一個路由器組,這個組裡面有一個master和多個backup,master上面有一個對外提供服務的vip(該路由器所在區域網內其他機器的預設路由為該vip),master會發組播,當backup收不到vrrp包時就認為master宕掉了,這時就需要根據VRRP的優先順序選舉一個backup當master。這樣的話就可以保證路由器的高可用了。

keepalived主要有三個模組,分別是core、check和vrrp。core模組為keepalived的核心,負責主程式的啟動、維護以及全域性配置檔案的載入和解析。check負責健康檢查,包括常見的各種檢查方式。vrrp模組是來實現VRRP協議的。

keepalived 結構

keepalived只有一個配置檔案keepalived.conf。裡面主要包括以下幾個配置區域,分別是global_defsvrrp_instance、和virtual_server

global_defs區域

主要是配置故障發生時的通知物件以及機器標識,通俗點說就是出狀況後發郵件通知的一個配置。

global_defs {
    notification_email {    故障發生時給誰發郵件通知
        a@abc.com
        b@abc.com
        ...
    }
    notification_email_from alert@abc.com    通知郵件從哪個地址發出
    smtp_server smtp.abc.com        smpt_server 通知郵件的smtp地址。
    smtp_connect_timeout 30       連線smtp伺服器的超時時間
    enable_traps      開啟SNMP陷阱
    router_id host163      標識本節點的字條串,通常為hostname
}
複製程式碼
vrrp_instance區域

vrrp_instance用來定義對外提供服務的VIP區域及其相關屬性

vrrp_instance VI_1 {
    state MASTER         state 可以是MASTER或BACKUP
    interface ens33        本機網路卡的名字
    virtual_router_id 51      取值在0-255之間,用來區分多個instance的VRRP組播
    priority 100            權重
    advert_int 1       發VRRP包的時間間隔,即多久進行一次master選舉
    authentication {        身份認證區
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {        虛擬ip地址
        192.168.27.160
    }
}
複製程式碼
virtual_server

超大型的LVS中用到,我在這裡不用它。

virtual_server 192.168.200.100 443 {
    delay_loop 6                                延遲輪詢時間(單位秒)
    lb_algo rr                                 後端除錯演算法
    lb_kind NAT                               LVS排程型別
    persistence_timeout 50 
    protocol TCP

    real_server 192.168.201.100 443 {                              真正提供服務的伺服器
        weight 1
        SSL_GET {
            url {
              path /
              digest ff20ad2481f97b1754ef3e12ecd3a9cc         表示用genhash算出的結果
            }
            url {
              path /mrtg/
              digest 9b3a0c85a887a256d6939da88aabd8cd
            }
            connect_timeout 3
            nb_get_retry 3                                                           重試次數
            delay_before_retry 3                                                下次重試的時間延遲
        }
    }
}
複製程式碼

keepalived安裝

yum install keepalived -y 
複製程式碼

環境模擬

我準備了四個主機,ip是192.168.27.166-169,都搭建nginx服務,然後把166和167分別當主備機。

nginx配置
upstream centos_pool{
        server s168:80;
        server s169:80;
}
server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    location / {
       # root   /usr/share/nginx/html;
       # index  index.html index.htm;
        proxy_pass http://centos_pool;
    }
複製程式碼

四個主機都用該配置啟動,看起來是4個nginx服務,在這個例項裡不是這樣的,不同的是166和167是nginx服務,168和169是web服務(用nignx開放80埠來模仿服務)。

換句話說,166和167用來做負載均衡,168和169是web服務主機。

我在168和169的主機/usr/share/nginx/html/index.html裡做了簡單的標識:

Nginx&Keepalived 實現高可用
Nginx&Keepalived 實現高可用
Nginx&Keepalived 實現高可用
Nginx&Keepalived 實現高可用

注意對照位址列看變化。就把168和169當作普通的web服務。

好了,下來配置keepalived

配置keepalived

166主機配置:

! Configuration File for keepalived

global_defs {
   router_id LVS_DEVEL
}

vrrp_instance VI_1 {
    state MASTER
    interface ens33
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.27.160
    }
}
複製程式碼

可以說是最核心的配置了,也是最簡配置,想要配置郵件服務可以對照上文中的模組介紹註釋去弄。lvs配置也一樣。

167熱備配置:

! Configuration File for keepalived

global_defs {
   router_id LVS_DEVEL
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens33
    virtual_router_id 51
    priority 50
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.27.160
    }
}
複製程式碼

可以看出,除了state MASTER/BACKUPpriority 100屬性不同,其他都相同也必須相同。
好了,現在啟動keepalived,從ip路由就能看出誰是主機(接管節點的網路卡會繫結VIP地址192.168.27.160)

測試

1. 訪問虛擬ip:
Nginx&Keepalived 實現高可用
2. 檢視主機路由
166主機
167主機
3. 情景演練

一切配置正常後,我把166上的nginx停了,會發生什麼呢?

167會接手虛擬ip地址,完成雙機熱備任務嗎?答案是不會,因為你回頭看看,keepalived沒有一點是和nginx有關係的,兩服務互不影響。keepalived其實是監控master上的keepalived的心跳的。所以,我把keepalived服務也關掉。

[root@s166 keepalived]# nginx -s stop
[root@s166 keepalived]# service keepalived stop
Redirecting to /bin/systemctl stop keepalived.service
複製程式碼

然後再檢視166的ip路由

[root@s166 keepalived]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:7b:59:07 brd ff:ff:ff:ff:ff:ff
    inet 192.168.27.166/24 brd 192.168.27.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::83ee:6998:a0d4:7974/64 scope link tentative dadfailed 
       valid_lft forever preferred_lft forever
    inet6 fe80::2513:4c77:5da7:f031/64 scope link tentative dadfailed 
       valid_lft forever preferred_lft forever
    inet6 fe80::99b3:c79:5377:c3fe/64 scope link tentative dadfailed 
       valid_lft forever preferred_lft forever
複製程式碼

可以看到是沒有192.168.27.160這個虛擬ip了。那我們再看一下167是否有,如果有,證明配置生效。

Nginx&Keepalived 實現高可用

那我們繼續重新整理192.168.27.160這個連結,會發現執行正常。

Nginx&Keepalived 實現高可用

指令碼優化

既然keepalived和nginx沒有關聯,那我們可以寫個指令碼監聽nginx,如果nginx掛了,然後用命令把keepalived也停掉,這樣就會完成雙機熱備的任務。

建立指令碼check_nginx.sh

#!/bin/bash
A=`ps -C nginx --no-header | wc -l`
if [ $A -eq 0 ];then
    echo "restart the nginx server" >> /etc/keepalived/keepalived_error.log
    /usr/sbin/nginx
    sleep 2
    if [ `ps -C nginx --no-header | wc -l` -eq 0 ];then
	echo "keepalived is closed" >> /etc/keepalived/keepalived_error.log
 	/usr/bin/ps -ef | grep "keepalived" | grep -v "grep" | cut -c 9-15 | xargs kill -9
	echo /usr/bin/ps -ef | grep "keepalived" >> /etc/keepalived/keepalived_error.log
    fi
fi
複製程式碼

對了,記住修改指令碼可執行許可權。為什麼要重定向呢,因為echo不會列印在控制檯上,我們可以跟蹤keepalived_error.log來判斷指令碼是否執行。

Nginx&Keepalived 實現高可用

那既然這樣,我們的指令碼如何控制時間呢?sleep的時間也得控制好,在保持高效能下轉換越快越好。所以,我們把指令碼載入到keepalived配置中,keepalived每進行一次選舉,就執行一次指令碼。

把指令碼新增到keepalived任務中

! Configuration File for keepalived 
 
global_defs { 
   router_id LVS_DEVEL 
} 
 
vrrp_instance VI_1 {
    state MASTER
    interface ens33
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.27.160
    }
    track_script {
       chk_nginx  # nginx存活狀態檢測指令碼
    }
}

vrrp_script chk_nginx {
       script "/etc/keepalived/check_nginx.sh"
       interval 2 
       weight -20
}
複製程式碼

同理,BACKUP主機也要配置

! Configuration File for keepalived

global_defs {
   router_id LVS_DEVEL
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens33
    virtual_router_id 51
    priority 50
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.27.160
    }
    track_script {
       chk_nginx
    }
}
vrrp_script chk_nginx {
       script "/etc/keepalived/check_nginx.sh"
       interval 2 
       weight -20
}
複製程式碼
優化後測試
Nginx&Keepalived 實現高可用

日誌也會發現一直有在執行指令碼。

那如何測試不重啟nginx,讓它直接關keepalived,然後啟用BACKUP呢。我把那行重啟nginx指令碼註釋掉。然後再跑。

#!/bin/bash
A=`ps -C nginx --no-header | wc -l`
if [ $A -eq 0 ];then
#    echo "restart the nginx server" >> /etc/keepalived/keepalived_error.log
#    /usr/sbin/nginx
#    sleep 2
#    if [ `ps -C nginx --no-header | wc -l` -eq 0 ];then
	echo "keepalived is closed" >> /etc/keepalived/keepalived_error.log
 	/usr/bin/ps -ef | grep "keepalived" | grep -v "grep" | cut -c 9-15 | xargs kill -9
	echo /usr/bin/ps -ef | grep "keepalived" >> /etc/keepalived/keepalived_error.log

#    fi
fi
複製程式碼
Nginx&Keepalived 實現高可用

說明指令碼和keepalived整合沒問題,將註釋去掉。任務完成。

思考

[root@s166 keepalived]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:7b:59:07 brd ff:ff:ff:ff:ff:ff
    inet 192.168.27.166/24 brd 192.168.27.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet 192.168.27.160/32 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::83ee:6998:a0d4:7974/64 scope link tentative dadfailed 
       valid_lft forever preferred_lft forever
    inet6 fe80::2513:4c77:5da7:f031/64 scope link tentative dadfailed 
       valid_lft forever preferred_lft forever
    inet6 fe80::99b3:c79:5377:c3fe/64 scope link tentative dadfailed 
       valid_lft forever preferred_lft forever
複製程式碼
[root@s167 keepalived]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:d4:26:34 brd ff:ff:ff:ff:ff:ff
    inet 192.168.27.167/24 brd 192.168.27.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet 192.168.27.160/32 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::99b3:c79:5377:c3fe/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
複製程式碼

雖然我們把s166都關閉了keepalived,但是ip路由還會有虛擬ip 192.168.27.160,這個可能是keepalived並沒有完全終止。但是我在重新整理的時候沒有出現錯誤頁面,證明並沒有影響到服務的正常執行。不屬於裂腦問題。我將指令碼中的強制killkeepalived的操作換成更委婉的/usr/sbin/service keepalived stop,解決了該問題.

擴充:高可用之裂腦問題

相關文章