1. 程式人生 > 其它 >nginx keepalive 高可用 原理和實操 (圖解+秒懂+史上最全)

nginx keepalive 高可用 原理和實操 (圖解+秒懂+史上最全)

面試問題:Nginx 、haproxy如何實現高可用?

面試官:

既然使用nginx、haproxy 反向代理 HTTP服務,或者TCP服務,做到了上游服務的高可用

可問題是,如果 反向代理掛了,了怎麼辦?

答曰:

首先,其實反向代理的主機宕機概率很小,但是絕對不是沒有,任何事都不是絕對

但是,為了減少 反向代理 的 宕機概念,在我們的 高可用架構 上做了優化,進行了 反向代理的 高可用

具體的技術方案是:我們使用 nginx-keepalived雙機熱備機制,vip主機可以進行漂移,這樣主機掛掉了,還有備用機可以頂上

具體的vip漂移架構圖,如下:

Keepalived是什麼?

Keepalived起初是為LVS設計的,專門用來監控集群系統中各個服務節點的狀態,

它根據TCP/IP參考模型的第三、第四層、第五層交換機制檢測每個服務節點的狀態,如果某個伺服器節點出現異常,或者工作出現故障,Keepalived將檢測到,並將出現的故障的伺服器節點從集群系統中剔除,這些工作全部是自動完成的,不需要人工干涉,需要人工完成的只是修復出現故障的服務節點。

後來Keepalived又加入了VRRP的功能,VRRP(VritrualRouterRedundancyProtocol,虛擬路由冗餘協議)出現的目的是解決靜態路由出現的單點故障問題,通過VRRP可以實現網路不間斷穩定執行,因此Keepalvied一方面具有伺服器狀態檢測和故障隔離功能,另外一方面也有HAcluster功能。

健康檢查和失敗切換是keepalived的兩大核心功能。所謂的健康檢查,就是採用tcp三次握手,icmp請求,http請求,udp echo請求等方式對負載均衡器後面的實際的伺服器(通常是承載真實業務的伺服器)進行保活;而失敗切換主要是應用於配置了主備模式的負載均衡器,利用VRRP維持主備負載均衡器的心跳,當主負載均衡器出現問題時,由備負載均衡器承載對應的業務,從而在最大限度上減少流量損失,並提供服務的穩定性。

VRRP虛擬路由冗餘協議

虛擬路由冗餘協議(Virtual Router Redundancy Protocol,簡稱VRRP)是由IETF提出的解決區域網中配置靜態網關出現單點失效現象的路由協議,1998年已推出正式的RFC2338協議標準。

VRRP廣泛應用在邊緣網路中,它的設計目標是支援特定情況下IP資料流量失敗轉移不會引起混亂,允許主機使用單路由器,以及即使在實際第一跳路由器使用失敗的情形下仍能夠維護路由器間的連通性。

VRRP協議的來源

在現實的網路環境中。主機之間的通訊都是通過配置靜態路由或者(預設閘道器)來完成的,而主機之間的路由器一旦發生故障,通訊就會失效,因此這種通訊模式當中,路由器就成了一個單點瓶頸,為了解決這個問題,就引入了VRRP協議。

VRRP的核心概念

VRRP是一種路由容錯協議,也可以叫做備份路由協議。

在VRRP協議中,有兩組重要的概念:

  • VRRP路由器和虛擬路由器
  • 主控路由器和備份路由器

備份路由器(BACKUP):虛擬路由器中的其他物理路由器不擁有對外的虛擬IP,也不對外提供網路功能,僅接受MASTER的VRRP狀態通告資訊,這些路由器被稱為備份路由器。當主路由器失敗時,處於BACKUP角色的備份路由器將重新進行選舉,產生一個新的主路由器進入MASTER角色,繼續提供對外服務,整個切換對使用者來說是完全透明的。

虛擬路由器

VRRP路由器(物理實體)

什麼是VRRP路由器?

VRRP路由器是指執行VRRP的路由器,是物理實體;

虛擬路由器

什麼是虛擬路由器?

虛擬路由器是指VRRP協議建立的,是邏輯概念。一組VRRP路由器(物理實體)協同工作,共同構成一臺虛擬路由器。該虛擬路由器對外表現為一個具有唯一固定的IP地址和MAC地址的邏輯路由器。

虛擬路由器是VRRP備份組中所有路由器的集合,它是一個邏輯概念,並不是正真存在的。從備份組外面看備份組中的路由器,感覺組中的所有路由器就像一個 一樣,可以理解為在一個組中: 主路由器+所有備份路由器=虛擬路由器。

虛擬路由器有一個虛擬的IP地址和MAC地址。

主機將虛擬路由器當作預設閘道器。虛擬MAC地址的格式為00-00-5E-00-01-{VRID}。通常情況下,虛擬路由器迴應ARP請求使用的是虛擬MAC地址,只有虛擬路由器做特殊配置的時候,才回應介面的真實MAC地址。

主控路由器主路由器(MASTER):

什麼是主控路由器?

處於同一個VRRP組中的路由器具有兩種互斥的角色:主控路由器和備份路由器

