架構基礎知識總結1


感謝和參考:
PS:理論思想和實踐實驗步驟主要是來自於馬哥linux教育的學習,特此感謝,也感謝下面這些博主精彩的博文,從中受益良多,有些比較精彩的部分有摘抄過來。
參考或摘抄博文鏈接:
http://www.linuxvirtualserver.org/zh/lvs4.html
https://www.cnblogs.com/wangshaojun/p/5149532.html
https://www.jianshu.com/p/2acab9134dbf
http://www.178linux.com/104977
https://www.jianshu.com/p/5184c6564ee2
https://www.jianshu.com/p/298a85b74abf
https://www.ibm.com/developerworks/cn/linux/l-cn-directio/
https://www.cnblogs.com/youngerchina/p/5624462.html

一、nginx入門配置篇(初識核心功能)

1.1、nginx配置文件結構和核心功能

以下幾個nginx官方相關的鏈接可以經常去看看:
nginx 總官方主頁地址
nginx 文檔的api鏈接地址(簡單的官網)
nginx 的所有模塊文檔地址入口
nginx 所有變量索引目錄
nginx 所有指令索引目錄

1.1.1、nginx的配置文件結構

官方地址:http://nginx.org/en/docs/beginners_guide.html
註解:
    nginx 由模塊組成,模塊由配置文件中指定的指令控制。指令被分成兩種,包括簡單指令塊指令。簡單指令由名字和參數組成,名字和參數之間以空格或空白隔開,然後命令結尾以分號結束。塊指令格式和簡單指令語法差不多,末尾分號去掉後,會使用一組花括號({})引起來。花括號內可以有其他指令包含在內,這個叫做上下文(比如:events,http,server以及location指令都是塊指令)。
    在任何上下文之外放置在配置文件中的指令都被認爲是在上下文中。nginx 分爲主配置段(全局配置段),可以支持很多全局配置的指令,相關指令可以參考[nginx全局配置端指令(http://nginx.org/en/docs/ngx_core_module.html)。在主配置段中,可以包含 events塊,http塊。而server塊在http塊中,location塊在server中。在配置文件中,以"#"開頭的都屬於註釋部分。

主配置文件的配置指令用法總結:
普通指令:directive value [value2 …];
塊指令:directive{ … }
所有nginx支持的指令使用說明鏈接:http://nginx.org/en/docs/dirindex.html

普通指令必須以分號結束;在整個配置文件中,允許智勇配置變量,配置變量主要分以下兩類:
(1) 內建變量
有nginx模塊引入,可直接使用。所有nginx模塊引入的變量使用說明鏈接:
http://nginx.org/en/docs/varindex.html

(2)自定義變量
由用戶使用set命令定義(set variable_name value;),引用變量方式:$variable_name
set除了可以做定義自定義變量,還可以修改內建變量的值。

大概的配置結構:

主配置文件結構如下:
#main block:主配置段,也即全局配置段;
......  #簡單指令部分(全局的配置指令)
event {
    ......
} #事件驅動相關的配置;

http {
	......
} #http/https 協議相關的配置段;

mail {
	......
} #郵件協議相關配置段,這部分應用很少
stream {
	......
} #傳輸層代理配置段

http協議相關的配置結構:
http {
    ......
    ......  #各server的公共配置部分
    server {
        ...... 
    } #每個server用於定義一個虛擬主機

    server {
        ......
        listen
        server_name
        root
        alias
        location [OPERATOR] URL {
            .....
            If CONDITION {
                ......
            }
        }
    }
}

1.1.2、nginx核心功能

核心功能配置部分按照作用分爲以下幾類:
1、正常運行必備的配置
2、優化性能相關的配置(其實談不上優化,而就是做參數調整而已,優化這個東西涉及知識點太廣)
3、用於調試及定位問題相關的配置
4、事件驅動相關的配置
下面的指令的官方文檔鏈接:http://nginx.org/en/docs/ngx_core_module.html
被nginx官方標識爲:Core functionality(核心功能)

  • 1、正常運行必備的配置
    (1) user指令
語法格式:user username [groupname]
默認值:user nobody nobody;
上下文:main
官網註解:Defines user and group credentials used by worker processes. 
If group is omitted, a group whose name equals that of user is used.

自己的註解:定義nginx工作進程啓動運行身份的用戶屬主和屬組。一般來說,建議worker是普通用戶,比如nginx,www,nobody等等。
如果省略組名,默認組會取和用戶名一樣的名字。(PS: 涉及nginx處理用戶請求,是worker進程來處理的,所以涉及到權限的東西,也是
工作進程運行用戶和組的進程安全上下文。)

重點補充:
安全上下文說明。
1、進程以某用戶的身份運行,進程就是發起此進程用戶的代理,因此以此用戶的身份和權限完成所有的操作。
2、權限匹配模型(基本權限,不涉及特殊權限和訪問控制列表)
a) 判斷進程的屬主是否爲被訪問的文件的屬主:如果是,則應用屬主的權限,否則接着往下判斷;
b) 判斷進程的屬組是否屬於被訪問的文件屬組:如果是,則應用屬組的權限,否則接着往下判斷;
c) 如果上述兩種情況都沒有匹配到權限,則應用other的權限,即文件的其他者;

權限字符表示有兩個:r、w和x,分別代表讀,寫和執行權限;
基本權限相較於文件而言:
	r:表示可獲取文件的數據;
	w:表示可修改文件的數據;
	x:表示可將此文件發起運行爲進程;

基本權限相對於目錄:
	r:可使用ls命令獲取其下的所有文件列表;
	w:可修改此目錄下的文件列表,即創建或刪除文件;
	x:可切換到此目錄中,且可使用ls -l來獲取所有文件的詳細屬性信息。

(2) pid指令

語法格式:pid file;
默認值:pid logs/nginx.pid;
上下文:main
官方註解:Defines a file that will store the process ID of the main process.

自己註解:定義用來存儲主進程的進程id的文件,或者叫用來指明存儲nginx主進程進程號的文件。
可以指定爲這種格式便於理解:pid /PATH/TO/PID_FILE #可以是相對路徑,也可以是絕對路徑。如果是相對路徑值,是相對nginx編譯安裝(不管
是自己源碼包編譯安裝,還是自己根據spec文件製作的rpm包手動使用rpm安裝或者是yum調用的nginx官方yum倉庫或者第三方yum倉庫安裝,
yum安裝也是安裝製作好的rpm包。都可以給nginx的主程序文件傳一個選項-V來查看編譯選項,其中一個--prefix=path,我們這裏的pid指定的如
果是相對目錄,就是相對這個選項的path路徑) 服務端文件安裝根目錄。

(3) include指令

語法格式:include file | mask;
默認值:無
上下文:any #任意塊或段落中都可以使用該指令
官方註解:Includes another file, or files matching the specified mask, into configuration. Included files should consist of syntactically correct
 directives and blocks.
自己註解:包含其他文件或者指定掩碼(通配符等,比如*.conf)匹配的文件加入到配置文件中。包含進來的文件中的指令和塊的語法要正確。
簡單概括一句話:用來指明包含進來的其他配置文件片段;

(4) load_module指令

語法格式:load_module file;
默認值:無
上下文:main
官方註解:Loads a dynamic module.This directive appeared in version 1.9.11.

自己註解:指明要裝載的動態模塊。比如:load_module " /usr/lib64/nginx/modules/ngx_http_image_filter_module.so";
PS:前提是要裝載的模塊要編譯進來了,而且是支持動態裝卸載的模塊。nginx是1.9.11版本引入的動態模塊,1.9.11以及之後的版本才支持
load_module指令。現在默認epel倉庫默認版本已經是nginx-1.10.x,而且官方穩定版本更新到了1.14.x(2019年1月7日確認,最新穩定版本是
1.14.2)。

  • 2、性能優化相關的配置
    (1) worker_process指令
語法格式:worker_processes number | auto;
默認值:worker_processes 1;
上下文:main
官方註解:Defines the number of worker processes.
The optimal value depends on many factors including (but not limited to) the number of CPU cores, the number of hard disk drives that store
 data, and load pattern. When one is in doubt, setting it to the number of available CPU cores would be a good start (the value “auto” will try
  to autodetect it).
The auto parameter is supported starting from versions 1.3.8 and 1.2.5.
自己註解:
定義nginx的工作進程的數量。(建議設置爲小於或等於當前主機的cpu的物理核心數)
最佳取值取決於居多因素,包括(但不僅限於)CPU的核心數,存取數據的硬盤驅動的數量以及負荷模型等。如果不確定如何設置,設置它的值爲
cpu核心數是一個好的開端(如果設置爲auto,會嘗試去自動檢測cpu核心數,並設置爲檢測的實際值)。
auto參數是在nginx的 1.3.8版本和1.2.5版本引入。
PS:這裏設置爲大於cpu的核心數沒有意義而且反而可能會有副作用,因爲本身nginx的IO模型就是一個進程處理可以響應多個請求,如果我
物理服務器性能指標能承受得住,我可以調整單個工作進程響應處理的請求數量。通常建議是物理cpu有幾個核心,我們設置的值應該小於或等
於這個核心數的值。爲什麼不固定設置爲1呢,因爲爲了更好的利用cpu的核心來工作。因爲一個nginx工作進程在工作的時候必然是調度在一個
選定的或固定的物理cpu核心上,如果此時系統其他進程負載很小可以忽略不計,那麼如果我物理cpu核心數量不止1,其他cpu核心就處於空閒狀
態了。什麼情況下,至少要小於物理cpu核心數呢?就是系統上除了我們nginx進程之外,還有其他的進程,而且也有生產力,所以這種情況下建
議至少預留一個核心來調度其他進程。如果把這個參數的值設置大於物理cpu核心數,我們都知道nginx的工作進程如果有啓動,勢必要響應處理
請求,如果對於一個繁忙的web服務器,如果設置的這個參數值大於物理cpu核心數,那麼多出來的部分,勢必就要做cpu按照時間分片技術調
度,調度就存在資源浪費(有人可能會說,我一臺系統不可能不存在除了nginx之外的其他進程。話雖如此,其他進程相較繁忙的nginx的工作進
程,相對佔用cpu時間也不會太長,負載可以忽略。但是對於繁忙的工作進程,cpu切換帶來開銷是挺大的,具體底層比較複雜。如果一定要計
算輕量負載的其他非nginx的進程cpu切換帶來的性能影響,可以考慮後邊cpu綁定設置)。

(2) worker_cpu_affinity_cpumask指令

語法格式:worker_cpu_affinity cpumask ...;
		  worker_cpu_affinity auto [cpumask];
默認值:無
上下文:main
官方註解:Binds worker processes to the sets of CPUs. Each CPU set is represented by a bitmask of allowed CPUs. There should be a 
separate set defined for each of the worker processes. By default, worker processes are not bound to any specific CPUs.
自己的註解:
將工作進程綁定在CPU集上。每個CPU集由允許的CPU的位掩碼所表示。應該爲每一個工作進程定義一個單獨的集。默認,工作進程不綁定在任
何的cpu上。(綁定只是一種簡單的優化邏輯。每一個進程在運行時候都有它自己的數據加載,而爲了能夠加速這個數據處理過程,我們cpu有設
計1級緩存,2級緩存,3級緩存等。如果把一個進程綁定在cpu上,它的緩存始終會命中。如果不綁定,cpu在做處理進程切換的時候,緩存項可
能就會失效了,因爲除了nginx本身的進程之外,還會有系統進程。如果當時工作進程1被分配到了cpu核心1上,工作進程2被分配到了cpu核心2
上,那麼這兩個cpu就有可能分別緩存了工作進程1和2的數據。當處理nginx進程之外的其他系統進程需要使用cpu的時候,可能會把cpu核心1和
cpu核心2都調度切換,回頭再次調度到工作進程1和工作進程2的時候有可能工作進程1被分配到了cpu核心2,工作進程2被分配到了cpu核心1,
這樣之前工作進程在cpu的緩存數據勢必就失效了。
還有一種比較複雜的設置,比如我係統有4個cpu核心。如果系統上處理nginx,其他進程幾乎沒有負載,可以實現,把nginx的3個工作進程直接
綁定到4個cpu核心中的3個,然後通過某些技術屏蔽掉這3個綁定的cpu,讓它們只給其各自綁定的nginx的工作進程服務,剩下的1個cpu核心,
用於調度系統的其他進程任務。也就是說綁定的3個cpu核心數不再參與系統其他進程的調度任務。這個就是與之前的綁定的區別。不過這個功
能需要系統在啓動的時候,單獨隔離這3個綁定的cpu,比如麻煩。)
例如:
worker_processes    4;
worker_cpu_affinity 0001 0010 0100 1000;
設置工作進程爲4個,並分別把每個工作進程綁定到每個分離的cpu上。0001表示第0號cpu(第一個cpu核心),0010表示第1號cpu(第二個cpu核
心),0100表示第2號cpu(第三個cpu核心),1000表示第3號cpu。這裏之所以是4爲二進制,因爲物理cpu核心總共才8個。如果有8核心,位掩碼
一共要有8位纔對。
例如:
worker_processes    2;
worker_cpu_affinity 0101 1010;
綁定第一個工作進程在CPU0和CPU2上(0101可以拆分爲0001和0100),因爲有可能多核新是超線程實現的多核。比如我實際只有兩顆物理
cpu,而且每顆物理cpu分別有一個核,然後cpu通過超線程技術支持,然每顆物理cpu支持2核,所以這樣通過超線程實現的多核cpu(4核)實際真
正的物理核心數應該只有2。
綁定的二個工作進程在CPU1和CPU3上(1010可以拆分爲0010和1000).這第二個示例對於適用於超線程。

例如:
worker_processes auto;
worker_cpu_affinity auto;
這種情況,就是工作進程設置的是auto,綁定也支持的是auto。對於一個系統專門只提供nginx業務的,而且其他系統進程負載很輕的情況下,
可以這樣做。如果還提供其他業務,比如mysql等,建議不要綁定。

例如:
worker_cpu_affinity auto 01010101;
可選參數mask可以用來限定可以自動綁定的cpu。

(3) worker_priority指令

語法格式:worker_priority number;
默認值:worker_priority 0;
上下文:main
官方註解:Defines the scheduling priority for worker processes like it is done by the nice command: a negative number means higher 
priority. Allowed range normally varies from -20 to 20.
自己的註解:爲工作進程定義調度的優先級,就像由nice執行命令一樣。通常我們也說指定worker進程的nice值,設定woker進程優先級:負數
意味着優先級高。允許使用的默認範圍爲:-20到20。什麼場景,這樣使用呢?就是當一個業務系統,nginx作爲一個比較核心的業務的時候,然
後有其他業務進程會和它競爭資源,可以把nginx的工作進程的進程優先級條高一點。
小技巧:ps如何查看nice值,ps axo comm,pid,psr,ni
例如:
worker_priority -10;

(4) worker_priority指令

