爲了保證Web應用程序的高可用性和性能,通常會使用多個應用服務器,然後使用負載均衡器接收用戶的請求,將請求導向後端的應用服務器。目前有許多流行的軟件可以起到負載均衡器的作用,它們在服務的架構有着非常重要的地位。

負載均衡器類型

應用程序通過網絡進行通信,需要不同的軟件和硬件合作完成。爲了將複雜的問題簡化,將通信過程中的相關功能各進行分層。開放系統互連(OSI)將網絡通信抽象爲七層模型,OSI分層模型如圖1所示。

圖1 OSI模型

按照OSI模型定義的層級,將負載均衡器分爲四層負載均衡和七層負載均衡。

四層負載均衡工作在傳輸層。傳輸層負責處理消息的傳遞而不考慮消息的內容。HTTP協議使用了傳輸控制協議(TCP),故四層負載均衡器簡單地將網絡數據包轉發到上游服務器和轉發上游服務器的數據包,不檢查數據包的內容。四層負載均衡器可以通過檢查TCP流中的前幾個數據包來做出有限的路由決策。

七層負載均衡器工作在應用層。HTTP就是工作在第七層的協議。第七層的負載均衡器的工作方式比第四層的負載均衡器更復雜,它會截取流量,讀取其中的信息,並根據消息的內容(如URL、cookie)作出負載均衡的決策。然後,它與選定的上游服務器建立新的TCP連接,並將請求寫入服務器。

與七層負載均衡相比,四層負載均衡需要的計算量更小;在IT技術發展的早期,客戶端和服務器之間的交互也不如現在複雜,所以當時四層負載均衡是一種更流行的流量處理方法。遵循摩爾定理,硬件在性能提高的同時價格也降低,現在的CPU和內存已經足夠便宜,大多數的情況下,四層負載均衡的性能優勢可以忽略不計。

七層負載均衡在時間和計算量方面比第四層更加昂貴,不過它可以提供更豐富的功能,從而帶來更高的整體效率。比如七層負載均衡器可以確定客戶端請求的數據類型,從而不必在所有的服務器上覆制相同的數據。

Linux虛擬服務器

Linux虛擬服務器(下面簡稱LVS)是基於Linux操作系統內核的負載均衡軟件。這個軟件採用集羣技術,可用來構建高性能和高可用性的服務器,並提供良好的可擴展性、可靠性和可維護性。

LVS是工作在第四層的負載均衡軟件,在各個主要用來構建高可用的網絡服務,比如Web服務、電子郵件服務、媒體服務、語音服務等。

使用LVS需要了解下面的術語:

  • LVS負載均衡器(LVS director)。負載均衡器用戶接收所有傳入的客戶端請求,並將這些請求定向到特定的“真實服務器”來處理請求。
  • 真實服務器(real server)。真實服務器是構成LVS集羣的節點,用於代表集羣提供服務。
  • 虛擬IP(VIP)。LVS集羣對外表現的就像一臺服務器。虛擬IP是負載均衡器爲客戶端提供服務的IP地址,客戶端通過虛擬IP請求到集羣的服務。
  • 真實IP(RIP)。真實服務器的IP地址。
  • 控制器IP(DIP)。負載均衡器的真實IP地址。
  • 客戶端IP(CIP)。客戶端的IP地址,是請求的源IP地址。
    LVS有四種常見的工作模式。它們分別是NAT模式、直接路由模式(DR)、IP隧道模式和FULL NAT模式。

由於IPv4中定義的IP數量有限,生產環境中難以給所有的服務器分配公網IP地址。常見的做法是給機器分配一個內網IP地址,通過網絡地址轉換給客戶提供服務。

當用戶訪問集羣提供的服務時,發往虛擬IP地址的請求數據包到達負載均衡器。負載均衡器檢查目標地址和端口號。負載均衡器根據調度算法從集羣中選擇真實服務器,然後將請求數據包中的目的IP地址和端口重寫爲所選服務器的IP地址和端口,並將數據包轉發到服務器。

服務器處理完成請求後,回覆數據包給負載均衡器。負載均衡器將數據包中的源IP地址和端口重寫爲虛擬服務的源IP地址和端口。

在這種模式下,真實服務器的必須將網關配置爲負載均衡器的IP。不妨設用戶請求報文中的源IP和端口分別爲202.100.1.2和3456,目的IP和端口分別爲202.103.106.5和80。負載均衡器選擇了服務器2,修改請求報文的目的IP和端口爲172.16.0.3和80。