一個VRRP組中有且只有一臺處於主控角色的路由器,可以有一個或者多個處於備份角色的路由器VRRP協議從路由器組中選出一臺作為主控路由器,負責ARP解析和轉發IP資料包,組中的其他路由器作為備份的角色並處於待命狀態。

虛擬路由器通過虛擬IP對外提供服務,而在虛擬路由器內部同一時間只有一臺物理路由器對外提供服務,這臺提供服務的物理路由器被稱為主路由器。一般情況下Master是由選舉演算法產生,它擁有對外服務的虛擬IP,提供各種網路功能,如:ARP請求,ICMP資料轉發等。

當由於某種原因主控路由器發生故障時,其中的一臺備份路由器能在瞬間的時延後升級為主控路由器,由於此切換非常迅速而且不用改變IP地址和MAC地址,故對終端使用者系統是透明的。

一個區域網絡內的所有主機都設定預設路由,當網內主機發出的目的地址不在本網段時,報文將被通過預設路由發往外部路由器,從而實現了主機與外部網路的通訊。

當預設路由器down掉(即埠關閉)之後,內部主機將無法與外部通訊,如果路由器設定了VRRP時,那麼這時,虛擬路由將啟用備份路由器,從而實現全網通訊。

題外話:ARP協議

地址解析協議,即ARP(Address Resolution Protocol),是根據IP地址獲取實體地址的一個TCP/IP協議

主機傳送資訊時將包含目標IP地址的ARP請求廣播到區域網絡上的所有主機,並接收返回訊息,以此確定目標的實體地址。

arp快取:收到返回訊息後將該IP地址和實體地址存入本機ARP快取中並保留一定時間,下次請求時直接查詢ARP快取以節約資源。

地址解析協議是建立在網路中各個主機互相信任的基礎上的,區域網絡上的主機可以自主傳送ARP應答訊息,其他主機收到應答報文時不會檢測該報文的真實性就會將其記入本機ARP快取;由此攻擊者就可以向某一主機發送偽ARP應答報文,使其傳送的資訊無法到達預期的主機或到達錯誤的主機,這就構成了一個ARP欺騙ARP命令可用於查詢本機ARP快取中IP地址和MAC地址的對應關係、新增或刪除靜態對應關係等。相關協議有RARP代理ARPNDP用於在IPv6中代替地址解析協議。

ARP協議工作過程

主機A的IP地址為192.168.1.1,MAC地址為0A-11-22-33-44-01;

主機B的IP地址為192.168.1.2,MAC地址為0A-11-22-33-44-02;

當主機A要與主機B通訊時,地址解析協議可以將主機B的IP地址(192.168.1.2)解析成主機B的MAC地址,以下為工作流程:

第1步:根據主機A上的路由表內容,IP確定用於訪問主機B的轉發IP地址是192.168.1.2。然後A主機在自己的本地ARP快取中檢查主機B的匹配MAC地址。

第2步:如果主機A在ARP快取中沒有找到對映,它將詢問192.168.1.2的硬體地址,從而將ARP請求幀廣播到本地網路上的所有主機。源主機A的IP地址和MAC地址都包括在ARP請求中。本地網路上的每臺主機都接收到ARP請求並且檢查是否與自己的IP地址匹配。如果主機發現請求的IP地址與自己的IP地址不匹配,它將丟棄ARP請求。

第3步:主機B確定ARP請求中的IP地址與自己的IP地址匹配,則將主機A的IP地址和MAC地址對映新增到本地ARP快取中。

第4步:主機B將包含其MAC地址的ARP回覆訊息直接傳送回主機A。

第5步:當主機A收到從主機B發來的ARP回覆訊息時,會用主機B的IP和MAC地址對映更新ARP快取。本機快取是有生存期的,生存期結束後,將再次重複上面的過程。主機B的MAC地址一旦確定,主機A就能向主機B傳送IP通訊了。

VRRP協議選舉機制

VRRP路由器在執行過程中有三種狀態:

  1. Initialize狀態: 系統啟動後就進入Initialize,此狀態下路由器不對VRRP報文做任何處理;

  2. Master狀態;

  3. Backup狀態;

一般主路由器處於Master狀態,備份路由器處於Backup狀態。

VRRP使用選舉機制來確定路由器的狀態,優先順序選舉:
1.VRRP組中IP擁有者。如果虛擬IP地址與VRRP組中的某臺VRRP路由器IP地址相同,則此路由器為IP地址擁有者,這臺路由器將被定位主路由器。
2.比較優先順序。如果沒有IP地址擁有者,則比較路由器的優先順序,優先順序的範圍是0~255,優先順序大的作為主路由器
3.比較IP地址。在沒有Ip地址擁有者和優先順序相同的情況下,IP地址大的作為主路由器。

如下圖所示,虛擬IP為10.1.1.254,在VRRP組中沒有IP地址擁有者,則比較優先順序,很明顯RB和RA的優先順序要大於RC,則比較RA和RB的IP地址,RB的IP地址大。

所以RB為組中的主路由器。

選舉流程

路由器使用VRRP 功能後,會根據優先順序確定自己在備份組中的角色。