語法格式:worker_rlimit_nofile number;
默認值:無
上下文:main
官方註解:Changes the limit on the maximum number of open files (RLIMIT_NOFILE) for worker processes. Used to increase the limit without 
restarting the main process.
自己註解:改變所有worker進程所允許打開的最大文件數量限制(即所有worker進程所能夠打開的文件上限)。增加這個限制,不需要重啓nginx的
管理進程。因爲worker進程打開一個連接就需要維持一個套接字,如果worker能夠支持的最大併發爲1024,就表示需要打開1024個套接字件,
還姑且不說其他文件。如果我們要提高這個值的話,還受制於worker進程對應系統用戶所能打開的進程數,否則它無法創建這麼多的連接數量。
切記這個是定義所有worker進程所允許打開的最大文件數量。這個值應該大於或等於後面介紹的event塊中的worker_connections參數設定的值
與worker_processes設定值。公式:
worker_rlimit_nofile >= worker_processes* worker_connections

  • 3、調試、定位問題
    (1) daemon指令
語法格式:daemon on | off;
默認值:daemon on;
上下文:main
官方註解:Determines whether nginx should become a daemon. Mainly used during development.
自己註解:是否以守護進程方式運行nginx。

(2) master_process指令

語法格式:master_process on | off;
默認值:master_process on;
上下文:main
官方註解:Determines whether worker processes are started. This directive is intended for nginx developers.
自己註解:是否以master/worker模型運行nginx; 

(3) error_log指令

語法格式:error_log file [level];
默認值:error_log logs/error.log error;
上下文:main,http,mail,stream,server,location
官方註解:官方註解比較長,請自行參考:http://nginx.org/en/docs/ngx_core_module.html#error_log
自己註解:這個指令用來配置日誌日誌記錄信息的,是我們常說的錯誤日誌和調試日誌。指令的第一個參數file表示用來指明定義一個文件用來
存儲日誌,可以是一個相對路徑也可以是一個絕對路徑。指定文件權限要nginx的工作進程用戶要對這個文件有讀寫的權限。從1.7.11版本開始,
error_log可以在stream中用;從1.9.0開始,error_log可以在mail中有。

日誌級別可選值有,debug,info,notice,warn,error,crit,alert以及emerg。一般生成環境使用warn或notice就行。

  • 4、事件驅動相關的配置
    事件驅動相關的配置通常在下面這個語句塊中:
events {
    ......
}

(1) worker_connections指令

語法格式:worker_connections number;
默認值:worker_connections 512;
上下文:events
官方註解:Sets the maximum number of simultaneous connections that can be opened by a worker process.
It should be kept in mind that this number includes all connections (e.g. connections with proxied servers, among others), not only 
connections with clients. Another consideration is that the actual number of simultaneous connections cannot exceed the current 
limit on the maximum number of open files, which can be changed by worker_rlimit_nofile.
自己註解:設置被單個工作進程(worker process)所能同時打開的最大併發連接數。要牢記一點,這個連接不僅僅只指客戶端的連接,而是所有
的連接(比如,被代理服務器的連接也包含在內)。設置這個值的時候還要考慮實際允許併發的連接數不能超過最大的文件打開限制,可以改變
worker_rlimit_nofile這個值來調整。

(2) use指令

語法格式:use method;
默認值:無
上下文:events
官方註解:Specifies the connection processing method to use. There is normally no need to specify it explicitly, because nginx will by default 
use the most efficient method。
關於連接處理的方法可以參考:http://nginx.org/en/docs/events.html

自己註解:指明併發連接請求的處理方法。紅帽髮型版本一般推薦使用epoll,只要linux內核是2.6之後的版本。關於nginx所支持的連接方法說
明,可以參考上面的鏈接。

(3) accept_mutex指令

語法格式:accept_mutex on | off;
默認值:accept_mutex off;
上下文:events
官方註解:If accept_mutex is enabled, worker processes will accept new connections by turn. Otherwise, all worker processes will be notified 
about new connections, and if volume of new connections is low, some of the worker processes may just waste system resources.
There is no need to enable accept_mutex on systems that support the EPOLLEXCLUSIVE flag (1.11.3) or when using reuseport.
Prior to version 1.11.3, the default value was on.
自己註解:處理新的連接請求方法;on意味着由各worker輪流處理請求,off意味着每個新請求的到達都會通知所有的worker進程;這是一種互
斥鎖機制。設置啓用這種機制,表示起點公平,比如現在有100個請求過來,有啓動4個worker進程,每個進程分配25個。如果設置爲off,意味着
結果公平,就是來了請求,所有的worker進程會去競爭響應,可想而知,對於比較空閒的工作進程能競爭到的概率就要高一點。所以從大局來
看,所有的工作進程所分配到的請求的數量應該是平均的。從我個人的理解來看,對於繁忙的業務系統,可以開啓這個。對於空閒的業務系統或
者說新的連接數很少,如果所有的工作進程都去響應每一次的請求,勢必會有一些資源浪費。

1.1.3、nginx實現web功能(http)的核心模塊

與http相關的模塊文檔地址:http://nginx.org/en/docs/
    下面部分涉及的指令99%都是來自於http協議功能的核心模塊,ngx_http_core_module,部分1到2個不屬於此http協議的核心模塊功能,會在講解指令的時候特殊說明。http功能的核心模塊官方文檔鏈接爲:
http://nginx.org/en/docs/http/ngx_http_core_module.html
本部分配置大概框架:

http {
	... ...
	server {
		...
		server_name
		root
		location [OPERATOR] /uri/ {
			...
		}
	}
	server {
		...
	}
}

說明:從這部分開始,後續講解的指令由於官方文檔涉及全部用法非常複雜,所以後續講解,不會把官方文檔的所有部分都貼
出,只會選取比較重要的部分說明。如果有些沒有提到後續又要用,請自行參考官方文檔對應功能模塊的對應指令的詳細用法。

  • 1、與套接字相關的配置
    (1) server指令
語法:server { ... }
默認值:無
上下文:http
註解:
配置虛擬主機。基於IP和基於主機名的虛擬主機並沒有清晰的區別。(主機名最終還是會通過DNS解析成IP,所以並沒有大的區別)。在server指
令定義的虛擬主機配置段中,可以使用listen指令指明該虛擬主機接受連接涉及到的所有地址和端口信息。server_name指令用來列出虛擬主機
的所有主機名稱。

官方有個說明引導,關於如何配置的:
http://nginx.org/en/docs/http/request_processing.html

(2) listen指令

語法(基於官方精簡版):listen port | address[:port] | unix:/PATH/TO/SOCKET_FILE
listen address[:port] [default_server] [ssl] [http2 | spdy] [backlog=number] [rcvbuf=size] [sndbuf=size]
默認值:listen *:80 | *:8000;
上下文:server

default_server:設定默認虛擬主機;
ssl:限制僅能夠通過ssl連接提供服務;
backlog=number:後援隊列長度;(超過最大併發連接後,排隊的隊列的長度)
rcvbuf=size:接受緩衝區大小;
sndbuf=size:發送緩衝區大小;
說明:在接收請求的虛擬主機中可以使用address和port指明對應虛擬主機的監聽地址和端口或者使用unix套接字用來指明本地通信的一個套接
字文件。可以同時指明address和port或者只給定address或只給定port。其中的address可以是一個主機名。例如:
listen 127.0.0.1:8000;
listen 127.0.0.1;
listen 8000;
listen *:8000;
listen localhost:8000;
#ipv6 ,nginx 0.7.36版本之後可以使用
#UNIX-domain套接字,nginx0.8.21版本之後可以使用,要指明"unix:"前綴
listen unix:/var/run/nginx.sock;

重點:如果只給定了address,默認http協議使用的是80端口,配合ssl後使用的是443。
如果指令address沒有給定,如果nginx服務是以管理員權限用戶啓動(就是nginx的管理進程的啓動用戶是管理員用戶),默認是用*:80,如果
nginx服務是以非管理員權限用戶啓動,默認是用*:8000。
如果有多個虛擬主機,默認多個虛擬主機都沒有指定default_server,那麼第一個虛擬主機會作爲默認的虛擬主機。如果在啓動一個或者多個虛
擬主機中使用了(通常應該只會指定一個,或者把要當作的默認虛擬主機放在第一個) default_server指令,那麼這個虛擬主機會作爲默認的虛擬
主機。如果給定了ssl參數,表示所有連接請求都應該以SSL模式,即使我指定的監聽端口是80,連接也應該以SSL模式工作,比如:
listen 80;
區別於
listen 80 ssl;

有時候爲了讓HTTP和HTTPS協議都能使用,可以這樣指定:
listen 80;
listen 443 ssl;

(3) server_name指令

語法結構:server_name name ...;
默認:server "";
上下文:server
註解:指明虛擬主機的主機名稱;後可跟多個由空白字符分隔的字符串;
支持*通配匹配任意長度的任意字符:server_name *.yanhui.com www.yanhui.*
支持~起始的字符做正則表達式模式匹配:server_name ~^www\d+\.yanhui\.com$
匹配機制:
	(a) 首先匹配字符串精確的匹配;
	(b) 左側*通配符;
	(c) 右側*通配符;
	(d) 正則表達式;

(4) tcp_nodelay指令

語法結構: tcp_nodelay on | off;
默認值: tcp_nodelay on;
上下文:	http, server, location
註解:在keepalived模式下的連接是否啓用TCP_NODELAY選項。
對於一個要發送的數據,以TCP/IP4層結構來說,在每一層要加上對應的首部。如果要發送的數據的字節數比較小,有可能數據報文的首部的大
小要大於要發送的實際的內容的字節數。這樣的一次發送請求,對於開銷來說未免有些浪費,有一種思路就是我可以把小的內容先攢着,然後集
中發送,這樣就會有一個問題,對於客戶端來講,接受就會有延遲。爲什麼說nodelay要運行於保持連接模式呢,因爲非保持連接模式,客戶端
每一次請求連接都是3次握手4次揮手的過程,如果是保持連接,可以一次會話的建立,會有多個請求在一次會話中進行。這個tcp_nodelay啓用
後表示,不延遲到達,每一次請求都會及時發送,用戶體驗方面會好一點,不過每一次及時發送,小資源的報文未免會浪費帶寬等資源。

(5) tcp_nopush指令

語法結構:tcp_nopush on | off;
默認值:tcp_nopush off;
上下文:http,server,location
註解:在sendfile模式下,是否啓用TCP_CORK選項;啓動此選項可以實現的功能:
	(a) 響應報文首部和整個文件的起始內容放在一個報文中發送。
	(b) 利用完整的報文發送一個文件,不會把文件分開來發送。

使用sendfile機制的話,可以基於內容把文件發過去了,但是應用層首部呢?內核無法生成應用層首部信息(tcp首部可以生成),因爲它不理解應
用層協議。比如封裝http協議的應用層首部,只有nginx或者httpd纔有權限封裝。sendfile的機制就是文件內容先發送,然後應用層首部隨後到
達。使用tcpnopush,等用戶空間的應用層首部發送過來,然後內核打包成一個報文然後發送。sendfile啓用的優勢是整個文件的內容直接在內
核空間生成封裝的內容,而不需要經過用戶空間後然後再到內核空間。

(6) sendfile指令

語法結構:sendfile on | off;
默認值:sendfile off;
上下文:http,server,location,if in location
註解:Enables or disables the use of sendfile().
sendfile這個功能已經在多處提到。大概功能就是,外部客戶端請求服務端的內容後,請求的內容可能在磁盤上,默認工作模式是從磁盤加載數
據到內核的內存空間(因爲對磁盤的操作是特權的操作,只有內核才能完成),然後從內核的內存空間加載到web服務的進程的內存空間。而且以
TCP/IP 4層或者5層模型來看,報文要封裝,每一層都要封裝一層首部,實現報文的傳輸和拆解。啓動sendfile的好處就在於,除了應用層首部的
報文的封裝之外,磁盤加載的文件的內容不會發送給用戶空間,而是直接存儲在內核空間的內存中,封裝成報文發送,對於文件傳輸內容很大的
情況下,這種sendfile是很大的優化點。(解釋可能不夠精細,重點在於通俗)

  • 2、定義路徑相關的配置
    (1) root指令
語法結構:root path;
默認值:root html;
上下文:http,server,location,if in location
註解:設置web資源路徑映射;用於指明用戶請求的url所對應本地文件系統上的文檔所在目錄路徑;
指令的路徑參數可以包含變量,有兩個變量比較特殊,不能使用,它們是:$document_root和$realpath_root,$document_root表示root或者
alias指令的值,如果訪問路徑涉及符號鏈接,會使用符號鏈接;$realpath_root表示root或alias指令的值,與之前$document_root的區別在於,
如果對應目錄中有符號鏈接,會把真正的鏈接給出來。
例如:
location /i/ {
    root /data/w3;
}
比如我請求/i/top.gif,實際對應文件系統路徑的內容爲:/data/w3/i/top.gif

(2) location指令

語法結構:location [ = | ~ | ~* | ^~ ] uri { ... }
默認值:
上下文:server,location
註解:根據請求的URI來設置配置屬性。location中就是用來做nginx屬性設置相關的。在一個server中location配置段可存在多個,用於實現從uri
到文系統的路徑映射;nginx會根據用戶請求的URI來檢查定義的所有的location,並找出一個最佳匹配,而後應用其配置。

=:表示對URI做精確匹配,例如:http://www.yanhui.com/, http://www.yanhui.com/index.html
	location = / {
        ......
    }
	#上面這種形式,第一個鏈接可以被匹配,第二個不行,因爲第一個是/,第二個是/index.html
	
~:對URI做正則表達式模式匹配,區分字符大小寫;
~*:對URI做正則表達式模式匹配,不區分字符大小寫;
^~:對URI的左半部分做匹配檢查,不區分字符大小寫;
不帶符號:匹配起始於此uri的所有的url;
匹配優先級:(從高到低)
	=
    ^~
    ~和~*是同級
    不帶符號

官方有個例子比較全面,就直接粘貼過來了:
location = / {
    [ configuration A ]
}

location / {
    [ configuration B ]
}

location /documents/ {
    [ configuration C ]
}

location ^~ /images/ {
    [ configuration D ]
}

location ~* \.(gif|jpg|jpeg)$ {
    [ configuration E ]
}

The “/” request will match configuration A, the “/index.html” request will match configuration B, the “/documents/document.html” request will 
match configuration C, the “/images/1.gif” request will match configuration D, and the “/documents/1.jpg” request will match configuration E.

(3) alias指令

語法結構:alias path;
默認值:無
上下文:location
說明:定義路徑別名,文檔映射的另一種機制;
注意:location中使用root指令和alias指令的意義不同(下面的區別描述不一定精確,但是可以這麼理解)
    (a) root,給定的路徑對應於location中的/uri/左側的/;
	(b)alias,給定的路徑對應於location中的/uri/ 右側的/;
由於這個東西比較重要,我舉幾個例子:
location /i/ {
    alias /data/w3/images/;
    #root /data/w3/images/;
}
請求/i/top.gif,文件系統路徑對應會去找:/data/w3/images/top.gif
如果把上面的alias換成root,文件系統路徑會去找:/data/w3/images/i/top.gif

有一種情況,就是圖片資源和其他資源是分目錄存儲,訪問的時候,比如:
location /images/ {
    alias /data/w3/images/;
}
請求/images/123.jpg,文件系統路徑對應會去找: /data/w3/images/123.jpg。這樣就會給人一種誤解,兩個都有images字符串。所以建議配置成以下這種格式:
location /images/ {
    root /data/w3;
}
上面這種情況是定義location後邊的path與指令alias指令值的末尾部分一致的時候,建議把alias換成root,並把原本alias的值中與location相同的部分去掉。