服務器2處理完成請求後,返回的報文中源IP和端口爲172.16.0.3和80,目的IP和端口爲202.100.1.2和3456。負載均衡器收到回覆報文後,將回復報文中的源IP和端口修改爲202.103.106.5和80。

NAT模式的工作過程如圖2所示。

圖2 LVS-NAT模式

NAT模式有一個問題:負載均衡器成爲了集羣的瓶頸,因爲所有流入和流出的數據包都要經過負載均衡器。直接路由(DR)解決了這個問題。

在LVS-DR模式下,真實服務器和負載均衡共享虛擬IP地址。負載均衡器的虛擬IP接口用於接收請求數據包,並將數據包路由到選定的真實服務器。真實服務器需要單獨配置接口用於傳輸返回報文,並在迴環接口配置虛擬IP地址,這是爲了讓真實服務器能夠處理目標地址爲虛擬IP的數據包;不能將虛擬IP設置在真實服務器的出口網卡上,否則真實服務器會響應客戶端的ARP請求,從而造成內網的混亂。

負載均衡器和真實服務器必須通過集線器/交換機連接。當用戶訪問集羣的虛擬IP時,發送到虛擬IP地址的數據包會到達負載均衡器。負載均衡器檢查數據包的目的IP和端口,選擇一個真實服務器,然後將報文中目的MAC地址修改爲選中的真實服務器的MAC地址,最後將數據包直接發送到局域網中。

真實服務器接收到數據包後,發現包中目的IP爲自己配置在迴環接口上的IP地址,因此處理這個請求,並將回覆請求直接發送給客戶端。DR模式的工作方式,如圖3所示。

圖3 LVS直接路由模式

採用DR模式的集羣能夠處理很大的請求量,是一種應用廣泛的模式。不過使用這種模式需要負載均衡器和真實服務器處於同一廣播域中,這限制了集羣的可擴展性,也不利於集羣的異地容災。

IP隧道模式是比DR模式更利於擴展的模式。IP隧道(IP封裝)是一種將IP數據報封裝在IP數據報中的技術。它允許將發往一個IP地址的數據報包裝並重定向到另一個IP地址,這是網絡中非常常見的技術。

在LVS的IP隧道模式中,負載均衡器收到用戶的請求後,選擇真實服務器,將數據包封裝在IP數據報中,數據包中的源IP爲負載均衡器的IP,目的IP爲真實服務器的IP,最後將數據包轉發到所選真實服務器。

真實服務器收到封裝的數據報後,它會解封數據包冰處理請求,處理完成後將結果直接返回給請求的用戶。

在集羣中,真實服務器可以擁有任意真實IP地址,也就是說真實服務器的部署可以分佈不同的地理位置,只要支持IP隧道,讓服務器能夠正確解封所接收的封裝數據包即可。同時要注意的是,配置虛擬IP的網卡不能響應ARP請求:可以在不支持ARP的設置上配置虛擬IP,或者將配置虛擬IP的設備發出的數據包重定向到本地套接字中。

IP隧道模式的工作過程,如圖4所示。

圖4 LVS隧道模式

在NAT模式下,負載均衡調度器和真實服務器必須在同一個VLAN下,否則負載均衡調度器無法作爲真實服務器的網關。LVS的FULLNAT模式解決了這個問題。

FULLNAT模式是NAT模式的升級版。在這種模式下,負載均衡器不僅會將請求數據包中目的IP替換真實服務器的IP,還會請求數據包中的源IP替換爲負載均衡器的IP。在返回報文中,負載均衡器將報文目的IP替換爲客戶端的IP,並將源IP替換爲虛擬IP。

FULLNAT模式不要求負載均衡器和真實服務器在同一個網段,因此可以支持真實服務器的跨機房部署。不過也會帶來一定的性能損失,同時真實服務器不能直接獲取到客戶端的請求IP。

LVS的服務是通過名爲IPVS的軟件實現的,在Linux內核2.4及以上版本中,ip_vs模塊已經是內核的一部分了。管理員通過ipvsadm程序來管理服務器集羣。LVS的模式在系統

下面演示如何安裝和使用ipvsadm。在命令行終端中輸入下面的命令:

# 安裝ipvsadm

$ sudo apt-get update && sudo apt-get install ipvsadm

# 查看ipvsadm的版本