優先順序高的路由器成為Master 路由器,優先順序低的成為Backup 路由器。

Master 擁有對外服務的虛擬IP,提供各種網路功能,並定期傳送VRRP 報文,通知備份組內的其他裝置自己工作正常;

Backup 路由器只接收Master 發來的報文資訊,用來監控Master 的執行狀態。

當Master 失效時,Backup 路由器進行選舉,優先順序高的Backup 將成為新的Master 。

在搶佔方式下,當Backup路由器收到VRRP 報文後,會將自己的優先順序與報文中的優先順序進行比較。如果大於通告報文中的優先順序,則成為Master 路由器;否則將保持Backup狀態;

在非搶佔方式下,只要Master 路由器沒有出現故障,備份組中的路由器始終保持 Master 或Backup 狀態,Backup 路由器即使隨後被配置了更高的優先順序也不會成為Master 路由器;

如果Backup 路由器的定時器超時後仍未收到Master 路由器傳送來的VRRP報文,則認為Master 路由器已經無法正常工作,此時Backup 路由器會認為自己是Master 路由器,並對外發送VRRP報文。

備份組內的路由器根據優先順序選舉出Master 路由器,承擔報文的轉發功能。

Keepalived體系結構

Keepalived起初是為LVS設計的,由於Keeplalived可以實現對叢集節點的狀態檢測,

而IPVS可以實現負載均衡功能,因此,Keepalived藉助於第三方模組IPVS就可以很方便地搭建一套負載均衡系統。

在Keepalived當中IPVS模組是可配置的,如果需要負載均衡功能,可以在編譯Keepalived時開打負載均衡功能,也可以通過編譯引數關閉。

keepalived執行時,會啟動3個程序,分別為:core(核心程序),check和vrrp
- core:負責主程序的啟動,維護和全域性配置檔案的載入;
- check:負責健康檢查
- vrrp:用來實現vrrp協議

SchedulerI/OMultiplexer是一個I/O複用分發排程器,它負載安排Keepalived所有內部的任務請求;

Memory Mngt是一個記憶體管理機制,這個框架提供了訪問記憶體的一些通用方法;

Control Plane 是keepalived的控制版面,可以實現對配置檔案編譯和解析;

Core componets 這部分主要包含了5個部分;

  • Watchdog:是計算機可靠領域中極為簡單又非常有效的檢測工具,Keepalived正是通過它監控Checkers和VRRP程序的。
  • Checkers: 這是Keepalived最基礎的功能,也是最主要的功能,可以實現對伺服器執行狀態檢測和故障隔離。
  • VRRP Stack: 這是keepalived後來引用VRRP功能,可以實現HA叢集中失敗切換功能。負責負載均衡器之間的失敗切換FailOver;
  • IPVS wrapper:這個是IPVS功能的一個實現,IPVSwarrper模組將可以設定好的IPVS規則傳送的核心空間並且提供給IPVS模組,最終實現IPVS模組的負載功能。
  • Netlink Reflector:用來實現高可用叢集Failover時虛擬IP(VIP)的設定和切換,

IPVS核心模組

此模組是此文的重點。

Linux 的 IPVS核心模組基本上是一種高效的Layer-4交換機,它提供負載平衡的功能。

ipvs (IP Virtual Server) 實現了傳輸層負載均衡,也就是我們常說的4層LAN交換,作為 Linux 核心的一部分。

ipvs執行在主機上,在真實伺服器叢集前充當負載均衡器。

ipvs可以將基於TCP和UDP的服務請求轉發到真實伺服器上,並使真實伺服器的服務在單個 IP 地址上顯示為虛擬服務。

當一個TCP連線的初始SYN報文到達時,IPVS就選擇一臺伺服器,將報文轉發給它。

此後通過查發報文的IP和TCP報文頭地址,保證此連線的後繼報文被轉發到相同的伺服器。這樣,IPVS不用檢查到請求的內容再選擇伺服器,這就要求後端的伺服器組是提供相同的服務,不管請求被送到哪一臺伺服器,返回結果都應該是一樣的。但是在有一些應用中後端的伺服器可能功能不一,有的是提供HTML文件的Web伺服器,有的是提供圖片的Web伺服器,有的是提供CGI的Web伺服器。這時,就需要基於內容請求分發 (Content-Based Request Distribution),同時基於內容請求分發可以提高後端伺服器上訪問的區域性性。

當一個TCP連線的初始SYN報文到達時,IPVS就選擇一臺伺服器,將報文轉發給它。此後通過查發報文的IP和TCP報文頭地址,保證此連線的後繼報文被轉發到相同的伺服器。這樣,IPVS無法檢查到請求的內容再選擇伺服器,這就要求後端的伺服器組是提供相同的服務,不管請求被送到哪一臺伺服器,返回結果都應該是一樣的。但是在有一些應用中後端>的伺服器可能功能不一,有的是提供HTML文件的Web伺服器,有的是提供圖片的Web伺服器,有的是提供CGI的Web伺服器。這時,就需要基於內容請求分發 (Content-Based Request Distribution),同時基於內容請求分發可以提高後端伺服器上訪問的區域性性。