(4) index指令

特殊保護說明:屬於ngx_http_index_module模塊。這裏之所以放在這裏,是因爲這個指令也比較常用。
語法結構:index file ...;
默認值:index index.html;
上下文:http, server, location
官方說明:Defines files that will be used as an index. The last element of the list can be a file with an absolute path.
自己註解:定義默認主頁文件,或者叫默認的索引文件。指定參數值可以包含變量,多個參數之間以空白分隔開,參數值列表的最後一個值可以
是一個絕對路徑。例如:index index.$geo.html index.0.html /index.html;檢測順序是從左到右的順序。

(5) error_page指令

語法結構:error_page code ... [=[response]] uri;
默認值:無
上下文:http, server, location, if in location
註解:Defines the URI that will be shown for the specified errors
自定義指定錯誤先調用顯示的頁面。比如指定404狀態的,自定義500,502,503,504等異常狀態顯示的錯誤頁面。而且可以指定調用後錯誤頁面
的狀態碼,可以改成正常的。

(6) try_files指令

語法結構:try_files file ... uri;
		   try_files file ... =code;
默認值:無
上下文:server,location
說明:按照指定順序分別檢查文件是否存在,並使用找到的第一個文件進行請求處理。檢查處理是在當前上下文中進行的。根據root和alias指令
指向的文件系統路徑去查找文件。如果要檢查目錄是否存在,可以在名稱的尾部加上左斜線(/)後綴,例如:"$uri/"。如果前邊所有的文件都沒有被
找到,會以文件參數列表的最後一個參數指向的路徑指定的URI來進行重定向。

  • 3、定義客戶端請求的相關配置
    (1) keepalive_timeout指令
語法結構:keepalive_timeout timeout [header_timeout]
默認值:keepalive_timeout 75s;
上下文:http,server,location
註解:如果第一個參數timeout設置爲0表示關閉保持連接功能。設置爲非0表示啓動保持連接。這裏和httpd不一樣,沒有單獨的關閉或打開保持
連接的開關按鈕。可選參數第二個,可以用來設置相應報文首的"Keep-Alive:timeout=time"的值。在Mozilla 火狐瀏覽器和Konqueror瀏覽器可以
識別這個"Keep-Alive: timeout=time";IE瀏覽器使用自己默認的60s,不受到這個參數控制(沒有測試過)。

(2) keepalive_requests指令

語法結構:keepalive_requests number;
默認值:keepalive_requests 100;
上下文:http,server,location
註解:指令從0.8.0版本引入。在一次長連接上所允許請求的資源的最大數量,生產環境就可以使用默認值;
這個參數生效的前提是保持連接功能要開啓的。

(3) keepalive_disable指令

語法結構:keepalive_disable none | browser ...;
默認值:keepalive_disable msie6;
上下文:http,server,location
註解:對哪種瀏覽器禁用長連接;

(4) send_timeout指令

語法結構:send_timeout time;
默認值:send_timeout 60s;
上下文:http,server,location
註解:向客戶端發送響應報文的超時時長,此處,是指兩次成功的寫操作之間的間隔時長;客戶端向服務端發送請求報文後,服務端會構建響應
報文給客戶端發過去。如果客戶端發送請求報文之後,突然離線或者其他原因導致網絡異常,收不到服務端的響應報文。這個發送響應報文會有
一個超時時間,這個值就是用來設置這個超時時間。如果客戶端在給定的超時時間內收不到響應報文,連接將會被關閉。

(5) client_body_buffer_size指令

語法結構:client_body_buffer_size size;
默認值:client_body_buffer_size 8k|16k;
上下文:http, server, location
註解:用於接收客戶端請求報文的body部分的緩衝區大小;
默認,緩存區大小是2倍的內存頁(4k)。如果是x86以及其他32位的平臺,默認值爲8K。如果是x86-64以及其他的64位平臺,默認值爲16K;超
出此大小時,其將被暫存到磁盤上的由client_body_temp_path指令所定義的位置(存磁盤性能就會受到影響了);
現在一般都是64位的平臺。對於論壇或者允許用戶上傳的業務站點(允許用戶上傳巨大的內容或者允許用戶使用PUT方法上傳文件時),而且站點
所在服務器內存允許的話,這個值如果調大會提高站點性能。對於一個電商站點,可能POST方法提交的信息很少,註冊提交的信息也很少,使
用默認值16K足以。

(6) client_body_temp_path指令

語法結構:client_body_temp_path path [level1 [level2 [level3]]];
默認值:client_body_temp_path client_body_temp;
上下文:http, server, location
註解:設定用於存儲客戶端請求報文的body部分的臨時存儲路徑及子目錄結構和數量;
請求併發很大,如果每個請求都允許上傳資源,這個量級是很大的。所以無法用平面存儲的思路,而是採取的分層存儲機制。level1表示定義1
級子目錄使用的16進制字符數量。1爲表示16個1級子目錄,2表示256個1級子目錄。level2表示二級子目錄使用的16進制字符數量。level3表示
3級子目錄使用的16進制字符數量。

比如請求爲:http://www.yanhui.com/images/default.jpg,對其做md5
執行命令:echo -n 'http://www.yanhui.com/images/day.jp'|md5sum,結果爲32位16進制字符:
1abb41e927569466f4bec3575a08f2b6

md5算法爲128位,上面32個字符爲二進制的16進製表示形式,每個字符可以由4位二進制所表示。
正是這種機制,存儲在磁盤上的文件是每一個連接請求的md5值。

比如設置:client_body_temp_path path 1 2 2;
上面案例表示的含義爲,使用1位字符表示一級子目錄,所以數量爲2^4(2^3+2^2+2^1+2^0=15,二進制是1111,由於從0開始,所以結果要加1)=16個。使用2位字符表示二級子目錄,也就是每一個一級子目錄下面會創建256個二級子目錄,所以此時擁有的所有二級子目錄數量爲
256*16=4096個。使用2位字符表示三級子目錄,也就是每一個二級子目錄下面會創建256個三級子目錄,所以此時擁有的所有三級子目錄數量爲256*256*16=10485676(百萬級別的了)。如果超過百萬級別,可以把一級子目錄數量設置爲256位,所以三級子目錄數量爲256*256*256= 16777216(1千6百多萬了)
以本例爲例,臨時文件存儲可能是:/spool/nginx/client_temp/7/45/f2/ 00000123457
可以這樣映射,不止nginx會這樣做,其他類似的也會這樣做:
16進制的數字;
client_body_temp_path   /var/tmp/client_body  2 1 1 
	1:表示用一位16進制數字表示一級子目錄;0-f
	2:表示用2位16進程數字表示二級子目錄:00-ff
	2:表示用2位16進程數字表示三級子目錄:00-ff

(7) types_hash_max_size指令

語法結構:types_hash_max_size size;
默認值:types_hash_max_size 1024;
上下文:http,server,location
註解:設置nginx內存中開闢的類型hash表的大小。
資源類型,內容類型(有個文件叫mime.types),nginx爲了加速對某些內容的訪問,mime.types的內容是直接被轉入到內存中的,當客戶端請求
一個資源之後,本地加載之後要立即分析這是什麼資源類型,要怎麼匹配,在內存中就知道這些內容類型了。我們要如何比較呢,難道一個一個
字符的比較嗎?並不是,爲了效率,內存中存儲的是類型hash之後的結果。每次比較的時候,會把內容hash後與之前存儲的hash內容做比較,
這種速度是非常快的。在內存中分配多少內存來保存這些hash值,這個types_hash_max_size就是用來定義這個大小。這裏單位是否爲字節或者項,不太確認,留個疑問,等將來學習深入後給出解答?

關於nginx的設置hash表的說明鏈接:
http://nginx.org/en/docs/hash.html

  • 4、對客戶端進行限制的相關配置
    (1) limit_rate指令
語法結構:limit_rate rate;
默認值:limit_rate 0;
上下文:http, server, location, if in location
註解:限制響應給客戶端的傳輸速率,單位值bytes/second(字節每秒)。值0表示無限制。這裏要注意的是,限制是對每一個請求來限制的,
如果一個客戶端同時打開了兩個鏈接,總速率會是限制速率的2倍。也可以使用$limit_rate變量來指定限制的速率,在條件判斷中可能用得到。
有個"X-Accel-Limit-Rate"首部值與速率限制相關。

(2) limit_except指令

語法結構:limit_except method ... { ... };
默認值:無
上下文:location
註解:限制客戶端使用除了指定方法之外的方法;
支持的方法可以有:GET, HEAD, POST, PUT, DELETE, MKCOL, COPY, MOVE, OPTIONS, PROPFIND, PROPPATCH, LOCK, UNLOCK, 或
PATCH
(在ngx_http_access_http,ngx_http_auth_basic_module,ngx_http_auth_jwt_module模塊中還支持其他方法)。GET方法會隱式允許HEAD方法。
案例:
limit_except GET {
    allow 192.168.1.0/32;
    deny  all;
}
除GET和HEAD方法之外的方法,只允許192.168.1.0/32網段中的客戶度端來使用,其他網段的客戶端都不允許使用。
  • 5、文件操作優化的配置
    (1) aio指令
語法結構:aio on | off | threads[=poll];
默認值:aio off;
上下文:http, server, location
說明:是否啓用aio功能。建議開啓,可以減少我們訪問文件被阻塞到io上的可能。可以通過threads來指定多少個線程池來配合
使用AIO功能,使用默認值就行。

(2) directio指令

語法結構:directio size | off;
默認值:directio off;
上下文:http, server, location
官方註解:
Enables the use of the O_DIRECT flag (FreeBSD, Linux), the F_NOCACHE flag (macOS), or the directio() function (Solaris), when reading 
files that are larger than or equal to the specified size. The directive automatically disables (0.7.15) the use of sendfile for a given request. 
It can be useful for serving large files:  
directio 4m;
or when using aio on Linux.

當讀取文件大於或等於(directio)指定的大小的時候。對於給定的請求,directive會自動關閉sendfile的功能。
這個部分,有人建議不要用。有人建議與aio一起使用,由於本身學術有限,不深入挖掘,待後續深入學習後回來補充。
可以參考:

https://www.cnblogs.com/youngerchina/p/5624462.html
https://yq.aliyun.com/articles/11243
https://www.ibm.com/developerworks/cn/linux/l-cn-directio/
https://blog.csdn.net/zhangxinrun/article/details/6874143

(3) open_file_cache指令

語法結構:open_file_cache off;
默認值:open_file_cache off;
上下文:http, server, location
說明:對於站點有大量文件被頻繁範圍,可以把文件的元數據信息存儲到緩存中。此選項就是控制緩存文件元數據的。nginx可以緩存以下三種
信息:
(1) 文件的描述符,文件大小和最近一次的修改事件;
(2) 打開的目錄結構;
(3) 文件查找錯誤,比如沒有找到或者沒有權限訪問的文件的相關信息等。
該指令支持參數值可以有:max,inactive,off
max:可緩存項上限;這個值不能隨意設置,要看自己內存允許不允許。達到上限後會使用LRU算法實現緩存管理;當緩存移除,就是超過這個
值的時候,LRU(the least recently used,最近最少使用)算法會把最近最少使用的緩存項刪除。
inactive:緩存項的非活動時長,在此處指定的時長內(默認值60秒)未被命中的或命中的次數少於open_file_cache_min_uses指令所指定的次數
的緩存項即爲非活動項;特別說明,非活動項的清理在緩存空間滿之前會進行。
off:關閉緩存文件元數據的功能。

(4) open_file_cache_valid指令

語法結構:open_file_cache_valid time;
默認值:open_file_cache_valid 60s;
上下文:http, server, location
註解:緩存項有效性的檢查頻率;如果又想啓用這種緩存的功能,內存空間又不夠用,可以把這個檢查的頻率設置的快一點,即這個時間設置短
一點。

(5) open_file_cache_min_uses指令

語法結構:open_file_cache_min_uses number;
默認值:open_file_cache_min_uses 1;
上下文:http, server, location
註解:在open_file_cache指令的inactive參數指定的時長內,至少應該被命中多少次方可被歸類爲活動項;

(6) open_file_cache_errors指令

語法結構:open_file_cache_errors on | off;
默認值:open_file_cache_errors off;
上下文:http, server, location
註解:是否緩存查找時發生錯誤的文件一類的信息。例如第一次請求的文檔爲/images/hi/a.png,因爲hi目錄不存在,所以此資源查找失敗,這個文件查找失敗被緩存下來了。

說明:至於nginx的上面常見配置項涉及的舉例,以及nginx的其他非核心模塊(比如實現基於用戶認證的訪問控制,基於ip的
訪問控制,nginx的狀態頁信息,nginx的訪問日誌控制設置,資源壓縮控制、https協議實現、重寫實現、防盜鏈實現等功能
模塊),包括nginx的代理功能的實現模塊(比如反代http,反代fastcgi),還包括代理4層協議以及負載均衡等功能,會在架構
基礎知識總結2中詳細註解。

二、linux集羣

2.1、集羣概述

2.1.1、單臺主機所面臨的問題和解決概述

    不管是多麼複雜的集羣或者架構,無非都是從單臺主機演變而來。單臺計算機受限於本地的計算資源和存儲資源以及IO資源等。以併發訪問服務的模型來舉例,單臺主機所支持的併發數是有效的,這種有限性不僅僅受限於我們主機的硬件資源,而且還受限於併發服務編程模型當中所實現的機制。如果有基礎的人,可能瞭解過IO模型的select,poll等阻塞的模型以及基於事件驅動模型實現的epoll模型。epoll這種可以實現單進程響應多個用戶請求,從而大大解決了所謂的每一個請求都得需要一個進程來響應導致資源消耗比較大的應用場景。

    對於磁盤IO來講,可以使用AIO(異步IO),來讓進程的請求發出以後直到對方準備並複製完成數據以後再接管資源,所以在磁盤IO方面又進一步解脫了併發服務編程模型當中的進程所需要承載的任務。即便如此,受限於我們的硬件,軟件等資源,單臺服務器的響應能力依然是有限的。比如基於httpd的prefork模型,單臺主機的併發訪問能力支持併發到1000或2000個,以及後邊引入的更好的,更新設計的nginx,支持併發服務請求到1萬或2萬個。但是,一旦併發連接請求依然超過這個數值之後要如何處理,對於我們的應用來講,無非就是這樣的模型或模式,一旦我們的業務需求所依賴到的硬件資源超過我們的硬件資源所承載的能力,我們通常會遵循以下幾個思路來考慮:
思路一:
    這種思路主要是對機器所處環境或者應用相關的參數做調整;對單臺主機的請求做優化。比如增加相關的空間,調整內核中相關的參數、併發連接數、內核內存資源的使用量等。但是內存資源總會有一個限制,也總有耗盡的一刻,一旦資源枯竭,我們也不能通過調整的手段來解決或來獲取性能上的提升的之後,那就只有下面的另外兩種方案來解決了。