$ sudo ipvsadm -l

IP Virtual Server version 1.2.1 (size=4096)

Prot LocalAddress:Port Scheduler Flags

-> RemoteAddress:Port           Forward Weight ActiveConn InActConn

# 添加一臺真實服務器,使用Round Robin調度算法

$ sudo ipvsadm -A -t 192.168.1.10:80 -s rr

# 查看真實服務器列表

$ sudo ipvsadm -l -n

IP Virtual Server version 1.2.1 (size=4096)

Prot LocalAddress:Port Scheduler Flags

-> RemoteAddress:Port           Forward Weight ActiveConn InActConn

TCP  192.168.1.10:80 rr

# 使用加權Round Robin算法

$ sudo ipvsadm -E -t 192.168.1.10:80 -s wrr

Nginx反向代理

Nginx既可以作爲四層負載均衡器使用,也可以作爲七層負載均衡器使用。這裏將主要介紹七層負載均衡的使用。

Nginx我們已經很熟悉了,前面的章節中也演示瞭如何安裝和使用Nginx。下面將重點介紹Nginx的配置。Nginx最簡單的負載均衡配置如下:

# http協議塊

http {

# 上游配置

upstream myapp1 {

    server srv1.example.com;

    server srv2.example.com;

    server srv3.example.com;

}

# server塊配置

server {

    # 監聽端口

    listen 80;

    # location配置

    location / {

        proxy_pass http://myapp1;

    }

}

} 

在上面的配置中,有三個實例運行相同的服務,這三個實例分別是srv1、srv2和srv3。默認配置下,負載均衡將使用循環調度算法。Nginx的代理支持HTTP、HTTPS、FastCGI、uwsgi、SCGI、memcached和gRPC協議。

Nginx也支持最少連接數算法。在某些請求需要更長時間才能完成的情況下,最少連接數算法允許更公平地控制應用程序實例上的負載。使用最少連接數算法時,Nginx將嘗試優先將新請求發送給不太繁忙的服務器,而從避免繁忙的應用程序服務器過載。

在配置中使用least_conn指令將激活最少連接負載均衡,使用示例如下:

upstream myapp1 {

    # 啓用最小連接負載均衡

    least_conn;

    server srv1.example.com;

    server srv2.example.com;

    server srv3.example.com;

} 

需要注意,使用循環或最少連接數算法,沒有後續客戶端的請求可能會發送到不同的服務器,也就是說Nginx無法保證同一客戶端每次都會請求到同一個服務器。

如果需要將客戶端綁定到特定的應用程序服務器,可以使用IP哈希負載均衡算法。使用這個算法,客戶端的IP地址將作爲哈希密鑰,以確定應響應客戶端請求服務器。用這個方法可以確保來自同一客戶端的請求始終定向到同一服務器。在配置中使用ip_hash指令可以開啓IP哈希調度,示例如下:

upstream myapp1 {

ip_hash;

server srv1.example.com;

server srv2.example.com;

server srv3.example.com;

} 

可以爲不同的服務器設置不同的權重,來影響負載調度的結果。上面的循環示例中沒有配置服務器權重,所有的服務器都有一樣的幾率被負載均衡器分發請求。當爲服務器指定權重參數時,調度器做負載均衡決策時,會將這個權重考慮進去,比如下面的示例:

upstream myapp1 {

    server srv1.example.com weight=3;

    server srv2.example.com;

    server srv3.example.com;

} 

採用了上面的配置後,服務器每接收5個新請求,會將3個請求定向到srv1,一個請求定向到srv2,一個請求定向到srv3。

Nginx的反向代理實現中包含了服務器運行狀態檢查。如果來自特定服務器的響應失敗並顯示錯誤,Nginx會將此服務器標記爲宕機狀態,在此後的一段時間內避免將請求定向到該服務器。

有兩個指令可以用來設置監控檢查參數:max_fails和fail_timeout。max_fails指令用於設置與服務器通信連續不成功的嘗試次數,默認值是1,當這個值設置爲0時,將不會對對應的服務器啓用健康檢查。fail_timeout參數定義Nginx將服務器標記爲宕機的時間。在服務器宕機時間超過fail_timeout設置的值後,Nginx將開始使用正常請求探測服務器,如果探測成功,Nginx會認爲服務器已經恢復正常。

文章節選自《Django項目開發實戰》,經出版社授權發佈。

相關文章