Keepalived對伺服器執行狀態和故障隔離的工作原理

Keepalived工作在TCP/IP參考模型的網路層/傳輸層/應用層:

網路層:

Keepalived通過ICMP協議向伺服器叢集中的每一個節點發送一個ICMP資料包(有點類似與Ping的功能),如果某個節點沒有返回響應資料包,那麼認為該節點發生了故障,Keepalived將報告這個節點失效,並從伺服器叢集中剔除故障節點。

傳輸層:

Keepalived 在傳輸層裡利用了TCP協議的埠連線和掃描技術來判斷叢集節點的埠是否正常,比如對於常見的WEB伺服器80埠。或者SSH服務22埠,Keepalived一旦在傳輸層探測到這些埠號沒有資料響應和資料返回,就認為這些埠發生異常,然後強制將這些埠所對應的節點從伺服器叢集中剔除掉。

應用層:

Keepalived 的執行方式也更加全面化和複雜化,使用者可以通過自定義Keepalived工作方式,例如:

可以通過編寫程式或者指令碼來執行Keepalived,而Keepalived將根據使用者的設定引數檢測各種程式或者服務是否允許正常,如果Keepalived的檢測結果和使用者設定的不一致時,Keepalived將把對應的伺服器從伺服器叢集中剔除。

離線環境匯入映象

兩種方法制作映象:

1 手工製作機芯

我們從下載映象,啟動容器,在容器中輸入命令來執行程式,這些命令都是手工一條條往裡輸入的,無法重複利用,而且效率很低。

2 通過指令碼製作映象

所以就需要一 種檔案或指令碼,我們把想執行的操作以命令的方式寫入其中,然後讓docker讀取並分析、執行,那麼重複構建、更新將變得很方便,所以Dockerfile就此誕生了。

Dockerfile常用引數:

FROM命令。用法,``FROM ``<image>:<tag>。FROM命令告訴docker我們構建的映象是以哪個(發行版)映象為基礎的
RUN命令。用法RUN <command>。RUN 後面接要執行的命令,比如,我們想在映象中安裝vim,只需在Dockfile中寫入RUN yum install -y vim
ENV命令。用法,ENV <key> <value>。ENV命令主要用於設定容器執行時的環境變數
ADD命令。用法,ADD <src> <dest>。ADD主要用於將宿主機中的檔案新增到映象中

製作自己的映象

1.從遠端倉庫拉取一個純淨的 centos 系統映象

從有公網的環境拉取映象,然後匯出映象

docker pull  docker pull nginx:latest

結果

[root@VM-4-17-centos ~]# docker pull  docker pull nginx:latest

1.13.5-alpine: Pulling from library/nginx
b1f00a6a160c: Pull complete
b5441325f46d: Pull complete
049763556f13: Pull complete
555a8317e22d: Pull complete
Digest: sha256:4a97b863a4386ba588cd4f264582d1f306bc9da46fe3e02540bd171709ce09d7
Status: Downloaded newer image for docker pull nginx:latest


[root@VM-4-17-centos ~]# docker image ls
REPOSITORY                                        TAG                     IMAGE ID            CREATED             SIZE
minio/minio                                       latest                  ecfbb387b46a        2 weeks ago         261MB
haproxy                                           latest                  d2164a4a948d        4 weeks ago         101MB
nginx                                             latest                  87a94228f133        6 weeks ago         133MB

 mkdir -p /root/ha-nginx
 cd /root/ha-nginx

把dockerfile 和sh指令碼拖進去:

[root@VM-4-17-centos ha-nginx]# ll
total 8
-rw-r--r-- 1 root root 263 Oct 28 15:19 Dockerfile
-rw-r--r-- 1 root root 256 Oct 28 15:19 nginx_check.sh

構建映象

docker build -t custom/ha-nginx:1.0 --rm=true .    

引數:

--rm :設定映象成功後刪除中間容器;

--shm-size :設定/dev/shm的大小,預設值是64M;

--ulimit :Ulimit配置。

--squash :將 Dockerfile 中所有的操作壓縮為一層。

--tag, -t: 映象的名字及標籤,通常 name:tag 或者 name 格式;可以在一次構建中為一個映象設定多個標籤。

output:

[root@VM-4-17-centos ha-nginx]# docker build -t custom/ha-nginx:1.0 --rm=true .
Sending build context to Docker daemon  3.072kB
Step 1/6 : FROM nginx:1.13.5-alpine
 ---> ea7bef82810a