思路二:
    這種思路主要是靠提高主機配置或者新增主機來完成;這種思路也有兩個方案,分別是:
    方案a:換更好性能,更好配置的計算機。比如原來2萬塊錢一臺的主機,現在花20萬塊錢來買一臺更好的替代。應用主機的向上擴展(scale up)。
    方案b:如果單臺主機的併發處理及響應能力不夠或不足以支撐用戶請求的時候,如果這些用戶請求是可以並行隔離的,彼此間沒有什麼關係,那就意味着一臺主機的壓力可以被分散出去,不是一個大請求的很多子任務而是各自都是獨立的請求,各自都是獨立的需求,那這種情況之下,我們可以把它分隔或分散開來。於是,可以多加一臺或多加幾臺主機來實現,這種應用實現叫做應用主機的向外擴展(scale out)。這種向外擴展的思想是組合多臺主機來完成一個任務。這一個任務其實是可以被分隔或分散成許多個任務的任務。所以這種解決方案,就叫做計算機的cluster(羣集,集羣),表示聚集在一起的一類事物統稱爲cluster。

2.1.2、集羣面臨的問題和解決思路

    多臺主機會把任務分隔或分散出去,這種集羣通常稱爲負載均衡集羣簡稱LB Cluster(load balancing cluster).
    我們考慮考慮一個主要的問題,當我們把用戶的請求給分散開來的時候,看上去是一件容易的事,我們可能一直假設有一個前提,這多個請求或者我們要處理的事情可以被分散或者分割成許多小請求來處理。對於我們的web服務來講,的的確確每個請求都是獨立的,這沒有問題。對於我們其他的服務,比如openssh,mysql等等也沒有問題,每一次的請求都是一個獨立的可分割的請求。事實上也是能夠分散的。

  • 問題1引入
        但是這裏會有一個問題,如果說分散用戶請求容易而且沒有問題或者這個問題容易解決。但是要考慮另一個問題,有些應用是需要追蹤用戶的狀態信息的,比如現在各種的動態的站點,一個電商站點或者其他類似站點,一個用戶在我們的站點瀏覽了哪些網頁或者瀏覽了哪些商品,在用戶下次來訪問時,我們期望在那個欄上顯示用戶已瀏覽的商品。還有用戶向購物車添加了哪些商品,希望在用戶刷新以後或者用戶重新登錄或者用戶隔幾分鐘或者隔很長時間以後再來訪問的時候,用戶要依然能看到用戶此前加入購物車中的商品。有些人可能會好奇,這種問題還是一個問題嗎,這個居然會成爲問題嗎,答案是會成爲一個問題;

  • 用戶session
        對於只有單臺服務器時,一般來講,我們動態應用程序,比如像jsp或php,它們是需要去追蹤用戶身份。追蹤完以後,能夠把每一個用戶加入到當前主機上的活動,通過一個內存中的記錄信息-(會話),來記錄它,這個通常稱爲用戶的session,是一個很小的數據結構,它裏面記錄了一個用戶在此站點上所作出的活動,比如像瀏覽的商品,加入購物車中的商品,收藏的商品等等,都應該予以記錄的。從而能使得,當用戶向購物車中加入多個商品並點擊付款的時候,這個商品還都在這臺主機之上,這沒有任何問題,因爲我們只有單臺主機。
        如果這個事情被分散或分隔到多臺主機了,問題所在呢。http請求,即便我們使用長連接,它的有效時長通常也只不過是幾十秒的時間,當用戶第一次發出請求的,碰巧我們給它分散到了第一臺主機之上,過了幾分鐘以後,用戶纔看完整個商品,看完之後,用戶決定要加入購物車之後,依然在這臺主機之上,這是OK的。再過了幾分鐘,觀看另外一件商品的時候,很有可能,這個用戶的請求被分發給另外一臺主機的,之所以如此,我們剛纔說明http協議本身是無狀態的,而第二,我們有多臺主機的集羣是需要把每一個用戶的請求分散到多臺不同的主機之上去的,那麼這個用戶的身份將難以被追蹤。所以即便請求本身可以被分割成多個任務,那麼來自於同一個用戶的多個請求是依然有可能需要被服務器追蹤識別纔可以。這是現代站點所面臨的必然問題。這也只是其中的問題之一。

  • 問題2引入
        再想一個問題,有經驗或者有自己測試過的人可能曾經部署wordpress了,如果部署過wordpress,假設第一個站點使用的是wordpress,現在面臨請求難以在單臺主機上處理,於是分散到第二臺主機之上,第二臺主機之上也有一個wordpress。wordpress是允許用戶上傳圖片的,這個圖片上傳之後保存在什麼地方呢。一般來講,在wordpress裏面有一個upload這樣的一個路徑,用來專門保存用戶上傳的圖片資源並在於本地生成一個URL,作爲它的單一訪問路徑資源獲取節點。想象一下,如果用戶第一次登錄的時候,被分發到第一臺主機之上,並上傳了圖片。第二次再次請求查看這麼一個文章時,它的請求卻被分發到第二臺主機之上,那麼他的信息還有嗎?很顯然,信息就沒了。所以你會發現,即便我們的請求是分離的,但是對於我們的業務本身來講,考慮到我們的請求是允許寫操作的,那麼很有可能某一次的用戶請求的寫會被某個服務器本地所承載,這樣就會導致同集羣中的承載同類任務的其他節點獲取不到這樣的請求了或者無法提供此種資源,那麼這依然會是一個問題。那麼怎麼解決?我們只能用多臺服務器都通同時訪問到的共享機制來解決,比如nis中的實現訪問,NFS,CIFS。

  • 問題2解決分析
        可以讓兩個wordpress對應的上傳(圖片)資源的目錄路徑是一個掛載的共享的後端的NFS或CIFS存儲,那麼因此用戶通過第一臺服務器上傳的,通過第二臺服務器也可以看到。這是一種解決思路。還有一種解決思路,假設有多個用戶請求,雖然說每一個請求都可以孤立,可以隔離開來,但是考慮到同一個用戶的請求,我們可以需要反覆被追蹤被識別,我們應該實現的是,對同一個用戶的請求,我們始終給它發往同一個主機。對於同一個用戶請求發往同一個主機,但是還是那句話,這最終只能解決用戶自己上傳的內容被自己看到。當其他的用戶被分配到另外一個主機之上,那麼另外的用戶就看不到用戶發的帖子,會造成有一部分用戶看得見,一部分用戶看不見。爲了避免這個問題,共享存儲也依然是必須的。

  • 其他問題引入和解決:
        對於我們的wordpress來講,它所需要存儲的數據不只有我們上傳的圖片,我們上傳的文章中的那些標題,正文的文字放哪兒去了呢,一般是存在mysql或mariadb這樣的db中去了,而這個db是不是應該是某一臺主機本地的呢,如果是依然面臨我們剛纔上文所說的那個問題。用戶發這個文章,圖片的問題解決了。當用戶被分配到第二服務器,圖片的問題解決了,用戶卻看不到之前用戶發的文章。因爲按照這個設想,文章內容被存在第一臺服務器的數據庫當中的。好在,數據庫本來就是用來提供數據共享存儲服務的,因此對於訪問的文章的標題,正文等,也應該被放在一個共享的存儲當中,這個共享存儲設備,就是我們的數據庫管理系統。

  • 圖片問題
    好像對於一個圖片,我們重來都沒有做過或者想不通怎麼做,才能把它存儲到我們這個數據庫管理系統當中,能不能做呢。可以這樣實現,存圖片的二進制編碼,問題是存進去之後如何取出來呢,更何況的很多查看圖片的工具,它沒辦法去聯繫數據庫。

  • 數據的組織方式
    通俗來講,數據的組織方式有以下三種:
    (1) 結構化的
    (2) 半結構化的
    (3) 非結構化的

結構化數據:
    一般而言,對於結構化的並要求數據強一致的數據,它們通常放在關係型數據庫並支持事務的存儲引擎上,例如mariadb的innodb存儲引擎。那麼,如果是存儲文件的,比如圖片,MP3等音樂文件,我們一般把它們放在文件系統的結構之上,它們一定是文件系統結構而不能被進一步抽象成爲所謂的數據管理模型自己專有的結構;
半結構化的數據:
    半結構化的數據,一般放在noSQL中或文件系統之上。比如json格式的數據,通常被組織成document storage(文檔存儲),比如ELK中的Elasticsearch ,它是一個著名的分佈式的document storage(文檔存儲),它們基於json格式去組織格式。json是一種著名的輕量化的半結構化數據的存儲邏輯。

非結構化的數據,就不說了。

  • web服務的三個層面
        突然發現,按照上面分析提到的,這樣會多了許多需求。這個需求以web服務來講,它分散於三個層面:
    第一:
        用戶的活動信息,自己的活動信息。在這個層面上,多臺主機如果都能夠處理用戶需要的話,一個用戶的活動在第一臺主機上活動一部分,在另外一臺主機上活動一部分,那麼我們的服務器將可能很難完全或完整的追蹤一個用戶的活動信息或整體活動信息。比如他瀏覽了哪些網頁,他加入到購物車中的商品有哪些等等;
    第二:
        如果服務器允許寫操作,它不僅僅能瀏覽網頁,還能發起交易,付款等。比如我們提到的文章類的站點,比如wordpress,用戶有可能是需要發表文章的,文章中可能還要插入圖片的,對於這種應用,它所帶來的不關是交易本身了,甚至可能是生成我們服務上所展示的內容的或者所展示的頁面的。那麼對於這種需求來講,我們如果允許用戶上傳時,存儲在服務器主機的單臺服務器的主機本地,勢必會導致用戶通過其他主機看不到,不關是無法追蹤用戶活動的問題了。連請求的同一個內容,你只能在不同的服務器上所看見,那麼這樣的負載均衡事實上 是不成立的。服務器不一樣,均衡到不同的主機之上能得到的是不同的內容,這事實上對於用戶來講是有失公允的。那麼後兩個問題,我們已經有了簡單解決方案,比如如果允許用戶寫操作,文件類的內容可以放在共享的存儲NFS或者CIFS之上。如果是所謂的結構化的數據,我們可以把它放在mariadb這樣的關係型數據庫存儲系統之上。

  • cookie解決思路的引入
        上面分析的過程,我們之前提到的第一個問題還沒有解決。剛纔說過了,用戶的活動身份行爲怎麼處理。第一個用戶在第一臺服務器上瀏覽了網頁,用戶的活動行爲是臨時產生,也通常是臨時有效,不會永久有效,而且這種活動行爲也不會生成網頁數據本身,只是作爲一個簡單的活動評判或者叫用戶行爲的追蹤。一般來講,我們不可能把它存儲在文件系統之上或者關係型數據庫當中。因此,既然如此,每一臺服務器是如何追蹤用戶身份呢? 我們可以基於cookie機制來追蹤。
        一個用戶第一次來訪問我們的服務器的時候,比如把它分發到第一臺服務器上,這個服務器會給它生成一個隨機的,唯一的ID,並把它發給客戶端。客戶端會把它保存在瀏覽器的緩存路徑下或者存儲路徑下。這個內容會保存在一個文件中,這個文件有它的適用範圍,比如它只適用於訪問某個域名下的哪一個URL時來使用。通常還有它的有效期,這就叫用戶的cookie。那麼用戶隨後再一次的訪問這個服務器或者這個域名時,都會加上這個身份,都會自動帶上這麼一個數據。所以服務器一看到它帶上這個數據,就會識別這個用戶的身份了。但是,只識別它的身份,它還不能追蹤用戶的活動。比如用戶在當前服務器上瀏覽過多少商品,如何追蹤?

  • 胖cookie和瘦cookie
        傳統方式,每一個用戶在每一個站點上訪問的所有行爲,都一 一被記錄在這個cookie文件中,當客戶端本地保存,而後客戶端每一次訪問的時候,都會帶着這個信息去服務端。這是一種胖cookie機制(fat cookie)。但是fat cookie會有很大的風險,我們的單臺主機之上,都有所謂的打着各種名義的或者以安全爲名的軟件程序,它擁有你整個系統的最高控制權限,它可以輕易的掃描你的瀏覽器下的每一個cookie上的所有數據,從而獲知用戶的訪問行爲。所以說胖cookie這種機制有可能會泄露用戶的隱私的。而且更重要的是,有些站點不希望去泄露用戶的隱私。比如某東的站點不希望用戶瀏覽商品的行爲被某寶的軟件獲取了,很顯然,對於大家來講可能是一種損失。因此,他們也不期望把這種用戶的行爲記錄到客戶端,避免別人來獲取到這些信息。因此現在都不再基於胖cookie來實現,而是每一個cookie僅用於追中用戶的身份。
        對於用戶的所有的行爲,在站點上,對應站點應用程序的進程的內部,都會有自己的內存使用空間。會在這個進程的內存空間的區域中爲每一個用戶分配一個數據結構,通常是hash類型的。每一個用戶在當前站點上做了什麼樣的訪問行爲,它都會有一個單獨的數據記錄位置。這裏記錄用戶的訪問,並且記錄了是哪一個用戶的訪問,它會和你cookie的值所綁定一起。所以用戶此後再訪問,只要帶來了cookie,我們識別了這個用戶,就會把這個用戶關聯到對應的數據結構的數據記錄之上,這個數據記錄,通常叫做session,服務端的session,服務端的會話。那麼,對於一個繁忙的站點來講,服務端的session可能會很多個。比如3千個併發可能是10萬個用戶發起的,那麼這裏有可能需要記錄10萬個session。

  • session 以及session的解決方案
        這樣一來,用戶第一請求,被分配到第一臺服務器上,session中記錄了用戶的行爲,這個session數據保存在第一臺服務器上,那麼用戶再請求,如果被分發到第二臺服務器上,那麼他的session信息就沒了。第二臺服務器上是沒有這個用戶的配置相關的這些數據的。雖然你能看見那個網頁,看見那個商品,我們也能夠往購物車中加入,但是此前你加入的那個行爲卻追蹤不到了。這對於用戶來講,可能會覺得莫名其妙,這對於負載均衡來講,這種機制就成爲有狀態的了。我們需要去追蹤並記住用戶的狀態。這個該如何解決呢?一般而言,這種記錄會有三種方式。
    (1) 方法一,session粘性(會話粘性)
        做用戶綁定,只要來自於同一個用戶的訪問,始終發給同一臺服務器,不往其他服務器上發。但是DNS好像沒有這種能力,DNS的A記錄是一個域名對應一條記錄,並且是輪循發送的。對於同一個域名來講,如果有多條A記錄,它是沒辦法始終把同一個用戶的請求綁定到某臺主機之上,但有些是可以實現。那麼該如何識別一個請求是來自於同一個用戶呢,有兩個方案。
    方案一,根據網絡層的IP地址來識別,只要來源的IP都相同,我們就當這是同一個用戶;
    方案二,對於web服務來講,可以使用cookie來識別,用戶的請求中隱含了cookie,只要他的請求是明文的,我們就能看到他的cookie,甚至是密文的也沒關係,在負載均衡器上,我們可以卸載ssl請求,也能做到看見那個cookie。只要cookie是同一個,我們就可以當作它是同一個用戶的請求。
    簡單的可以分析一下,方案二控制更加精準。對於方案一而言,對於有過網絡基礎的人可能知道,SNAT是網絡源地址轉換。對於大部分公司請求互聯網,可能都是通過同一個ip地址去請求訪問互聯網,但是我們服務器端如果基於方案一來判斷用戶,就只能把它識別成同一個用戶,事實上,壓根不是同一個用戶。如果我們能基於cookie來識別的話,無論你的源IP是什麼,只要你的cookie不同,都被識別成不同的用戶。所以方案二的粒度更加精細更加精準了。不管怎麼來講,這種方法都叫做會話粘性,由於實現把一個會話綁定在一個主機之上的的實現方式。
        但是這個方法(session 粘性) 有一個缺陷,如果把一個用戶的請求始終綁定到一臺主機之上就意味着一組會話在單獨一臺服務器上了,一旦這個服務器掛了,除非做了會話持久,等我們的服務器啓動,這個用戶還能訪問,這可能是3或5分鐘之後的事情了,這個用戶的請求並不能夠通過被分發到其他主機之上以解決用戶等待的問題,那麼這依然會是一個比較大的缺陷,於是就有了方法二來實現;

(2)方法二, session複製集羣(會話複製集羣)
    每一個主機的內存中都維持一組會話信息。那麼,每一個服務器的本地會話信息都傳給其他服務器一份,那麼集羣中的每一臺服務器就都擁有一個完整的會話。可以把兩臺服務器構建成一個會話集羣,從而讓每一臺主機的每一個本地的會話都傳遞給同集羣中的其他主機。這個時候都不用綁定用戶身份了,主機可以隨意被調度,隨意被輪詢。這裏依然會有一個很重大的問題,每一臺服務器都保存了整個集羣中所有會話的信息。假設你集羣有10萬個用戶訪問,每一臺服務器上都要保存10萬個session,這得需要多大的資源空間,更重要的是,如果你的服務器本身很繁忙,session會經常因爲用戶的活動行爲而被更新,大量的session信息會在集羣中傳來傳去,那麼服務器的帶寬就消耗的差不多了,如果有專用接口了的話,服務器的資源也會被消耗的差不多。所以這種方法不適用於太大規模的集羣。因爲這個方法二,有缺陷,所以有來方法三;

(3) 方法三,共享存儲(session server,會話服務器)
    每一臺服務器所生成的session不保存在自己的內存空間而保存在一個內存服務器中。爲什麼不保存在自己的內存空間中而保存在內存服務器,是因爲要考慮到性能的問題。內存存儲服務器能夠實現基於內存向其他服務器提供所謂的存儲功能,所以每一臺服務器上的進程需要保存會話和使用會話時,都到這個內存存儲服務器上去獲取,每一次更新也都給你更新到這個內存存儲服務上去。那麼,大家訪問的是同一組內存存儲服務器,所得到的內容都應該是一樣的。這個機制叫session server,session服務器。

PS:上面每一個環節可能解決起來很爽快,但是每一個節點都可能事關全局。比如我們的文件節點掛了,得不到圖片數據;mysql掛了,通過任何一個服務器都得不到這個存儲的數據;session也一樣。通常把這些事關全局的,一臺主機,一個服務,一個節點,一個事物故障會導致整個集羣服務不可用的硬節點稱作爲單點故障所在(Single Point Of Failure,SPoF).既然是單點故障,我們就要對它做其他的解決方案。

2.2、linux集羣類型

  • 何爲集羣?
    通俗來講,爲計算機集合,爲解決某個特定問題組合起來形成的單個系統。也可以理解爲集羣(cluster)表示聚集在一起的一類事物統稱。

  • linux集羣類型?
    (1) 負載均衡集羣
        負載均衡集羣的英文表示爲 Load Balancing,簡稱爲LB。所以我們通常會把負載均衡集羣叫做LB集羣或者LB cluster。LB 集羣主要提供和節點個數成正比的負載能力,這種集羣很適合提供大訪問量的Web服務。負載均衡集羣往往也具有一定的高可用性特點。
    常見硬件實現有:F5 Big-IP,Citrix Netscaler,A10等;
    常見軟件實現有:LVS,nginx,haproxy,ats,perlbal,pound等;
    (2) 高可用集羣
        高可用集羣的英文表示爲High Availiablity ,簡稱HA。所以我們通常會把負載均衡集羣叫做HA集羣或HA cluster。HA集羣一般是指當集羣中有某個節點失效的情況下,其上的任務會自動轉移到其他正常的節點上。還指可以將集羣中的某節點進行離線維護再上線,該過程並不影響整個集羣的運行。設計思想就是要最大限度地減少服務中斷時間。
    高可用集羣一般是通過系統的可靠性(reliability)和系統的可維護性(maintainability)來衡量的。通常用平均無故障時間(MTTF)來衡量系統的可靠性,用平均維護時間(MTTR)來衡量系統的可維護性。因此,一個高可用集羣服務可以這樣來定義:
    A=MTBF/(MTBF+MTTR)
    公式涉及的結果和簡稱說明:

MTTF(Average fault-free time):平均無故障時間;
MTBF(Mean Time Between Failure):平均故障間隔時間;
MTTR(Mean Time to Repair):平均恢復時間;
A表示這個結果,A的值在區間(0,1)之間。幾乎不可能有值爲1的集羣,而且如果值爲0,集羣將無任何意義。
(0,1):90%, 95%, 99%, 99.5%,  99.9%, 99.99%, 99.999%, 99.9999%
一般高可用集羣的標準有如下幾種:
99%:表示 一年不在線時間不超過87小時;
99.9% :表示一年不在線時間不超過8.7小時;
99.99%: 表示一年不在線時間不超過1小時;
99.999% :表示一年不在線時間不超過3-5分鐘;
大部分的集羣,多數控制在3個9或者4個9。

(3) 高性能集羣
高性能集羣英文爲High Performance,簡稱爲HP集羣或HP cluster。比如使用超級計算機或超算計算機這樣的應用。利用分佈式系統來組織(包括分佈式存儲和分佈式計算)。這一類一般是科學研究或者極大規模才用得到,我們這裏瞭解一下即可。

2.3、linux集羣系統擴展方式

linux集羣系統擴展的兩個大的方向分爲縱向擴展和橫向擴展:
Scale UP:縱向擴展或者叫向上擴展(用性能更好的主機),在我們第一章的概述部分就有提到這個概念;
Scale Out:橫向擴展或者叫向外擴展(組合多臺主機來完成單個任務,將任務分散處理,簡單地說就是加機器),在我們第一章的概述部分就有提到這個概念;
PS:一般互聯網企業都是通過橫向擴展的思路來解決問題,當然也可能有部分解決思路是使用的第一種。我們上面一小節中提到的高性能集羣就是上述的第一種擴展。

2.4、linux集羣的調度方法

2.4.1、靜態調度算法

靜態調度算法,僅根據算法本身進行調度。包括:
(1) 輪詢(RR)
輪詢(roundrobin),簡單來說就是調度器將外部請求輪流分配到集羣中的節點中;

(2) 加權輪詢(WRR)
加權輪詢(Weighted RR),調度器根據事先設置的權重來分配外部請求到集羣中的節點。

(3) 原地址哈希(SH)
原地址哈希(Source Hashing),主要是實現會話粘性(session sticky),是基於原IP地址哈希。將來自於同一個IP地址的請求始終發往第一次挑中的後端real server(RS),從而實現會話綁定。

(4) 目標地址哈希(DH)
目標地址哈希(Destination Hashing),將發往同一個目標地址的請求始終轉發至第一次挑中的RS,典型使用場景是正向代理緩存場景中的負載均衡.

2.4.2、動態調度算法

動態調度算法,主要根據每後端real server 當前的負載狀態及調度算法進行調度。
(1) 最少連接(least connections,LC)
    調度器通過“最少連接”調度算法動態的將網絡請求調度到已經建立連接最少的服務器上,如果集羣的真實服務器具有相近的系統性能,採用“最小連接”調度算法可以更好地均衡負載。負載滿足以下公式:

Overhead=activeconns*256+inactiveconns

(2) 加權最少連接(Weighted least connections,WLC)
    在集羣系統中的服務器性能差異較大的情況下,調度器採用加權最小連接的調度算法來優化負載均衡,具有較高權值的服務器將承受較大比例的活動負載連接。負載滿足以下公式:

Overhead=(activeconns*256+inactiveconns)/weight

(3) 最短延遲調度(Shortest Expection Delay,SED)
    在WCL的基礎上改進,Overhead=(ACTIVE+1)*256/加權,不再考慮非活動狀態,把當前處於活動狀態的數目+1來實現,數目最小的接受下次請求,+1的目的是爲爲了考慮加權的時候,非活動連接過多的缺陷,當權限過大的時候,會導致空閒服務器一直處於無連接的狀態。負載滿足以下公式:

Overhead=(activeconns+1)*256/weight

(4) 用不排隊/最少隊列調度(Never Queue,NQ)
    無需列隊,如果有臺real server的連接數爲0,就直接就分配過去,不需要進行sed運算,保證不會有一個主機很空閒,在SED的基礎上不論增加幾個,第二次一定調度到下一臺real server,不考慮非活動連接,纔會用NQ,SED要考慮活動狀態連接,對於DNS的UDP不需要考慮非活動連接,而http的處於保持狀態就需要考慮非活動連接給服務器的壓力。

(5) 基於局部的最少連接(Locality-Based Least connections,LBLC)
    基於局部性的最少連接調度算法是針對目標IP地址的負載均衡,目前主要運行在Cache集羣系統。該算法根據請求的目標IP地址找出該目標IP地址最近使用的服務器,若該服務器是可用而且沒有超載,將請求發送到該服務器,若該服務器不存在,或者該服務器超載且有服務器處於一半的工作負載,則用最少連接的原則選出一個可用的服務器,將請求發送到該服務器。

(6) 帶複製的基於局部性最少連接(LBLC with Replication)
    帶複製性的基於局部性最少連接調度算法也是針對目標IP地址的負載均衡,目前主要用在Cache集羣系統。它和LBLC算法不同的是它要維護從一個目標IP地址到一組服務器的映射,而LBLC算法維護從一個目標IP到一臺服務器的映射,該算法根據一請求的目標IP地址找出該目標IP地址對應的服務器組,按照最小連接原則服務器組中選一臺服務器,若服務器沒有超載,將請求發送到該服務器,若服務器超載,則按照最小連接原則從這個集羣中選出一臺服務器,將該服務器加到服務組中,將請求發送到該服務器,同時當該組服務器有一段時間沒有被修改,將最忙的服務器從服務組刪除,以降低複製的程度。

三、LVS應用

3.1、lvs功能的實現

    lvs是附加在netfilter的input鉤子上的一段程序實現,簡單理解就是工作在INPUT鏈上,lvs的工作也是基於規則來實現的,由ipvsadm工具來配置策略或規則。下圖是基於之前iptables框架圖的幾個工作鏈的實現邏輯,然後配合上lvs部分。由於netfilter的過濾功能要經過INPUT鏈,所以lvs和這個是衝突的,比如你把通過INPUT鏈的報文先通過INPUT鏈上的規則DROP掉後,lvs上定義的集羣服務規則是無法有效工作的,一般如果要想使用lvs,不會配合netfilter防火牆的過濾功能。
在這裏插入圖片描述

圖上標記含義:

a:PREROUTING鏈或prerouting鉤子
b:INPUT鏈或input鉤子
c:FORWARD鏈或forward鉤子
d:INPUT或input鉤子
e:POSTROUTING或postrouting鉤子
x:路由標誌;
y:路由標誌;
L:lvs程序附加處理;
  • lvs的ipvs和ipvsadm:
        lvs的實現邏輯由兩部分組成,一個是ipvs,一個是ipvsadm。其中ipvs是工作於內核空間的netfilter的input鉤子之上的框架,而ipvsadm是用戶空間的命令行工具,規則管理器,用於管理集羣服務及real server。

  • ipvsadm命令行工具用法

ipvsadm用法:
ipvsadm主要管理兩類事務,一類是集羣服務,一類是集羣服務上的realserver;
先定義集羣服務,也就是虛擬(集羣)服務,以後我們就叫集羣服務。然後支持對這個添加的集羣服務刪、改以及
查詢,而且集羣服務可以定義多個。
先定義了集羣服務,然後我們可以在集羣服務的基礎上新增各real server,然後可以對對應集羣服務增、刪、
改、查各realserver。

[yanhui@director ~]$ sudo ipvsadm --help
ipvsadm v1.27 2008/5/15 (compiled with popt and IPVS v1.2.1)
Usage:
  ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]] [-M netmask] [--pe persistence_engine] [-b sched-flags]
  
  
  ipvsadm -D -t|u|f service-address
  ipvsadm -C
  ipvsadm -R
  ipvsadm -S [-n]
  ipvsadm -a|e -t|u|f service-address -r server-address [options]
  ipvsadm -d -t|u|f service-address -r server-address
  ipvsadm -L|l [options]
  ipvsadm -Z [-t|u|f service-address]
  ipvsadm --set tcp tcpfin udp
  ipvsadm --start-daemon state [--mcast-interface interface] [--syncid sid]
  ipvsadm --stop-daemon state
  ipvsadm -h
  
Commands:  #命令
Either long or short options are allowed.
  --add-service     -A        add virtual service with options,#新增一個集羣服務
  --edit-service    -E        edit virtual service with options,#修改一個集羣服務
  --delete-service  -D        delete virtual service,#刪除一個集羣服務
  --clear           -C        clear the whole table  #清空所有
  --restore         -R        restore rules from stdin  #從標準輸入中恢復(集羣)規則,可以利用bash重定向特性把一個保存了集羣規則的文件加載到內存中
  --save            -S        save rules to stdout		#保存(集羣)規則到標準輸出,可以利用bash重定向特性寫入到一個文件
  --add-server      -a        add real server with options  #新增real server
  --edit-server     -e        edit real server with options #修改real server
  --delete-server   -d        delete real server #刪除real server
  --list            -L|-l     list the table  #查看集羣服務和查看real server
  --zero            -Z        zero counters in a service or all services
  --set tcp tcpfin udp        set connection timeout values
  --start-daemon              start connection sync daemon
  --stop-daemon               stop connection sync daemon
  --help            -h        display this help message


Options:  #選項
  --tcp-service  -t service-address   service-address is host[:port]   #定義tcp協議集羣服務或real server的服務地址(host加端口);
  --udp-service  -u service-address   service-address is host[:port]   #定義udpp協議集羣服務或real server的服務地址(host加端口);
  --fwmark-service  -f fwmark         fwmark is an integer greater than zero #防火牆標記,它是一個大於0的整數;
  --ipv6         -6                   fwmark entry uses IPv6
  --scheduler    -s scheduler         one of rr|wrr|lc|wlc|lblc|lblcr|dh|sh|sed|nq,  #定義集羣服務的調度方法。rr輪詢;wrr加權輪詢。
                                      the default scheduler is wlc.
  --pe            engine              alternate persistence engine may be sip,
                                      not set by default.
  --persistent   -p [timeout]         persistent service #設置持久連接超時時長
  --netmask      -M netmask           persistent granularity mask
  --real-server  -r server-address    server-address is host (and port)
  --gatewaying   -g                   gatewaying (direct routing) (default)  默認值。表示dr類型(gateway)。
  --ipip         -i                   ipip encapsulation (tunneling) ipip,tun類型
  --masquerading -m                   masquerading (NAT)  地址僞裝,nat類型
  --weight       -w weight            capacity of real server  #設置rs服務器的權重值
  --u-threshold  -x uthreshold        upper threshold of connections
  --l-threshold  -y lthreshold        lower threshold of connections
  --mcast-interface interface         multicast interface for connection sync
  --syncid sid                        syncid for connection sync (default=255)
  --connection   -c                   output of current IPVS connections #輸出當前IPVS的活動鏈接狀態
  --timeout                           output of timeout (tcp tcpfin udp)
  --daemon                            output of daemon information
  --stats                             output of statistics information #輸出統計數據
  --rate                              output of rate information #顯示速率數據
  --exact                             expand numbers (display exact values) #精確顯示計數器的值
  --thresholds                        output of thresholds information
  --persistent-conn                   output of persistent connection info
  --nosort                            disable sorting output of service/server entries
  --sort                              does nothing, for backwards compatibility
  --ops          -o                   one-packet scheduling
  --numeric      -n                   numeric output of addresses and ports  #數字格式顯示地址和端口,不會反解(反解ip會解析成主機名,ip會解析成協議或服務)
  --sched-flags  -b flags             scheduler flags (comma-separated)