Step 2/6 : RUN apk update && apk upgrade
 ---> Running in cedbede9dda0
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/community/x86_64/APKINDEX.tar.gz
v3.5.3-40-g389d0b359a [http://dl-cdn.alpinelinux.org/alpine/v3.5/main]
v3.5.3-40-g389d0b359a [http://dl-cdn.alpinelinux.org/alpine/v3.5/community]
OK: 7973 distinct packages available
Upgrading critical system libraries and apk-tools:
(1/1) Upgrading apk-tools (2.6.9-r0 -> 2.6.10-r0)
Executing busybox-1.25.1-r0.trigger
Continuing the upgrade transaction with new apk-tools:
(1/8) Upgrading freetype (2.7-r1 -> 2.7-r2)
(2/8) Upgrading libjpeg-turbo (1.5.1-r0 -> 1.5.3-r2)
(3/8) Upgrading gd (2.2.4-r0 -> 2.2.5-r1)
(4/8) Upgrading libcrypto1.0 (1.0.2k-r0 -> 1.0.2q-r0)
(5/8) Upgrading libssl1.0 (1.0.2k-r0 -> 1.0.2q-r0)
(6/8) Upgrading libxml2 (2.9.4-r3 -> 2.9.8-r1)
(7/8) Upgrading libgcrypt (1.7.9-r0 -> 1.7.10-r0)
(8/8) Upgrading busybox (1.25.1-r0 -> 1.25.1-r2)
Executing busybox-1.25.1-r2.post-upgrade
Executing busybox-1.25.1-r2.trigger
OK: 13 MiB in 26 packages
Removing intermediate container cedbede9dda0
 ---> 18233f3baae5
Step 3/6 : RUN apk add --no-cache bash curl ipvsadm iproute2 openrc keepalived &&  rm -f /var/cache/apk/* /tmp/*
 ---> Running in 8ed3f3453400
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/community/x86_64/APKINDEX.tar.gz
(1/20) Installing ncurses-terminfo-base (6.0_p20171125-r1)
(2/20) Installing ncurses-terminfo (6.0_p20171125-r1)
(3/20) Installing ncurses-libs (6.0_p20171125-r1)
(4/20) Installing readline (6.3.008-r4)
(5/20) Installing bash (4.3.46-r5)
Executing bash-4.3.46-r5.post-install
(6/20) Installing ca-certificates (20161130-r1)
(7/20) Installing libssh2 (1.7.0-r2)
(8/20) Installing libcurl (7.61.1-r1)
(9/20) Installing curl (7.61.1-r1)
(10/20) Installing libelf (0.8.13-r2)
(11/20) Installing libmnl (1.0.4-r0)
(12/20) Installing libnftnl-libs (1.0.7-r0)
(13/20) Installing iptables (1.6.0-r0)
(14/20) Installing iproute2 (4.7.0-r0)
Executing iproute2-4.7.0-r0.post-install
(15/20) Installing libnl (1.1.4-r0)
(16/20) Installing popt (1.16-r6)
(17/20) Installing ipvsadm (1.28-r0)
(18/20) Installing keepalived-common (1.2.24-r0)
(19/20) Installing keepalived (1.2.24-r0)
(20/20) Installing openrc (0.21.7-r4)
Executing openrc-0.21.7-r4.post-install
Executing busybox-1.25.1-r2.trigger
Executing ca-certificates-20161130-r1.trigger
OK: 28 MiB in 46 packages
Removing intermediate container 8ed3f3453400
 ---> f11588d2c04c

Step 4/6 : COPY entrypoint.sh /entrypoint.sh
 ---> 3156f2333fc0
Step 5/6 : RUN chmod +x /entrypoint.sh
 ---> Running in a1b04490979b
Removing intermediate container a1b04490979b
 ---> 3d829725c7d4
Step 6/6 : CMD ["/entrypoint.sh"]
 ---> Running in beafbbeab51a
Removing intermediate container beafbbeab51a
 ---> c2a5a12324ab
Successfully built c2a5a12324ab
Successfully tagged custom/ha-nginx:1.0

檢視映象

docker images 可以檢視下映象是否構建成功

docker save tag名稱 -o 映象名(例如:/root/rocketmq.tar)

docker save custom/ha-nginx:1.0 -o /root/ha-nginx.tar

上傳到內網並且匯入

無公網的環境,上傳到到內網環境, 上傳映象到目標虛擬機器

然後匯入docker,load到docker

docker load   -i  /root/ha-nginx.tar

匯入後看到兩個image 映象

master的高可用配置檔案

隨後,撰寫nginx配置檔案,keepalived-master.conf 這裡由於我們沒有後端tornado服務,所以使用虛擬代理服務

vrrp_script chk_nginx {
    script "pidof nginx"
    interval 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 33
    priority 200
    advert_int 1
    unicast_src_ip 172.20.128.2
    unicast_peer {
        172.20.128.3
    }
    
    authentication {
        auth_type PASS
        auth_pass 123456
    }
    
    virtual_ipaddress {
        172.20.128.4/24 dev eth0
    }

    track_script {
        chk_nginx
    }
}

slave的高可用配置檔案

同理再複製一份從機的nginx配置keepalived-slave.conf

vrrp_script chk_nginx {
    script "pidof nginx"
    interval 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 33
    priority 100
    advert_int 1
    unicast_src_ip 172.20.128.3
    unicast_peer {
        172.20.128.2
    }
    
    authentication {
        auth_type PASS
        auth_pass letmein
    }
    
    virtual_ipaddress {
        172.20.128.4/24 dev eth0
    }
    
    track_script {
        chk_nginx
    }
}

pidof命令用於查詢指定名稱的程序的程序號id號。

語法

pidof(選項)(引數)

選項

-s:僅返回一個程序號;
-c:僅顯示具有相同“root”目錄的程序;
-x:顯示由指令碼開啟的程序;
-o:指定不顯示的程序ID。

一.體系架構

在Keepalived + Nginx高可用負載均衡架構中,keepalived負責實現High-availability (HA) 功能控制前端機VIP(虛擬網路地址),當有裝置發生故障時,熱備伺服器可以瞬間將VIP自動切換過來,實際執行中體驗只有2秒鐘切換時間,DNS服務可以負責前端VIP的負載均衡。
nginx負責控制後端web伺服器的負載均衡,將客戶端的請求按照一定的演算法轉發給後端Real Server處理,而Real Server將響應直接返回給客戶端。

二.簡單原理

NGINX_MASTER、NGINX_BACKUP兩臺伺服器均通過keepalived軟體把ens32網絡卡綁上一個虛擬IP(VIP)地址192.168.2.242,此VIP當前由誰承載著服務就繫結在誰的ens32上,當NGINX_MASTER發生故障時,NGINX_BACKUP會通過/etc/keepalived/keepalived.conf檔案中設定的心跳時間advert_int 1檢查,無法獲取NGINX_MASTER正常狀態的話,NGINX_BACKUP會瞬間繫結VIP來接替nginx_master的工作,當NGINX_MASTER恢復後keepalived會通過priority引數判斷優先權將虛擬VIP地址192.168.2.242重新繫結給NGINX_MASTER的ens32網絡卡。
使用此方案的優越性
1.實現了可彈性化的架構,在壓力增大的時候可以臨時新增web伺服器新增到這個架構裡面去;
2.upstream具有負載均衡能力,可以自動判斷後端的機器,並且自動踢出不能正常提供服務的機器;
3.相對於lvs而言,正則分發和重定向更為靈活。而Keepalvied可保證單個nginx負載均衡器的有效性,避免單點故障;
4.用nginx做負載均衡,無需對後端的機器做任何改動。
5.nginx部署在docker容器裡,即大量地節約開發、測試、部署的時間,又可以在出現故障時通過映象快速恢復業務。

三、系統環境

兩臺負載機器安裝:centos7.5+docker+nginx+keepalived,分別命名為:NGINX_MASTER,NGINX_BACKUP。
後端web伺服器,可以是提供web服務的任何架構,分別命名為:WEB_1,WEB_2。
後端資料庫機器可任意架構,只要能提供資料庫服務即可。

伺服器 作業系統 IP地址 安裝軟體
NGINX_MASTER Centos 7.5 64位 192.168.2.228 docker+nginx+keepalived
NGINX_BACKUP Centos 7.5 64位 192.168.2.229 docker+nginx+keepalived
WEB_1 Centos 7.5 64位 192.168.2.226 docker+springboot
WEB_2 Centos 7.5 64位 192.168.2.227 docker+springboot
資料庫叢集 Centos 7.5 64位 mysql叢集

keepalive的配置檔案

vim /etc/keepalived/keepalived.conf

vrrp_script chk_nginx {
    script "/etc/keepalived/nginx_pid.sh"   # 檢查nginx狀態的指令碼
    interval 2
    weight 3
}

vrrp_instance VI_1 {
    state MASTER     #備份伺服器上將MASTER改為BACKUP
    interface ens32
    virtual_router_id 51
    priority 100       #備份服務上將100改為小於100,可配置成90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.2.242    #有多個vip可在下面繼續增加
    }
    track_script {
        chk_nginx
    }
}

新增檢查nginx狀態的指令碼

Keepalived的配置檔案可以分為三塊:

  • 全域性定義塊:對整個 Keepalive 配置生效的,不管是否使用 LVS;
  • VRRP 例項定義塊:是 Keepalived 的核心;
  • 虛擬伺服器(LVS)定義塊:LVS 配置只在使用 Keepalived 來配置和管理 LVS 時才需要使用,如果僅僅使用 Keepalived做 HA,LVS 的配置完全是不需要的。

配置檔案都是以塊(block)形式組織的,每個塊都在{和}包圍的範圍內。#和!開頭的行都是註釋。

[root@localhost ~]# cat /usr/local/keepalived/etc/keepalived/keepalived.conf
! Configuration File for keepalived
 
global_defs {					#全域性配置
	notification_email {		#指定keepalived在發生切換時需要傳送email到的物件,一行一個
		[email protected]	#指定收件人郵箱
		[email protected]
		[email protected]
	}
	notification_email_from [email protected] #指定發件人
	smtp_server 192.168.200.1	#指定smtp伺服器地址
	smtp_connect_timeout 30		#指定smtp連線超時時間
	router_id LVS_DEVEL			#此處注意router_id為負載均衡標識,在區域網內應該是唯一的。
	vrrp_skip_check_adv_addr
	vrrp_strict
	vrrp_garp_interval 0
	vrrp_gna_interval 0
}
 
vrrp_sync_group VG_1{				#監控多個網段的例項
	group {
		inside_network				#例項名
		outside_network
	}
	notify_master /path/xx.sh		#指定當切換到master時,執行的指令碼
	netify_backup /path/xx.sh		#指定當切換到backup時,執行的指令碼
	notify_fault "path/xx.sh VG_1" 	#故障時執行的指令碼
	notify /path/xx.sh
	smtp_alert 						#使用global_defs中提供的郵件地址和smtp伺服器傳送郵件通知
}
 
vrrp_instance inside_network {
	state BACKUP 			#指定那個為master,那個為backup,如果設定了nopreempt這個值不起作用,主備考priority決定
	interface eth0 			#設定例項繫結的網絡卡
	dont_track_primary 		#忽略vrrp的interface錯誤(預設不設定)
	track_interface{ 		#設定額外的監控,裡面那個網絡卡出現問題都會切換
		eth0
		eth1
	}
	mcast_src_ip			#傳送多播包的地址,如果不設定預設使用繫結網絡卡的primary ip
	garp_master_delay		#在切換到master狀態後,延遲進行gratuitous ARP請求
	virtual_router_id 50	#VPID標記
	priority 99				#優先順序,高優先順序競選為master
	advert_int 1			#檢查間隔,預設1秒
	nopreempt				#設定為不搶佔 注:這個配置只能設定在backup主機上,而且這個主機優先順序要比另外一臺高
	preempt_delay			#搶佔延時,預設5分鐘
	debug					#debug級別
	authentication {		#設定認證
		auth_type PASS		#認證方式,型別主要有PASS、AH 兩種
		auth_pass 111111	#認證密碼
	}
	virtual_ipaddress {		#設定vip
		192.168.36.200
	}
}
 
vrrp_instance VI_1 {		#虛擬路由的識別符號
	state MASTER			#狀態只有MASTER和BACKUP兩種,並且要大寫,MASTER為工作狀態,BACKUP是備用狀態
	interface eth0			#通訊所使用的網路介面
    lvs_sync_daemon_inteface eth0  #這個預設沒有,相當於心跳線介面,DR模式用的和上面的介面一樣,也可以用機器上的其他網絡卡eth1,用來防止腦裂。
    virtual_router_id 51	#虛擬路由的ID號,是虛擬路由MAC的最後一位地址
    priority 100			#此節點的優先順序,主節點的優先順序需要比其他節點高
    advert_int 1			#通告的間隔時間
    nopreempt				#設定為不搶佔 注:這個配置只能設定在backup主機上,而且這個主機優先順序要比另外一臺高
    preempt_delay			#搶佔延時,預設5分鐘
    authentication {		#認證配置
		auth_type PASS		#認證方式
        auth_pass 1111		#認證密碼
    }
    virtual_ipaddress {		#虛擬ip地址,可以有多個地址,每個地址佔一行,不需要子網掩碼,同時這個ip 必須與我們在lvs 客戶端設定的vip 相一致!
        192.168.200.16
        192.168.200.17
        192.168.200.18
    }
}
 

nginx 的參考配置

參考為:4核

user  nobody;
worker_processes  4;
worker_cpu_affinity 00000001 00000010 00000100 00001000;

error_log  /usr/local/nginx/logs/nginx_error.log  crit;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

pid        /usr/local/nginx/nginx.pid;
worker_rlimit_nofile 204800;

events {
    use epoll;
    worker_connections  204800;
}


http {
    include       mime.types;
    default_type  application/octet-stream;
    charset  utf-8;
    log_format  access  '$remote_addr - $remote_user [$time_local] "$request" '
              '$status $body_bytes_sent "$http_referer" '
              '"$http_user_agent" $http_x_forwarded_for';
    access_log  /usr/local/nginx/logs/access.log  access;

    server_names_hash_bucket_size 128;
	client_header_buffer_size 2k;
	large_client_header_buffers 4 4k;
	client_max_body_size 8m;

	sendfile on;
	tcp_nopush     on;

	keepalive_timeout 60;

	fastcgi_cache_path /usr/local/nginx/fastcgi_cache levels=1:2
	keys_zone=TEST:10m
	inactive=5m;
	fastcgi_connect_timeout 300;
	fastcgi_send_timeout 300;
	fastcgi_read_timeout 300;
	fastcgi_buffer_size 16k;
	fastcgi_buffers 16 16k;
	fastcgi_busy_buffers_size 16k;
	fastcgi_temp_file_write_size 16k;
	fastcgi_cache TEST;
	fastcgi_cache_key http://$host$request_uri;
	fastcgi_cache_valid 200 302 1h;
	fastcgi_cache_valid 301 1d;
	fastcgi_cache_valid any 1m;
	fastcgi_cache_min_uses 1;
	fastcgi_cache_use_stale error timeout invalid_header http_500;

	open_file_cache max=204800 inactive=20s;
	open_file_cache_min_uses 1;
	open_file_cache_valid 30s;



	tcp_nodelay on;

	gzip on;
	gzip_min_length  1k;
	gzip_buffers     4 16k;
	gzip_http_version 1.0;
	gzip_comp_level 2;
	gzip_types       text/plain application/x-javascript text/css application/xml;
	gzip_vary on;

    server {
        listen       8080;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

	root  /www/html/;

	location /status
	{
	     stub_status on;
	}


    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$
    {
      expires      30d;
    }

      
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

配置參考配置為8核CPU

  • worker_processes 8;

nginx程序數,建議按照cpu數目來指定,一般為它的倍數。

  • worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;

為每個程序分配cpu,上例中將8個程序分配到8個cpu,當然可以寫多個,或者將一個程序分配到多個cpu。

  • worker_rlimit_nofile 102400;

這個指令是指當一個nginx程序開啟的最多檔案描述符數目,理論值應該是最多開啟檔案數(ulimit -n)與nginx程序數相除,但是nginx分配請求並不是那麼均勻,所以最好與ulimit -n的值保持一致。

  • use epoll;

使用epoll的I/O模型,這個不用說了吧。

  • worker_connections 102400;

每個程序允許的最多連線數,理論上每臺nginx伺服器的最大連線數為worker_processes*worker_connections。

  • keepalive_timeout 60;

keepalive超時時間。

  • client_header_buffer_size 4k;

客戶端請求頭部的緩衝區大小,這個可以根據你的系統分頁大小來設定,一般一個請求的頭部大小不會超過1k,不過由於一般系統分頁都要大於1k,所以這裡設定為分頁大小。分頁大小可以用命令getconf PAGESIZE取得。

  • open_file_cache max=102400 inactive=20s;

這個將為開啟檔案指定快取,預設是沒有啟用的,max指定快取數量,建議和開啟檔案數一致,inactive是指經過多長時間檔案沒被請求後刪除快取。

  • open_file_cache_valid 30s;

這個是指多長時間檢查一次快取的有效資訊。

  • open_file_cache_min_uses 1;

open_file_cache指令中的inactive引數時間內檔案的最少使用次數,如果超過這個數字,檔案描述符一直是在快取中開啟的,如上例,如果有一個檔案在inactive時間內一次沒被使用,它將被移除。

  • fastcgi_cache_path /usr/local/nginx/fastcgi_cache levels=1:2 keys_zone=TEST:10m inactive=5m;

這個指令為FastCGI快取指定一個路徑,目錄結構等級,關鍵字區域儲存時間和非活動刪除時間。

  • fastcgi_connect_timeout 300;

指定連線到後端FastCGI的超時時間。

  • fastcgi_send_timeout 300;

向FastCGI傳送請求的超時時間,這個值是指已經完成兩次握手後向FastCGI傳送請求的超時時間。

  • fastcgi_read_timeout 300;

接收FastCGI應答的超時時間,這個值是指已經完成兩次握手後接收FastCGI應答的超時時間。

  • fastcgi_buffer_size 16k;

指定讀取FastCGI應答第一部分需要用多大的緩衝區,這裡可以設定為fastcgi_buffers指令指定的緩衝區大小,上面的指令指定它將使用1個16k的緩衝區去讀取應答的第一部分,即應答頭,其實這個應答頭一般情況下都很小(不會超過1k),但是你如果在fastcgi_buffers指令中指定了緩衝區的大小,那麼它也會分配一個fastcgi_buffers指定的緩衝區大小去快取。

  • fastcgi_buffers 16 16k;

指定本地需要用多少和多大的緩衝區來緩衝FastCGI的應答,如上所示,如果一個php指令碼所產生的頁面大小為256k,則會為其分配16個16k的緩衝區來快取,如果大於256k,增大於256k的部分會快取到fastcgi_temp指定的路徑中,當然這對伺服器負載來說是不明智的方案,因為記憶體中處理資料速度要快於硬碟,通常這個值的設定應該選擇一個你的站點中的php指令碼所產生的頁面大小的中間值,比如你的站點大部分指令碼所產生的頁面大小為256k就可以把這個值設定為16 16k,或者4 64k 或者64 4k,但很顯然,後兩種並不是好的設定方法,因為如果產生的頁面只有32k,如果用4 64k它會分配1個64k的緩衝區去快取,而如果使用64 4k它會分配8個4k的緩衝區去快取,而如果使用16 16k則它會分配2個16k去快取頁面,這樣看起來似乎更加合理。

  • fastcgi_busy_buffers_size 32k;

這個指令我也不知道是做什麼用,只知道預設值是fastcgi_buffers的兩倍。

  • fastcgi_temp_file_write_size 32k;

在寫入fastcgi_temp_path時將用多大的資料塊,預設值是fastcgi_buffers的兩倍。

  • fastcgi_cache TEST

開啟FastCGI快取並且為其制定一個名稱。個人感覺開啟快取非常有用,可以有效降低CPU負載,並且防止502錯誤。但是這個快取會引起很多問題,因為它快取的是動態頁面。具體使用還需根據自己的需求。

  • fastcgi_cache_valid 200 302 1h;
  • fastcgi_cache_valid 301 1d;
  • fastcgi_cache_valid any 1m;

為指定的應答程式碼指定快取時間,如上例中將200,302應答快取一小時,301應答快取1天,其他為1分鐘。

  • fastcgi_cache_min_uses 1;

快取在fastcgi_cache_path指令inactive引數值時間內的最少使用次數,如上例,如果在5分鐘內某檔案1次也沒有被使用,那麼這個檔案將被移除。

參考文獻

https://blog.csdn.net/zcxey2911/article/details/105593005/