內核中已經編譯進來ipvs的功能模塊,簡單查看一下:

[yanhui@director ~]$ ls -l /boot/config-3.10.0-229.el7.x86_64 
-rw-r--r--. 1 root root 123838 Mar  6  2015 /boot/config-3.10.0-229.el7.x86_64
[yanhui@director ~]$ grep -i 'ipvs' -C 5 /boot/config-3.10.0-229.el7.x86_64 
......省略
CONFIG_NETFILTER_XT_MATCH_IPVS=m
......省略
# IPVS transport protocol load balancing support #IPVS支持的協議
#
CONFIG_IP_VS_PROTO_TCP=y
CONFIG_IP_VS_PROTO_UDP=y
CONFIG_IP_VS_PROTO_AH_ESP=y
CONFIG_IP_VS_PROTO_ESP=y
CONFIG_IP_VS_PROTO_AH=y
CONFIG_IP_VS_PROTO_SCTP=y
#
# IPVS scheduler #IPVS調度方法
#
CONFIG_IP_VS_RR=m
CONFIG_IP_VS_WRR=m
CONFIG_IP_VS_LC=m
CONFIG_IP_VS_WLC=m
--
CONFIG_IP_VS_SH=m
CONFIG_IP_VS_SED=m
CONFIG_IP_VS_NQ=m
#
# IPVS SH scheduler
#
CONFIG_IP_VS_SH_TAB_BITS=8

#
# IPVS application helper
#
CONFIG_IP_VS_FTP=m
CONFIG_IP_VS_NFCT=y
CONFIG_IP_VS_PE_SIP=m

在要模擬成director主機上安裝ipvsadm即可:

[yanhui@director ~]$ sudo yum info ipvsadm
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: mirrors.cn99.com
 * epel: mirrors.tuna.tsinghua.edu.cn
 * extras: mirrors.shu.edu.cn
 * updates: centos.ustc.edu.cn
Installed Packages
Name        : ipvsadm
Arch        : x86_64
Version     : 1.27
Release     : 7.el7
Size        : 75 k
Repo        : installed  #我這裏是已經安裝了的
From repo   : base
Summary     : Utility to administer the Linux Virtual Server
URL         : https://kernel.org/pub/linux/utils/kernel/ipvsadm/
License     : GPLv2+
Description : ipvsadm is used to setup, maintain, and inspect the virtual server
            : table in the Linux kernel. The Linux Virtual Server can be used to
            : build scalable network services based on a cluster of two or more
            : nodes. The active node of the cluster redirects service requests to a
            : collection of server hosts that will actually perform the
            : services. Supported Features include:
            :   - two transport layer (layer-4) protocols (TCP and UDP)
            :   - three packet-forwarding methods (NAT, tunneling, and direct routing)
            :   - eight load balancing algorithms (round robin, weighted round robin,
            :     least-connection, weighted least-connection, locality-based
            :     least-connection, locality-based least-connection with
            :     replication, destination-hashing, and source-hashing)
如果沒有安裝,執行yum安裝以下即可:
sudo yum install ipvsadm

3.2、lvs四種集羣優點和使用場景

爲了更好的理解lvs集羣,需要了解一下關鍵的術語:

vs:Virtual Server, Director, Dispatcher, Balancer。調度器,負載均衡器,分發器或者叫虛擬服務器;
rs:Real Server, upstream server, backend server。真實服務器、負載均衡服務器,後端服務器
CIP:Client IP, VIP: Virtual serve IP, RIP: Real server IP, DIP: Director IP。客戶端IP,虛擬IP,真實IP,調度器IP。
CIP <--> VIP == DIP <--> RIP 

請求客戶端ip,我們叫客戶端主機,客戶端主機的接口叫客戶端ip(簡稱CIP);
虛擬服務器面向客戶端一側的接口叫虛擬服務器ip(簡稱VIP)。Director主機用於與後端主機通信的,
此時,它就扮演了一個調度器,面向RS一側的接口叫調度器ip(簡稱DIP);
每一個Real Server的IP簡稱RIP。

如圖所示:
在這裏插入圖片描述

lvs集羣的可以分爲以下四類:(比較重要,要能通俗講解)
lvs-nat:修改請求報文的目標IP(和目標端口);多目標IP的DNAT;
lvs-dr:操縱封裝新的MAC地址;(不會修改請求報文的源IP,目標IP,源端口以及目標端口)
lvs-tun:在原請求IP報文之外新加一個IP首部;(原來的一級源IP,目標IP。現在加了一級,實現了兩級IP首部,這種叫基於IP來運載IP,所以和隧道很相似,所以叫隧道模型)
lvs-fullnat:修改請求報文的源和目標IP(以及源端口和目標端口);

如果按照請求報文和響應報文經過director分類:

  • 請求報文和響應報文都經過Director
    lvs-nat和lvs-fullnat
    lvs-nat:關鍵要點,RIP的網關要指向DIP;
    lvs-fullnat:關鍵要點,RIP和DIP未必在同一個網絡,但是要能通信;

  • 請求報文要經過Director,但響應報文由RS直接發往client
    lvs-dr和lvs-tun
    lvs-dr:關鍵要點,通過封裝新的MAC首部實現,通過MAC網絡轉發;
    lvs-tun:關鍵要點,通過在原IP報文之外封裝新的IP報文實現轉發,支持遠距離通信。

(1) lvs-nat

特性:
多目標IP的DNAT,通過將請求報文中的目標地址和目標端口修改爲某挑出的RS的RIP和PORT實現轉發
(1)RIP和DIP必須在同一個IP網絡,且應該使用私網地址;RS的網關要指向DIP;
(2)請求報文和響應報文都必須經由Director轉發;Director易於成爲系統瓶頸;(請求報文通過以GET方法居多
所以請求報文一般很小。但是響應報文要附帶請求的數據,所以會很大)
(3)支持端口映射,可修改請求報文的目標PORT;
(4)vs必須是Linux系統,rs可以是任意系統;

優點:配置簡單,且節省IP。只需要在調度器上配置一個公網IP即可。支持端口映射,即用戶請求的端口和RS端
口可以不一致;

缺點:所有請求和響應都要經由調度器,併發量大時會成爲集羣瓶頸。一般後端RS節點保持在10-20個(沒有實際
測試過,具體不知道。對於超大併發的場景,lvs的nat模型結構可想而知前段調度器壓力);

使用場景:由於配置簡單,節省IP的特點,一般用在併發量不大的中小企業。

(2) lvs-dr

Direct Routing,直接路由;

通過爲請求報文重新封裝一個MAC首部進行轉發,源MAC是DIP所在的接口的MAC,目標MAC是某挑選出的RS的RIP所在接口的MAC地址;源IP/PORT,以及目標IP/PORT均保持不變(不支持端口映射);

Director和各RS都得配置使用VIP;

(1) 確保前端路由器將目標IP爲VIP的請求報文發往Director:
	(a) 在前端網關做靜態綁定;(不適用)
	(b) 在RS上使用arptables;
	(c) 在RS上修改內核參數以限制arp通告及應答級別;
		arp_announce
		arp_ignore
(2) RS的RIP可以使用私網地址,也可以是公網地址;RIP與DIP在同一IP網絡;RIP的網關不能指向DIP,以確保響應報文不會經由Director;
(3) RS跟Director要在同一個物理網絡(要基於MAC地址進行報文轉發,所以不能經過路由器,經過交換機可以);
(4) 請求報文要經由Director,但響應不能經由Director,而是由RS直接發往Client;
(5) 不支持端口映射;

優點:RS處理完請求後是直接響應給客戶主機的,不會經由調度器,從而大大減輕了調度器的負擔,增加了集羣
的併發請求處理量;

缺點:配置較複雜,需要在所有RS上配置VIP並修改內核參數。DR類型是通過重新封裝請求報文的包頭文件的
MAC地址來實現轉發的,所以調度器和RS只能在一個局域網內(無法實現後端的RS異地災備)。DR類型相對於NAT類型需要消耗更多的公網IP;

使用場景:併發量非常大的情況下會用到此類型,DR模型的併發處理量能達到硬件級別的能力。

(3) lvs-tun

lvs-tun:
	轉發方式:不修改請求報文的IP首部(源IP爲CIP,目標IP爲VIP),而是在原IP報文之外再封裝一個IP首部(源IP是DIP,目標IP是RIP),將報文發往挑選出的目標RS;RS直接響應給客戶端(源IP是VIP,目標IP是CIP);
	
	(1) DIP, VIP, RIP都應該是公網地址;
	(2) RS的網關不能,也不可能指向DIP;
	(3) 請求報文要經由Director,但響應不能經由Director;
	(4) 不支持端口映射;
	(5) RS的OS得支持隧道功能;

優點:解決了DR類型下RS與調度器必須在同一局域網的限制;

缺點:配置繁瑣,應用場景少;而且由於要額外給請求報文封裝一層IP報文,所以對於請求報文攜帶較多數據
的場景(雖然這種場景少,但是也不是沒有。比如請求向數據庫寫入一大串字符串),會導致請求報文大小超過
MTU默認大小。會有巨型幀的概念(指有效負載超過IEEE 802.3標準所限制的1500字節的以太網幀),有巨型
幀會導致每一次請求報文會被切片,造成效率不高的問題。

使用場景:如果環境要求DIP與RIP不在同一物理網絡(如災備)時,就需要用到lvs-tun模型。

(4) lvs-fullnat

通過同時修改請求報文的源IP地址和目標IP地址進行轉發;
	CIP <--> DIP 
	VIP <--> RIP 

(1) VIP是公網地址,RIP和DIP是私網地址,且通常不在同一IP網絡;因此,RIP的網關一般不會指向DIP;
(2) RS收到的請求報文源地址是DIP,因此,只能響應給DIP;但Director還要將其發往Client;
(3) 請求和響應報文都經由Director;
(4) 支持端口映射;

優點:相比於NAT的局域網,FULLNAT是相對於沒有那麼侷限,它是限制在內網中的。對於運維比較方便。
(解決了lvs-tun類型的笨重,因爲做異地災備的還是挺少的。一般是在大的辦公大樓,不同機房。)也支持端口
映射。

缺點:請求報文和響應報文都要經過director,director壓力過大。
配置複雜。而且還不是LVS標準類型,如果要用。還要有自定義的能力,比如打補丁等。
而且ipvsadm也不支持這種類型,要自己寫工具實現。

使用場景:人比較少,肯投了的場景。(俗稱人少錢多)

注意:此類型默認不支持。要想時要此功能,要編譯內核打補丁,比較複雜。屬於理論類型,
沒有能力一般實現不了。沒有被收錄進LVS官方標準類型。

3.3、LVS-NAT和LVS-DR的原理和實現舉例,LVS-FUN和LVS-FULLNAT簡單圖解

3.3.1、LVS的NAT類型

LVS的NAT類型滿足以下概要說明概要說明的特點:

實現:多目標IP的DNAT,通過將請求報文中的目標地址和目標端口修改爲某挑出的RS的RIP和PORT實現轉發;
(1) RIP和DIP必須在同一個IP網絡,且應該使用私網地址;RS的網關要指向DIP;
(2) 請求報文和響應報文都必須經由Director轉發;Director易於成爲系統瓶頸;
(3) 支持端口映射,可修改請求報文的目標PORT;
(4) vs必須是Linux系統,rs可以是任意系統;

lvs-nat模式的請求報文和響應報文圖解:
在這裏插入圖片描述

工作邏輯簡述:

lvs的nat模型有點類似於之前iptables中講到的DNAT(目標地址轉換),和DNAT幾乎一樣。
不同的是,LVS的nat模型中後端主機有多個,而且實現方式不在iptables的PREROUTING鏈上,而是在iptables
的INPUT鏈上(所以要想用lvs,就不能用到iptables的INPUT鏈的功能)。當用戶請求達到時,在INPUT鏈上的規則
監控到了這個集羣服務,會把請求報文的目標IP改成後端某一個被挑選出來的主機的RIP地址。並強行把報文送
到POSTROUTING鏈上後離開本機,然後到對應的RS上去了。
不管後端調度的是哪一臺RS,客戶端發往VIP的請求報文,收到的都是VIP的響應報文。這種節奏不能變。如上
圖解請求報文的目標ip被改變了,而響應報文的源IP被改變了。請求報文的目標IP是如何改的,此前說的DNAT,是需要我們手動指定要修改的目標IP。有了LVS之後,請求報文到底被髮往哪一個後端主機,我們自己是不能定
義的,是由調度器根據調度算法從後端多個可用的RIP中挑選出來的,而非固定的。這種被挑選就是通過調度算
法來控制的。
VIP應該是一個公網地址。DIP應該是一個私網地址。各後端主機爲了達到隱藏的目的,要和DIP一樣,都應該是
一個私網地址。而且爲了讓響應報文的源IP是VIP,必須要把各後端的RIP的網關指向DIP。

實驗環境說明:

客戶端主機:172.16.0.88
調度器主機:
    VIP:172.16.0.61
    DIP:192.168.10.254
後端主機1:192.168.10.11
後端主機2:192.168.10.12
虛擬機環境說明:172.16.0.0/16網段是我家庭網絡的橋接網段。我要把這個網段模擬成外網環境,實際並不是。
爲了減少實驗難度,就讓客戶端主機和調度器主機在同一個網絡。實際應用場景,VIP和CIP都是真實的外網環
境,而且CIP請求VIP可能要經過層層路由而到達。後端主機的RIP是一個僅主機模式的接口,它們的網關配置
指向了調度器的DIP(192.168.10.254)。調度器要想中轉報文,要開啓防火牆的核心轉發功能。

調度器的yum倉庫可用epel和base,而且可以指向互聯網,所以這個沒必要說,它自己的時鐘同步指向互聯網。
通過chronyd來同步。它自己本身也是一個時鐘服務器,可以被後端的RS同步使用。
各RS的時鐘不同指向DIP,通過chronyd來同步。

調度器主機上有安裝ipvsadm軟件包,這個用來編寫lvs規則用的。然後防火牆演示lvs的nat模式沒有開啓。默認我
關閉來selinux,firewalld。iptables策略就算INPUT有規則,也無法功能。
RS上安裝了nginx,httpd,telnet-server等服務器,爲的的演示需要。爲了需要,我給它們掛載來光盤,然後配置
了yum指向自己配置的光盤。

實驗環境圖:
在這裏插入圖片描述

實驗環境簡單查看:

#director有兩塊網卡,eno16777736是VIP,eno33554984是DIP
[yanhui@director ~]$ ifconfig
eno16777736: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.0.61  netmask 255.255.0.0  broadcast 172.16.255.255
        inet6 fe80::20c:29ff:febb:6862  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:bb:68:62  txqueuelen 1000  (Ethernet)
        RX packets 1506671  bytes 1072035825 (1022.3 MiB)
        RX errors 0  dropped 11  overruns 0  frame 0
        TX packets 997524  bytes 88755408 (84.6 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eno33554984: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.10.254  netmask 255.255.255.0  broadcast 192.168.10.255
        inet6 fe80::20c:29ff:febb:686c  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:bb:68:6c  txqueuelen 1000  (Ethernet)
        RX packets 41  bytes 5420 (5.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 25  bytes 1818 (1.7 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 149  bytes 13112 (12.8 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 149  bytes 13112 (12.8 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

後端RS1:rip1是eno16777736,其ip爲192.168.10.11,網關指向DIP,192.168.10.254
[yanhui@rs1 ~]$ ifconfig
eno16777736: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.10.11  netmask 255.255.255.0  broadcast 192.168.10.255
        inet6 fe80::20c:29ff:fe18:8676  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:18:86:76  txqueuelen 1000  (Ethernet)
        RX packets 532600  bytes 52940708 (50.4 MiB)
        RX errors 0  dropped 11  overruns 0  frame 0
        TX packets 770085  bytes 567926043 (541.6 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 83  bytes 5282 (5.1 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 83  bytes 5282 (5.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[yanhui@rs1 ~]$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.10.254  0.0.0.0         UG    100    0        0 eno16777736
192.168.10.0    0.0.0.0         255.255.255.0   U     100    0        0 eno16777736

後端RS2:rip2是eno16777736,其ip爲192.168.10.12,網關指向DIP,192.168.10.254
[yanhui@rs2 ~]$ ifconfig
eno16777736: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.10.12  netmask 255.255.255.0  broadcast 192.168.10.255
        inet6 fe80::20c:29ff:fe99:19a4  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:99:19:a4  txqueuelen 1000  (Ethernet)
        RX packets 536180  bytes 53205796 (50.7 MiB)
        RX errors 0  dropped 11  overruns 0  frame 0
        TX packets 771806  bytes 568494213 (542.1 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 85  bytes 5414 (5.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 85  bytes 5414 (5.2 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

配置RS1上nginx的測試訪問站點文件:
[yanhui@rs1 ~]$ sudo vim /usr/share/nginx/html/test1.html 
[yanhui@rs1 ~]$ cat /usr/share/nginx/html/test1.html
<h1>RS1,192.168.10.11 </h1>
[yanhui@rs1 ~]$ sudo systemctl start nginx.service 
[yanhui@rs1 ~]$ ps aux|grep nginx
root       8868  0.0  0.9 124976  2116 ?        Ss   00:18   0:00 nginx: master process /usr/sbin/nginx
nginx      8869  0.0  1.3 125364  3144 ?        S    00:18   0:00 nginx: worker process

配置RS2上nginx的測試訪問站點文件:
[yanhui@rs2 ~]$ sudo vim /usr/share/nginx/html/test1.html 
[yanhui@rs2 ~]$ cat /usr/share/nginx/html/test1.html
<h1>RS2,192.168.10.12 </h1>
[yanhui@rs2 ~]$ sudo systemctl start nginx.service
[yanhui@rs2 ~]$ ps aux|grep nginx
root       8763  0.0  0.9 124976  2112 ?        Ss   00:18   0:00 nginx: master process /usr/sbin/nginx
nginx      8764  0.0  1.3 125364  3140 ?        S    00:18   0:00 nginx: worker process
yanhui     8766  0.0  0.4 112640   960 pts/1    S+   00:18   0:00 grep --color=auto nginx

先在調度器上簡單訪問一下:
[yanhui@director ~]$ curl http://192.168.10.11/test1.html
<h1>RS1,192.168.10.11 </h1>
[yanhui@director ~]$ curl http://192.168.10.12/test1.html
<h1>RS2,192.168.10.12 </h1>
#vs,rs1,rs2要時間同步,沒有誤差。可以自行配置實現。這裏就省略啦。簡單看看時間是同步的:
[yanhui@director ~]$ date
Tue Jan 15 00:23:13 CST 2019
[yanhui@rs1 ~]$ date
Tue Jan 15 00:23:18 CST 2019
[yanhui@rs2 ~]$ date
Tue Jan 15 00:23:23 CST 2019

#打開調度器的服務器核心轉發功能
[yanhui@director ~]$ sudo sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
[yanhui@director ~]$ cat /proc/sys/net/ipv4/ip_forward
1

配置集羣服務1(把tcp協議的80端口定義爲一個集羣服務,然後使用輪詢調度方法):
[yanhui@director ~]$ sudo ipvsadm -A -t 172.16.0.61:80 -s rr 
[yanhui@director ~]$ sudo ipvsadm -Ln #-L要在-n前邊,不能使用-nL組合
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.0.61:80 rr

向剛纔定義的tcp的80端口的集羣服務添加後端real server:
[yanhui@director ~]$ sudo ipvsadm -a -t 172.16.0.61:80 -r 192.168.10.11 -m
[yanhui@director ~]$ sudo ipvsadm -a -t 172.16.0.61:80 -r 192.168.10.12 -m
[yanhui@director ~]$ sudo ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.0.61:80 rr
  -> 192.168.10.11:80             Masq    1      0          0         
  -> 192.168.10.12:80             Masq    1      0          0    

客戶端上測試集羣訪問:
#如果不能訪問,請確認一下你Director主機的核心轉發功能是否有開啓
[yanhui@host11 ~]$ for i in {1..10};do
> curl http://172.16.0.61/test1.html
> done
<h1>RS2,192.168.10.12 </h1>
<h1>RS1,192.168.10.11 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS1,192.168.10.11 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS1,192.168.10.11 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS1,192.168.10.11 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS1,192.168.10.11 </h1>
#可以仔細觀察上面的集羣服務器響應主機的結果,是按照後端主機輪詢的方式響應的


修改後端被調度主機的權重值:
[yanhui@director ~]$ sudo ipvsadm -e -t 172.16.0.61:80 -r 192.168.10.11 -m -w 2 
[yanhui@director ~]$ sudo ipvsadm -e -t 172.16.0.61:80 -r 192.168.10.12 -m -w 3
[yanhui@director ~]$ sudo ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.0.61:80 rr
  -> 192.168.10.11:80             Masq    2      0          0         
  -> 192.168.10.12:80             Masq    3      0          0 
#默認輪詢rr調度方法,後端主機的權重即使不一樣,也沒有效果,現在我們還要把集羣服務的調度方法修改成wrr

修改集羣服務的調度方法爲加權輪詢:
[yanhui@director ~]$ sudo ipvsadm -E -t 172.16.0.61:80 -s wrr
[yanhui@director ~]$ sudo ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.0.61:80 wrr
  -> 192.168.10.11:80             Masq    2      0          0         
  -> 192.168.10.12:80             Masq    3      0          0   


客戶端測試集羣訪問情況:
[yanhui@host11 ~]$ for i in {1..10};do curl http://172.16.0.61/test1.html; done
<h1>RS2,192.168.10.12 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS1,192.168.10.11 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS1,192.168.10.11 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS1,192.168.10.11 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS1,192.168.10.11 </h1>
[yanhui@host11 ~]$ for i in {1..10};do curl http://172.16.0.61/test1.html; done
<h1>RS2,192.168.10.12 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS1,192.168.10.11 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS1,192.168.10.11 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS1,192.168.10.11 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS1,192.168.10.11 </h1>
#從結果來看,雖然不是一直3比1,但是有個規則,先2:1,然後1:1,然後2:1,然後1:1,如此反覆。總結果就是
3:2

讓一個後端RS不再接受請求的方式有兩種:設置它的權重爲0;把它從集羣中刪除或移除;
(1) 設置權重爲0就是修改
[yanhui@director ~]$ sudo ipvsadm -e -t 172.16.0.61:80 -r 192.168.10.11 -m -w 0
[yanhui@director ~]$ sudo ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.0.61:80 wrr
  -> 192.168.10.11:80             Masq    0      0          0         
  -> 192.168.10.12:80             Masq    3      0          0   
測試客戶端訪問結果:
[yanhui@host11 ~]$ for i in {1..10};do curl http://172.16.0.61/test1.html; done
<h1>RS2,192.168.10.12 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS2,192.168.10.12 </h1>
<h1>RS2,192.168.10.12 </h1>

(2) 從集羣服務中移除RS
[yanhui@director ~]$ sudo ipvsadm -d -t 172.16.0.61:80 -r 192.168.10.11 
[yanhui@director ~]$ sudo ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.0.61:80 wrr
  -> 192.168.10.12:80             Masq    3      0          30   

#其他的就不演示了。LVS負載均衡集羣服務中有後端RS異常,集羣服務本身不能做到移除這個RS,還是會調度
請求到異常的後端主機上去。此時,就需要藉助其他輔助程序來配合使用,而實際使用場景中,Director要做高可
用集羣,有些高可用集羣實現程序本身就支持這種後端RS監控狀態監測機制,可以實現把後端異常的主機移除,
修復後可以自動上線等等。

3.3.2、LVS的DR類型

LVS的DR類型滿足以下概要說明的特點:

Direct Routing,直接路由;
通過爲請求報文重新封裝一個MAC首部進行轉發,源MAC是DIP所在的接口的MAC,目標MAC是某挑選出的RS
的RIP所在接口的MAC地址;源IP/PORT,以及目標IP/PORT均保持不變;
Director和各RS都得配置使用VIP;

(1) 確保前端路由器將目標IP爲VIP的請求報文發往Director:
	(a) 在前端網關做靜態綁定;
	(b) 在RS上使用arptables;
	(c) 在RS上修改內核參數以限制arp通告及應答級別;
		arp_announce
		arp_ignore
(2) RS的RIP可以使用私網地址,也可以是公網地址;RIP與DIP在同一IP網絡;RIP的網關不能指向DIP,以確保響應報文不會經由Director;
(3) RS跟Director要在同一個物理網絡;
(4) 請求報文要經由Director,但響應不能經由Director,而是由RS直接發往Client;

lvs-dr模式的請求報文和響應報文的圖解:
在這裏插入圖片描述

在這裏插入圖片描述

實驗環境說明:

客戶端主機:172.16.0.88
調度器主機:
    VIP:172.16.0.61
    DIP:192.168.10.254(假設這個ip我們就不禁止,就當做內網ip)
後端主機1:172.16.0.62。lo網卡接口地址別名(lo:0)上配置來vip,爲了是先讓交換機丟過來的請求報文經過lo接
口;爲了是讓從RS出去的響應報文的原地址是lo:0上的VIP。因爲linux內核有個特點,先經過那個網卡的報文,
出去的報文的原地址就是誰。(還要通過arptables或者通過內核參數,控制lo:0接口的arp廣播和arp廣播通告)
後端主機2:172.16.0.63。lo網卡接口地址別名(lo:0)上配置了vip;爲了是先讓交換機丟過來的請求報文經過lo接口;
虛擬機環境說明:172.16.0.0/16網段是我家庭網絡的橋接網段。假設這個網段是模擬的外網網段。

調度器的yum倉庫可用epel和base,而且可以指向互聯網,所以這個沒必要說,它自己的時鐘同步指向互聯網。
通過chronyd來同步。它自己本身也是一個時鐘服務器,可以被後端的RS同步使用。
各RS的時鐘不同指向DIP,通過chronyd來同步。

調度器主機上有安裝ipvsadm軟件包,這個用來編寫lvs規則用的。然後防火牆演示lvs的nat模式沒有開啓。默認我
關閉來selinux,firewalld。iptables策略就算INPUT有規則,也無法功能。
RS上安裝了nginx,httpd,telnet-server等服務器,爲的的演示需要。

解決地址衝突配置說明:
dr模型中,各主機上均需要配置VIP,解決地址衝突的方式有三種:
	(1) 在前端網關做靜態綁定;(不適合)
	(2) 在各RS使用arptables;(需要額外安裝和使用arptables,可以實現)
	(3) 在各RS修改內核參數,來限制arp響應和通告的級別;(可以實現,較第二種方法簡單)
		限制響應級別:arp_ignore
			0:默認值,表示可使用本地任意接口上配置的任意地址進行響應;
			1: 僅在請求的目標IP配置在本地主機的接收到請求報文接口上時,纔給予響應;
		限制通告級別:arp_announce
			0:默認值,把本機上的所有接口的所有信息向每個接口上的網絡進行通告;
			1:儘量避免向非直接連接網絡進行通告;
			2:必須避免向非本網絡通告;

在這裏插入圖片描述

通告和響應簡述:

假設A主機有三個網絡接口,分別是B,C,D。B所在網絡爲2.1,C所在網絡爲3.1,D所在網絡爲1.1。
默認主機A的行爲,在網絡領域有一個通告和響應的概念。B,C,D三個網絡接口都對應一個MAC,假設
分別爲B-MAC,C-MAC,D-MAC。與D直連網絡是1.0,其中網絡中一個一個主機是s1,對應ip是1.2,mac是
s1-mac。與B直連網絡是2.0,其中網絡中一個主機是s2,對應ip是2.3,mac是s2-mac.C直連的網絡是3.0,對應
網絡中有一個主機是s3,對應ip是3.2。

默認行爲是A主機會通告給1.0網絡,2.0網絡,3.0網絡,它的三個接口B,C,D的信息,包括IP和MAC地址。
D的直連網絡是1.0,B的直連網絡是2.0,C的直連網絡是3.0.由於這種默認行爲,如果有一天有臺2.2的機器去請
求D接口,並告知,我需要和2.3主機通信,請告訴我2.3主機的MAC地址,經過arp廣播後,A主機會告知它自己
B接口,並知道B接口所在網絡的s2主機的mac地址的,這種屬於arp響應,每個接口所在網絡都知道A主機的三個
接口的信息,這個是A主機默認通告行爲促成。

實驗環境簡單配置:

(1) director初始狀態:(相比nat,這裏只需要一個接口,如果是配置VIP,可以配置一個別名即可)
[yanhui@director ~]$ ifconfig
eno16777736: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.0.61  netmask 255.255.0.0  broadcast 172.16.255.255
        inet6 fe80::20c:29ff:febb:6862  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:bb:68:62  txqueuelen 1000  (Ethernet)
        RX packets 84368  bytes 60108647 (57.3 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 55860  bytes 4974867 (4.7 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

(2) RS1初始狀態(還未配置lo:0的vip地址)
[yanhui@rs1 ~]$ ifconfig
eno16777736: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.0.62  netmask 255.255.0.0  broadcast 172.16.255.255
        inet6 fe80::20c:29ff:fe18:8676  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:18:86:76  txqueuelen 1000  (Ethernet)
        RX packets 52285  bytes 9958142 (9.4 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 58891  bytes 36026131 (34.3 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 42  bytes 2792 (2.7 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 42  bytes 2792 (2.7 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        
(3) RS2初始狀態(還未配置lo:0的vip地址)
[yanhui@rs2 ~]$ ifconfig
eno16777736: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.0.63  netmask 255.255.0.0  broadcast 172.16.255.255
        inet6 fe80::20c:29ff:fe99:19a4  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:99:19:a4  txqueuelen 1000  (Ethernet)
        RX packets 55280  bytes 10185276 (9.7 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 60633  bytes 36617412 (34.9 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 49  bytes 3376 (3.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 49  bytes 3376 (3.2 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
PS:時間,yum倉庫,實驗服務我就不說了,這些都是基礎,還有配置主頁什麼的,也不提供了。
簡單看看:
RS1站點的測試的靜態資源和測試的動態資源:
[yanhui@rs1 ~]$ cat /var/www/html/phpinfo.php 
<h1>RS1</h2>
<?php
    phpinfo();
?>
[yanhui@rs1 ~]$ cat /var/www/html/test1.html 
<h1>RS1,172.16.0.62</h1>

RS2的httpd站點的測試的靜態資源和測試的動態資源:(爲了看出效果,這裏有意把資源內容定義不一樣,實際
生產應用要一樣的)
[yanhui@rs2 ~]$ cat /var/www/html/phpinfo.php 
<h1>RS2</h2>
<?php
    phpinfo();
?>
[yanhui@rs2 ~]$ cat /var/www/html/test1.html 
<h1>RS2,172.16.0.63</h1>

配置arp通告和arp響應的簡單查看:
[yanhui@rs2 ~]$ ls -l /proc/sys/net/ipv4/conf/
total 0
dr-xr-xr-x 1 root root 0 Jan 12 23:43 all
dr-xr-xr-x 1 root root 0 Jan 14 01:15 default
dr-xr-xr-x 1 root root 0 Jan 14 01:15 eno16777736
dr-xr-xr-x 1 root root 0 Jan 12 23:43 lo
#all表示所有,lo和eno16777736表示指定單個接口。

這裏有個實現禁止通告和響應的功能以及配置lo:0接口vip地址的功能的bash腳本;
[yanhui@rs1 ~]$ pwd
/home/yanhui
[yanhui@rs1 ~]$ ls -l nomal_setparam.sh 
-rw-r--r-- 1 root root 795 Jan 12 23:58 nomal_setparam.sh
[yanhui@rs1 ~]$ cat nomal_setparam.sh 
#! /bin/bash

vip=172.16.0.199
mask=255.255.255.255 #爲了讓它自己和自己在一個網絡,也只廣播給自己,所以這樣定義
iface='lo:0'

case $1 in
start)
    echo 1 | sudo tee /proc/sys/net/ipv4/conf/all/arp_ignore
    echo 1 | sudo tee /proc/sys/net/ipv4/conf/lo/arp_ignore
    echo 2 | sudo tee /proc/sys/net/ipv4/conf/all/arp_announce
    echo 2 | sudo tee /proc/sys/net/ipv4/conf/lo/arp_announce
    
    ifconfig $iface $vip netmask $mask broadcast $vip up
    route add -host $vip dev $iface
    ;;
stop)
    
    ifconfig $iface down
    echo 0 | sudo tee  /proc/sys/net/ipv4/conf/all/arp_ignore
    echo 0 | sudo tee /proc/sys/net/ipv4/conf/lo/arp_ignore
    echo 0 | sudo tee /proc/sys/net/ipv4/conf/all/arp_announce
    echo 0 | sudo tee /proc/sys/net/ipv4/conf/lo/arp_announce
    ;;
*)
    echo "Usage: $(basename $0) start|stop"
    exit 1
    ;;
esac

RS1上執行這個腳本:
[yanhui@rs1 ~]$ sudo bash nomal_setparam.sh start
1
1
2
2
[yanhui@rs1 ~]$ ifconfig
eno16777736: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.0.62  netmask 255.255.0.0  broadcast 172.16.255.255
        inet6 fe80::20c:29ff:fe18:8676  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:18:86:76  txqueuelen 1000  (Ethernet)
        RX packets 56198  bytes 10306939 (9.8 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 64372  bytes 40006574 (38.1 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 42  bytes 2792 (2.7 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 42  bytes 2792 (2.7 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo:0: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 172.16.0.199  netmask 255.255.255.255
        loop  txqueuelen 0  (Local Loopback)
[yanhui@rs1 ~]$ cat /proc/sys/net/ipv4/conf/{all,lo}/{arp_announce,arp_ignore} 
2
1
2
1

RS2上執行這個腳本:
[yanhui@rs2 ~]$ sudo bash nomal_setparam.sh start
1
1
2
2
[yanhui@rs2 ~]$ ifconfig
eno16777736: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.0.63  netmask 255.255.0.0  broadcast 172.16.255.255
        inet6 fe80::20c:29ff:fe99:19a4  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:99:19:a4  txqueuelen 1000  (Ethernet)
        RX packets 59542  bytes 10566445 (10.0 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 66675  bytes 40996952 (39.0 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 49  bytes 3376 (3.2 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 49  bytes 3376 (3.2 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo:0: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 172.16.0.199  netmask 255.255.255.255
        loop  txqueuelen 0  (Local Loopback)

[yanhui@rs2 ~]$ cat /proc/sys/net/ipv4/conf/{all,lo}/{arp_announce,arp_ignore} 
2
1
2
1

#爲了讓報文先從RS的lo:0進來,我們需要給每個後端的RS添加一個主機路由條目,下面是單獨執行的過程。
如果集成到了腳本中,就可以忽略了,我上面給的腳本集成了,這裏只是簡單演示添加和刪除路由。
我執行腳本後有添加上這個主機路由,然後爲了演示,特意刪除了的。
RS1:
[yanhui@rs1 ~]$ sudo route del -host 172.16.0.199 #純粹爲了演示增刪路由
[yanhui@rs1 ~]$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.16.0.1      0.0.0.0         UG    100    0        0 eno16777736
172.16.0.0      0.0.0.0         255.255.0.0     U     100    0        0 eno16777736
[yanhui@rs1 ~]$ sudo route add -host 172.16.0.199 dev lo:0
[yanhui@rs1 ~]$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.16.0.1      0.0.0.0         UG    100    0        0 eno16777736
172.16.0.0      0.0.0.0         255.255.0.0     U     100    0        0 eno16777736
172.16.0.199    0.0.0.0         255.255.255.255 UH    0      0        0 lo

RS2:
[yanhui@rs2 ~]$ sudo route del -host 172.16.0.199
[yanhui@rs2 ~]$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.16.0.1      0.0.0.0         UG    100    0        0 eno16777736
172.16.0.0      0.0.0.0         255.255.0.0     U     100    0        0 eno16777736
[yanhui@rs2 ~]$ sudo route add -host 172.16.0.199 dev lo:0
[yanhui@rs2 ~]$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.16.0.1      0.0.0.0         UG    100    0        0 eno16777736
172.16.0.0      0.0.0.0         255.255.0.0     U     100    0        0 eno16777736
172.16.0.199    0.0.0.0         255.255.255.255 UH    0      0        0 lo


RS1啓動httpd服務:
[yanhui@rs1 ~]$ sudo systemctl start httpd.service
[yanhui@rs1 ~]$ ps aux|grep httpd
root       5529  1.2  6.1 404100 14012 ?        Ss   01:30   0:00 /usr/sbin/httpd -DFOREGROUND
apache     5531  0.0  3.3 404232  7744 ?        S    01:30   0:00 /usr/sbin/httpd -DFOREGROUND
apache     5532  0.0  3.3 404232  7744 ?        S    01:30   0:00 /usr/sbin/httpd -DFOREGROUND
apache     5533  0.0  3.3 404232  7744 ?        S    01:30   0:00 /usr/sbin/httpd -DFOREGROUND
apache     5534  0.0  3.3 404232  7744 ?        S    01:30   0:00 /usr/sbin/httpd -DFOREGROUND
apache     5535  0.0  3.3 404232  7744 ?        S    01:30   0:00 /usr/sbin/httpd -DFOREGROUND
yanhui     5537  0.0  0.4 112640   956 pts/0    R+   01:30   0:00 grep --color=auto httpd

RS2啓動httpd服務:
[yanhui@rs2 ~]$ sudo systemctl start httpd.service
[yanhui@rs2 ~]$ ps aux|grep httpd
root       5481  2.0  6.1 404100 14012 ?        Ss   01:30   0:00 /usr/sbin/httpd -DFOREGROUND
apache     5483  0.0  3.3 404232  7740 ?        S    01:30   0:00 /usr/sbin/httpd -DFOREGROUND
apache     5484  0.0  3.3 404232  7740 ?        S    01:30   0:00 /usr/sbin/httpd -DFOREGROUND
apache     5485  0.0  3.3 404232  7740 ?        S    01:30   0:00 /usr/sbin/httpd -DFOREGROUND
apache     5486  0.0  2.9 404232  6760 ?        S    01:30   0:00 /usr/sbin/httpd -DFOREGROUND
apache     5487  0.0  3.2 404232  7508 ?        S    01:30   0:00 /usr/sbin/httpd -DFOREGROUND
yanhui     5489  0.0  0.4 112640   960 pts/0    S+   01:30   0:00 grep --color=auto httpd

在前端主機(director主機)上直接請求測試一下這兩臺RS的RIP接口的80訪問情況:
[yanhui@director ~]$ curl http://172.16.0.62/test1.html
<h1>RS1,172.16.0.62</h1>
[yanhui@director ~]$ curl http://172.16.0.63/test1.html
<h1>RS2,172.16.0.63</h1>
[yanhui@director ~]$ curl -I http://172.16.0.62/phpinfo.php
HTTP/1.1 200 OK
Date: Sun, 13 Jan 2019 17:32:04 GMT
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/5.4.16
X-Powered-By: PHP/5.4.16
Content-Type: text/html; charset=UTF-8

[yanhui@director ~]$ curl -I http://172.16.0.63/phpinfo.php
HTTP/1.1 200 OK
Date: Sun, 13 Jan 2019 17:32:15 GMT
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/5.4.16
X-Powered-By: PHP/5.4.16
Content-Type: text/html; charset=UTF-8

在Director上配置VIP地址(172.16.0.199),因爲要通過物理網卡eno16777736接進來請求報文,並通過物理網卡
轉發報文給交換機。所以VIP也要配置到物理網卡上,我們可以配置網卡別名來實現:
[yanhui@director ~]$ sudo ifconfig eno16777736:0 172.16.0.199 netmask 255.255.255.255 broadcast 172.16.0.199 up
[yanhui@director ~]$ ifconfig
eno16777736: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.0.61  netmask 255.255.0.0  broadcast 172.16.255.255
        inet6 fe80::20c:29ff:febb:6862  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:bb:68:62  txqueuelen 1000  (Ethernet)
        RX packets 113955  bytes 80856593 (77.1 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 76000  bytes 6823007 (6.5 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eno16777736:0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.0.199  netmask 255.255.255.255  broadcast 172.16.0.199
        ether 00:0c:29:bb:68:62  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

集羣定義和測試:

#確認一下ipvsadm工具是否有安裝:
[yanhui@director ~]$ rpm -q ipvsadm
ipvsadm-1.27-7.el7.x86_64

#定義集羣(集羣調度方法爲輪詢 rr)
[yanhui@director ~]$ sudo ipvsadm -A -t 172.16.0.199:80 -s rr
[yanhui@director ~]$ sudo ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.0.199:80 rr
FWM  3 rr
  -> 127.0.0.1:80                 Route   1      0          0  

#添加後端主機(-g是默認類型dr的選項。)
[yanhui@director ~]$ sudo ipvsadm -a -t 172.16.0.199:80 -r 172.16.0.62 -g 
[yanhui@director ~]$ sudo ipvsadm -a -t 172.16.0.199:80 -r 172.16.0.63 -g 
[yanhui@director ~]$ sudo ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.16.0.199:80 rr
  -> 172.16.0.62:80               Route   1      0          0         
  -> 172.16.0.63:80               Route   1      0          0         
FWM  3 rr
  -> 127.0.0.1:80                 Route   1      0          0   

客戶端環境:
[yanhui@host11 ~]$ ifconfig
eno16777736: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.0.88  netmask 255.255.0.0  broadcast 172.16.255.255
        inet6 fe80::20c:29ff:feae:a6bf  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:ae:a6:bf  txqueuelen 1000  (Ethernet)
        RX packets 70376  bytes 8727803 (8.3 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 92011  bytes 8177456 (7.7 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 0  (Local Loopback)
        RX packets 35  bytes 2486 (2.4 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 35  bytes 2486 (2.4 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
訪問測試集羣:(結果是否符合輪詢,要簡單核對一下)
[yanhui@host11 ~]$ for i in {1..10};do
> curl http://172.16.0.199/test1.html
> done
<h1>RS2,172.16.0.63</h1>
<h1>RS1,172.16.0.62</h1>
<h1>RS2,172.16.0.63</h1>
<h1>RS1,172.16.0.62</h1>
<h1>RS2,172.16.0.63</h1>
<h1>RS1,172.16.0.62</h1>
<h1>RS2,172.16.0.63</h1>
<h1>RS1,172.16.0.62</h1>
<h1>RS2,172.16.0.63</h1>
<h1>RS1,172.16.0.62</h1>

關於動態資源,可以在瀏覽器上簡單測試一下。,我可以用curl -I選項簡單測試兩次:
[yanhui@host11 ~]$ curl -I http://172.16.0.199/phpinfo.php
HTTP/1.1 200 OK
Date: Sun, 13 Jan 2019 17:43:14 GMT
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/5.4.16
X-Powered-By: PHP/5.4.16
Content-Type: text/html; charset=UTF-8

[yanhui@host11 ~]$ curl -I http://172.16.0.199/phpinfo.php
HTTP/1.1 200 OK
Date: Sun, 13 Jan 2019 17:43:14 GMT
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/5.4.16
X-Powered-By: PHP/5.4.16
Content-Type: text/html; charset=UTF-8

在這裏插入圖片描述

在這裏插入圖片描述

可以看看172.16.0.199的mac地址是否爲Director的VIP:
[yanhui@host11 ~]$ arp -a
director (172.16.0.61) at 00:0c:29:bb:68:62 [ether] on eno16777736
? (172.16.0.14) at d4:81:d7:87:2f:21 [ether] on eno16777736
www.yanhui.com (172.16.0.199) at 00:0c:29:bb:68:62 [ether] on eno16777736
? (172.16.0.62) at 00:0c:29:18:86:76 [ether] on eno16777736
? (172.16.0.63) at 00:0c:29:99:19:a4 [ether] on eno16777736
? (172.16.0.1) at dc:fe:18:b4:bc:79 [ether] on eno16777736

[yanhui@director ~]$ ifconfig eno16777736:0
eno16777736:0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.16.0.199 netmask 255.255.255.255 broadcast 172.16.0.199
ether 00:0c:29:bb:68:62 txqueuelen 1000 (Ethernet)
#可以看出廣播的172.16.0.199的mac地址是Director的物理接口別名網卡en016777736:0的mac地址。

3.3.3、LVS的tun類型簡單報文圖解(隧道)

在這裏插入圖片描述

3.3.4、LVS的fullnat類型簡單報文圖解

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章