Nginx 無痛入門指南

在這個新的專題當中,筆者整理了Nginx的基本內容,以及在虛擬機環境下模擬部署高可用集羣HA。咱們主要從如下內容入手:php

  1. Nginx是什麼?它是用來作什麼的?
  2. Nginx在Linux系統下的安裝,包括反向代理,負載均衡,動靜分離,配置高可用集羣。

Nginx簡介

Nginx (engine x)是一個高性能的HTTP和反向代理web服務器,同時也提供IMAP/POP3/SMTP服務。它的特色是:佔用內存小,併發能力強css

Nginx能夠做爲靜態的web服務器,同時支持CGI協議的動態語言,好比perl,php。而JavaWeb項目則須要與tomcat配合完成。Nginx專門爲了優化而開發,性能是最重要的考量。html

Nginx支持熱部署。能夠在不間斷服務的狀況下,對軟件版本進行升級。linux

反向代理

在介紹反向代理以前,首先介紹,什麼是正向代理?nginx

咱們平常所使用的虛擬專用網絡就屬於正向代理。好比國內的客戶想要直接訪問Google官網是沒法鏈接的,但若是該用戶能夠訪問某服務器A,而A能夠訪問Google官網,那麼客戶能夠將此服務器A設置爲代理服務器,藉助A服務器請求得到Google的響應報文,再轉發給用戶。此時這個服務器A稱之爲正向代理服務器。git

也就是說正向代理,須要用戶主動配置代理服務器。 web

而反向代理,客戶是對代理無感知的。客戶只須要向公開的URL向代理服務器發送請求,代理服務器再選擇目標服務器處理請求並返回數據,以下圖所示。用戶並不關心是哪一個具體的目標服務器替他完成了服務,虛線右側的目標服務器對客戶來講是透明的。

負載均衡

若是一輛貨車很難拉動一大批貨物,那就再叫上幾輛車。算法

客服端發送多個請求到服務器,服務器處理請求,有些請求須要與數據庫交互,直到服務器處理完畢以後,再將結果返回給客戶端。docker

這種架構模式對於早期併發請求較少的狀況下仍是比較適合的,且維護成本也比較低。可是咱們已經都知道:摩爾定律逐漸失效,硬件的性能提高已經跟不上咱們實際的性能要求了。好比天貓雙十一,其瞬時訪問量都是很是龐大的,咱們即便將單節點升級到如今的頂級配置也很難應對(這還沒考慮到升級設備的成本)。所以如今的服務都逐漸採用集羣方式。

所謂負載均衡,就是反向代理服務器將大批量的請求分散到集羣的節點當中,以減小單點服務器的壓力,避免服務器崩潰的現象。 數據庫

動靜分離

爲了加快網站的解析速度,咱們能夠把動態頁面和靜態頁面分散到不一樣的服務器進行解析,加快解析速度,下降單個服務器的壓力。

Nginx安裝

咱們的主場仍舊是Linux系統。咱們能夠進入到nginx官網,下載對應的版本,而後發送到咱們的雲服務器/虛擬機當中安裝,可是安裝Nginx須要解決大量的依賴問題,在此不推薦用此方式。

筆者的虛擬機是CentOS 7.8 x86_64版本,在這裏經過簡單配置yum源的方式來安裝(和筆者安裝Docker的過程比較相似):

若是以前沒有安裝yum-utils,則須要先安裝它:

$ yum install yum-utils
複製代碼

進入到/etc/yum.reops.d/目錄下,建立一個nginx的下載源配置文件:

$ vim nginx.repo
複製代碼

在該文件中添加如下配置:

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
複製代碼

配置完畢後,就可使用yum命令安裝nginx服務器了。

#centOS7默認是沒有nginx安裝包的。
$ yum search nginx
$ yum install nginx
複製代碼

咱們可使用find命令查看nginx的安裝路徑:

$ find / | grep nginx.conf
複製代碼

此外,Nginx官網提供了RHEL/CentOS, Debian, Ubuntu等快捷安裝方式。

📦Nginx提供的官方安裝教程(適用於Linux內核)

嘗試啓動Nginx

咱們找到Nginx的啓動腳本,而後啓動Nginx服務(Nginx啓動腳本在/usr/sbin目錄下):

$ /usr/sbin/nginx
# 或者直接nginx也能夠。
# 或者systemctl start nginx
# 查看進程中是否有nginx
$ ps -ef | grep nginx
複製代碼

配置文件爲/etc/nginx/nginx.conf,若是找不到nginx的配置路徑,能夠經過剛纔的find命令搜索到。

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush on;

    keepalive_timeout  65;

    #gzip on;

    include /etc/nginx/conf.d/*.conf;
}
複製代碼

經過觀察該配置文件的最後一行,筆者注意到此下載版本中,nginx的配置被分散到了兩個路徑下:/etc/nginx/conf.d/*.conf

咱們再去瀏覽此/cet.nginx/conf.d/目錄下,初始只有一個文件(/etc/nginx/conf.d/default.conf),經過listen可以觀察到Nginx的默認啓動端口是80。

server {
    listen       80;
    server_name  localhost;

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

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page 404 /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    # proxy_pass http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    # root html;
    # fastcgi_pass 127.0.0.1:9000;
    # fastcgi_index index.php;
    # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
    # include fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    # deny all;
    #}
}
複製代碼

咱們在瀏覽器中輸入對應的url(若是監聽端口默認爲80,咱們能夠不主動地輸入端口號)來訪問nginx服務器,其中hadoop102是筆者已經配置好的主機名。

hadoop102
複製代碼

當看到這個頁面時,就說明咱們在虛擬機中啓動的Nginx服務器成功了。

配置Linux防火牆規則

在Linux的firewalld防火牆啓動狀態下,若是不進行任何配置,咱們是沒法訪問到80端口的。咱們首先使用firewall命令查看目前開放的端口:

$ firewall-cmd --list-all
複製代碼

咱們設置80端口號是開放的:

$ firewall-cmd --add-service=http --permanent
$ firewall-cmd --add-port=80/tcp --permanent
#重啓firewall-cmd --reload
$ firewall-cmd --reload
複製代碼

Nginx經常使用命令

查看Nginx的版本號:

$ nginx -v
#筆者的安裝版本是:
#nginx version: nginx/1.18.0
複製代碼

啓動Nginx服務:

$ nginx
複製代碼

中止Nginx服務:

$ nginx -s stop
複製代碼

重加載Nginx(咱們在開篇就提到過,Nginx支持熱部署,所以只須要從新刷新Nginx服務就能夠):

$ nginx -s reload
複製代碼

Nginx配置文件

咱們首先要對下面三個路徑比較清晰:

啓動腳本 -> /usr/sbin/nginx
主配置文件 -> /etc/nginx/nginx.conf
副配置文件-> /etc/nginx/conf.d/default.conf
複製代碼

Nginx配置文件主要由三大塊構成:全局塊,events塊,http塊。其中,http塊爲核心,具體又可細分。:

全局塊
events塊
http塊
  ┗ http全局塊
  ┗ server塊
      ┗ 全局server塊
      ┗ location塊
複製代碼

全局塊

做用域爲文件開始到events塊以前的部分。這裏的配置會影響到nginx服務器總體的運行

其中,work_process表示可併發處理的請求數量。這個配置取決於機器實際的硬件/軟件配置。

work_process 1
複製代碼

events塊

這裏的配置會影響到Nginx服務器和用戶的網絡鏈接

其中,worker_connections表示最大的鏈接數,它會影響到Nginx的整體性能,因此應當靈活配置。

http塊

這一塊是配置的主要內容,http塊還包含了兩部分:http全局塊,server塊。其中nginx默認將server塊單獨遷移到了另一個配置文件中。

http全局塊包括引入文件(如引入server塊所在的配置文件),MIME-TYPE定義,日誌自定義,鏈接超時時間,單連接請求數上限等內容。

server塊

server塊和虛擬主機由密切的聯繫,而從客戶的角度而言,虛擬主機和獨立的主機是沒有區別的。它主要是爲了節省互聯網服務器硬件的成本。

一個http塊,能夠包含多個server塊。每一個server塊都至關因而一個虛擬主機。而每一個server塊又分爲全局server塊,和一個(或者多個)location塊

下面表明其中一個虛擬主機:啓動在本地,並開啓80端口。

listen			80;
server_name		localhost;
複製代碼

location塊配置了請求url和資源的映射(咱們在後續的實例中會進行更詳細的配置),原配置文件中其實有大量有關location的註釋內容,可是咱們基本均可以推測出其大體功能。

location / {
	root html;
	index index.html index.htm
}
複製代碼

配置反向代理

咱們在Nginx簡介中介紹過了反向代理的做用。咱們主要依賴http block -> servr block ->location block來配置具體的反向代理。對於動態請求,咱們常使用proxy_pass映射到指定主機號的對應端口,對於靜態請求,咱們也可使用root將本機設爲靜態資源服務器,用於返回靜態頁面(詳見配置動靜分離的章節)。咱們還須要對location的uri匹配部分作一個基本的瞭解,才能避免意外的404問題。

關於Location的配置指令

實際上location能夠包含如下形式:

location [ = | ~ | ~* | ^~ ] uri{
	....
}
複製代碼
  • =表示最嚴格的精確匹配。好比配置location以下:

    # www.nginxText.com/hi/a.html => 127.0.0.1:9998/hello/a.html
    location = /hi/a.html {
    	proxy_pass http://127.0.0.1:9998;
    }
    複製代碼
  • ^~表示在正則匹配以前進行前綴匹配。

    # www.nginxText.com/hi/a.html => 127.0.0.1:9998/helloworld/a.html
    location ^~ /hi {
    	proxy_pass http:127.0.0.1:9998/helloworld
    }
    複製代碼
  • ~表示請求的uri包含了區分大小寫的正則匹配

    # www.nginxText.com/hello/a.html => 127.0.0.1:9998/hello/a.html
    location ~ /hello/ {
    	#不容許在proxy_pass配置uri
    	proxy_pass http:127.0.0.1:9998
    }
    複製代碼
  • ~*表示請求的uri包含了不區分大小寫的正則匹配。

    # www.nginxText.com/hello/cafe.jpg => 127.0.0.1:9998/hello/cafe.jpg
    # www.nginxText.com/hello/cafe.JPG => 127.0.0.1:9998/hello/cafe.jpg
    location ~* \.(jpg|png)$ {
    	#不容許在proxy_pass配置uri
    	proxy_pass http:127.0.0.1:9998
    }
    複製代碼
  • 若不帶任何符號,則表示在正則匹配以後進行前綴匹配。

匹配順序

匹配順序以下(圖源):

(location `=` ) 
(location `完整路徑` )  
(location `^~` 路徑)  
(location `~`,`~*` 正則順序) 
(location 部分起始路徑) 
(location `/`)
複製代碼

注意如下點:

  1. 前綴匹配遵循最長匹配原則。

  2. 在進行正則匹配(~~*)時,配置的proxy_pass不容許包含uri。不然會報錯:

    "proxy_pass" cannot have URI part in location given by regular expression
    複製代碼

有關更多的location匹配細則參考這裏

反向代理一個Tomcat服務器

根據配置反向代理,咱們主要想達到如下的效果:

打開瀏覽器,輸入www.nginxtest.com訪問咱們的Nginx服務器,Nginx服務器將請求轉發到虛擬機系統下的Tomcat服務器。

首先,進入到物理機(筆者的物理機是Window10系統)的C:\Windows\System32\drivers\etc目錄下,修改hosts文件,在文件末尾追加如下行:

{虛擬機ip地址} www.nginxtest.com
複製代碼

隨後在瀏覽器中直接輸入www.nginxtest.com,默認訪問80端口,便可訪問到虛擬機中啓動的Nginx服務。

筆者在這裏使用Docker啓動一個tomcat容器,端口映射爲0.0.0.0:10000->8080。如今要訪問Tomcat服務,咱們須要輸入:

www.nginxtest.com:10000/
複製代碼

咱們但願僅輸入www.nginxtest.com便可訪問到Tomcat服務器,所以須要配置Nginx的反向代理服務。

咱們打開Nginx配置文件,在server域中添加下面的location配置,表示咱們將:

www.nginxtest.com/helloworld映射到127.0.0.1:10000/helloworld。(tomcat會默認返回/helloworld/index.jsp

location /helloworld {
	proxy_pass http://127.0.0.1:10000;
}
複製代碼

隨後刷新Nginx服務器:

$ nginx -s reload
複製代碼

咱們在瀏覽器輸入www.nginxtest.com時,就可讓Nginx經過反向代理映射到虛擬機實際運行的Tomcat服務器了。

根據URI匹配不一樣的Tomcat服務器

經過配置反向代理,咱們但願實現如下效果:

www.nginxtest.com/hi/* -> 127.0.0.1:10000/hi/*

www.nginxtest.com/hello/* ->127.0.0.1:9998/hello/*
複製代碼

當路徑爲/hi/..時,將此請求轉發到127.0.0.1:10000的對應的/hi/..下的資源;當路徑爲/hello/..時,將此請求轉發到127.0.0.1:10000的對應的/hello/..下的資源;

筆者在這裏利用Docker的Tomcat鏡像生成了一個新的實例,端口映射爲0.0.0.0:9998->8080

咱們打開default.conf,進行以下配置:

location ~ /hi/ {
	proxy_pass http://127.0.0.1:10000;
}

location ~ /hello/ {
	proxy_pass http://127.0.0.1:9998;
}
複製代碼

注意,此次的配置中多了一個~符號,表示正則匹配。這個配置告訴Nginx:全部包含/hi/的請求uri都會轉發到10000端口的Tomcat服務器;全部包含/hello/的uri都會轉發到9998端口的Tomcat服務器。注意每一個配置項後面都要帶上;符號。

配置負載均衡

咱們想要實現一個這樣的效果:Tomcat1(10000端口)和Tomcat2(9998端口)都具有/helloworld/b.html資源(爲了觀察到效果,能夠在b.html中作下區分標記)。當客戶端不斷請求此資源的uri時,但願Nginx可以根據相應策略做負載均衡。

最基本的配置

打開/etc/nginx/nginx.conf主配置文件的http塊下,配置動態服務器組:

#upstream 後面加上本身配置的服務器組名字。
upstream dynamic {
    server localhost:10000; # docker->tomcat
    server localhost:9998; # docker->tomcat
}
複製代碼

保存退出,進入到/etc/nginx/conf.d/default.confserver塊下,在proxy_pass項中配置服務器組名。

location = /helloworld/b.html {
	proxy_pass http://dynamic;
}
複製代碼

這個配置是一個精確匹配。表示當訪問/helloworld/b.html這個資源時,將由Nginx根據負載均衡策略決定訪問Tomcat1,或者是Tomcat2。

經過nginx -s reload保存配置,而後在瀏覽器中輸入:www.nginxTest/helloworld/b.html,並反覆刷新,能夠觀察到每次的內容是不同的,則證實Nginx的負載均衡策略生效了。

Nginx中的負載均衡策略

以上僅是最基本的複雜均衡配置,實際上要根據服務器的配置來採起不一樣的策略。Nginx支持6種負載均衡策略,可是有2種須要依賴第三方。在這裏詳細介紹經常使用的四種方式。

參數 方式
不作任何配置,默認輪詢方式。
weight 基於輪詢方式,配置server配置項的後面表示權重。
ip_hash 依據ip分配方式,解決session跨域問題。
least_conn 最少鏈接方式
fair(第三方) 響應時間方式
url_hash(第三方) 依據URL分配方式

輪詢(默認)方式

輪詢是upstream默認的負載均衡策略,請求時按照時間順序分配到各服務器。輪詢策略可配置如下參數:

參數 做用
fail_timeout 設置超時時間,與max_fails結合使用。
max_fails 設置在fail_timeout參數設置的時間內最大失敗次數,若是在這個時間內,全部針對該服務器的請求都失敗了,那麼認爲該服務器會被認爲是停機(down)了。
fail_time 服務器會被認爲停機的時間長度,默認爲10s。
backup 標記該服務器爲備用服務器。當主服務器中止時,請求會被髮送到它這裏。
down 標記服務器永久停機了。

輪詢策略適用於各個服務器之間配置至關,無狀態(不須要coookie,session)的服務。

權重方式

在不作任何配置的狀況下,每一個server的權重默認均爲1。

權重越大的server,承擔流量的機率也會越高。經常使用於服務器配置差距較大的狀況。咱們會讓性能更好的服務器來承受更大的流量。

#upstream 後面加上本身配置的服務器組名字。
upstream dynamic {

	# 假定此服務器性能更好,則上調它的訪問權重。
    server localhost:10000 weight=2; 
    
    # 若是20秒內有10個請求都失敗了,則該server會被停用。
    server localhost:9998 max_fails=10 fail_timeout=20; 
}
複製代碼

ip_hash方式

session用於記錄客戶<->服務器的一個會話,所以有狀態的服務不可以直接跨服務器。咱們使用ip_hash來基於客戶端的ip進行分配,來保證同一個客戶的請求只會發送到指定的服務器,來保證session會話狀態。

#upstream 後面加上本身配置的服務器組名字。
upstream dynamic {
	#每一個訪客分配到固定的服務器中。
	ip_hash; 
	
	# ip_hash方式能夠基於輪詢一塊兒使用。
    server localhost:10000 weight=2; 
    server localhost:9998 max_fails=10 fail_timeout=20; 
}
複製代碼

注意如下幾點:

  • 若是使用ip_hash,則不能再分配備用服務器backup(合情合理,由於即便備用的服務器也沒有記錄其它服務器的session信息,沒有意義)。

  • 在Nginx版本1.3.1以前,不能在ip_hash策略中配置權重(weight)。

  • 當有服務器須要剔除,必須手動down掉。

least_conn方式

把請求轉發給鏈接數較少的後端服務器。輪詢算法是把請求平均的轉發給各個後端,使它們的負載大體相同;可是,有些請求佔用的時間很長,會致使其所在的後端負載較高。這種狀況下,least_conn這種方式就能夠達到更好的負載均衡效果。

#upstream 後面加上本身配置的服務器組名字。
upstream dynamic {
	#基於鏈接數分配。
	least_conn
	
    server localhost:10000 weight=2; 
    server localhost:9998 max_fails=10 fail_timeout=20; 
}
複製代碼

適用於不一樣請求的處理時間差別較明顯的狀況。

配置動靜分離

動靜分離,就是將靜態資源(圖片,視頻,css,js等文件)和動態資源(jsp,php)區別開。動態分離並非爲了單純的將這兩種資源區別開,而是使用Nginx處理靜態資源,由Tomcat(或其它服務器)處理動態資源。

除此以外,經過配置反向代理和expires參數,咱們能夠將一些不會常常變更的靜態資源直接發送到對方瀏覽器中保存起來。

用戶下次經過瀏覽器再訪問此資源時,瀏覽器只須要作一件事:在必定時間內(取決於expires),瀏覽器向原資源服務器發送簡單請求,僅對比最後修改時間來判斷此靜態資源是否發生變化。

服務器正常狀況下會發送兩種狀態碼:

  1. 304 => 文件沒有變更,瀏覽器直接使用緩存的資源。
  2. 200 => 文件發生變更,瀏覽器成功下載到新的文件。

目前的主流作法是,將全部的靜態文件單獨分離出來保存到一個單獨的域名中。也有部分開發者選擇將動態文件和靜態文件混合在一塊兒發佈。

動靜分離的配置

在動靜分離以前,筆者先在虛擬機的/usr/images/目錄下存放一些.jpg,.png等格式的圖像文件,並配置本地做爲靜態資源的倉庫。

配置方式一:前綴匹配

location ^~ /images {
	root /usr;
	autoindex on;
}
複製代碼

因爲咱們直接使用本地文件夾,所以在這裏配置的是root。所以全部前綴知足/images/...的資源都會映射到本機的/usr/iamges/...路徑下。

當配置autoindex on;時,咱們能夠在瀏覽器中輸入www.nginxTest.com/images/直接查看此路徑下全部的文件。爲了不中文文件顯示亂碼,建議在server塊中配置charset utf-8

配置方式二:正則匹配

另外一種思路是碰到全部後綴名爲.jpg, .JPG, .png, .PNG等文件的uri時,使用本機的靜態資源庫。因爲這裏不肯定前綴名,這裏將autoindex on;選項去掉。

location ~* \.(jpg|png|gif|jepg)$ {
	root /usr;
}
複製代碼

若是靜態資源存放在另外一個服務器,一樣可使用proxy_pass進行反向代理。

設置expires緩存

設置expires比較簡單,咱們在location內部直接配置便可。語法以下:

expires 30s;   #緩存30秒
expires 30m;   #緩存30分鐘 
expires 2h;    #緩存2小時
expires 30d;   #緩存30天
複製代碼

咱們直接在剛纔的正則匹配中加入這段配置,刷新Nginx,在瀏覽器中輸入uri,並查看response header

location ~* \.(jpg|png|gif|jepg)$ {
	#緩存1天
	expires 1d
	root /usr;
}
複製代碼

筆者經過Postman工具查看,並標註了重點部分。

局域網*下搭建高可用集羣

筆者出於學習目的,僅演示虛擬機環境下的局域網搭建一個簡單的高可用集羣(HA),而在實際部署環境中,咱們至少要準備兩臺雲服務器搭建VPC,還須要另購一個公網IP用做VIP(Virtual IP)。

在學習了Nginx以後,咱們可以繪出這樣的拓撲結構了:經過動靜分離將靜態資源單獨保管到資源服務器中;經過負載均衡使不一樣的服務器之間可以協同工做......尤爲是筆者剛剛學會使用Docker快速部署服務,感受技能樹一下點亮了很多。

如圖展現了一個徹底是圍繞着一個Nginx服務器構建的星型拓撲結構,這要咱們必須面臨一個fatal的問題:若是Nginx自己宕機了,那麼全部的請求就所有失效了。因此很明顯,當前這種作法,是 存在風險的。

高可用模式的配置

爲了不將全部的雞蛋放到一個籃子裏,最直觀,最顯著的方法就是:準備一個(或多個)Nginx的Backup(後備服務器),當Master因意外宕機的時候,Backup可以快速代替Master進行工做。

咱們須要使用一款keepalived來實現Nginx的主備模式。同時還對用戶隱藏細節,使用戶可以按統一的接口來訪問

在配置高可用模式以前

咱們首先羅列一下配置高可用模式所須要的清單:

  1. 兩臺服務器(筆者使用兩臺虛擬機代替)
  2. 使用keepalived軟件。
  3. 供keepalived進行檢測目標Nginx是否運行的可執行腳本。
  4. 須要一個虛擬IP地址(用於對用戶提供統一的訪問)。
  5. 禁用SElinux,清除iptables規則,關閉防火牆

keepalived檢測Nginx的流程

keepalived集羣內部有多個Nginx服務器,在這個集羣運做時,其中只有一個Nginx是處於工做狀態,即Master(主機),其他的Nginx都處於Backup(候選)狀態。每一個主機內的keepalived監視本機的Nginx運行的狀態。

不過不管是Master仍是Backup在進行反向代理工做,對客戶來講是不可見的。這主要經過提供一個統一的VIP(Virtual IP)來實現。因此不管是誰在運做,客戶都只經過這一個VIP來請求服務。

keepalived基於arp廣播模式工做,所以全部的keepalived處於一個局域網當中。(若是是多個雲服務器互備則要配置在同一個虛擬私有云VPC內)

每一個主機的keepalived和只本機的Nginx綁定。keepalived會經過一個腳原本檢測本地的Nginx是否處於正常狀態(或者稱進行健康檢查)。當keepalived檢測到Nginx處於不可重啓的狀態中,則會將自己的keepalived進程一同中斷(表示本機的"Nginx was died")。

集羣中各個keepalived會進行心跳檢測(經過組播形式實現)。其它的keepalived"感知"不到Master的keepalived(說明Master Nginx宕機了)時,便會從Backup中選出一個候補獲取VIP的使用權,以後客戶輸入的路徑將所有由此候選的Nginx負責。

對於實際工做的Nginx節點,咱們是能夠經過ip addr檢查到網口是綁定了VIP的。而處於候選狀態的節點,ip addr是沒有綁定VIP的。

keepalived的配置能夠有兩種形式:

  1. 搶佔式(默認),主節點出現故障以後,由備機工做。當主節點從新啓動時,備機將從新處於候選狀態。
  2. 非搶佔式,主節點出現故障以後,由備機工做。當主節點從新啓動時,備機繼續工做。

配置另外一個虛擬機的Nginx

咱們以前都是在主機"hadoop102"中配置的Nginx反向代理/負載均衡/動靜分離,因此在hadoop102的Nginx中,有不少配置都指向"localhost","127.0.0.1"。

而對於新的主機"hadoop101"而言,這些配置就要相應地替換成主機hadoop102或者它的ip地址了。咱們直接拷貝以前hadoop102主機的配置文件,修正nginx.confdefault.conf的一些細節:

upstream dynamic{
    #因爲此次咱們須要通信,因此要配置max_fails和fail_timeout。
	server hadoop102:10000 max_fails=10 fail_timeout=10s;
	server hadoop102:9998 max_fails=10 fail_timeout=10s;
}	
複製代碼

default.conf中作如下修改,靜態資源的目錄再也不位於本機,因此須要proxy_pass進行代理。

location ~* \.(jpg|png|gif|jepg)$ {
	expires 1d;
	proxy_pass http://hadoop102;
}
複製代碼

正常狀況下,靜態資源服務器是會單獨在另外一個服務器中的,不過這裏的hadoop102的Nginx既做爲資源服務器,又做爲keepalived集羣的備用機。

在MASTER工做的狀況下,處於BACKUP狀態的Nginx也仍然正常工做。

安裝keepalived

注意!!請謹慎閱讀如下內容的每一處細節,由於稍有不慎就會致使keepalived沒法正常運行。

其中keepalived有多種渠道安裝,也能夠直接使用yum源進行安裝。兩個服務器都須要安裝此軟件。

$ yum install keepalived -y
複製代碼

咱們可使用find命令來查找到keepalived配置文件所在的目錄(實際它在/etc/keepalived/目錄下):

$ find / | grep keepalived.conf 
複製代碼

除此以外,另一個虛擬機也須要安裝一個Nginx服務器,過程參見上文。

編寫檢測腳本

筆者在健康檢測腳本中的設定是:keepalived在Nginx處於關閉狀態時仍然會嘗試再次開啓Nginx服務,直到2秒後發現Nginx仍沒法啓動時纔會斷定爲dead.

keepalived使用腳原本實現檢測目標Nginx是否正常運做。當腳本檢測到Master Nginx宕機時,要切換到Backup Nginx上。

#!/bin/bash
A=`ps -C nginx --no-header |wc -l`
if [ $A -eq 0 ];then
    systemctl start nginx
    sleep 2
    if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then
        killall keepalived
    fi
fi
複製代碼

❗編寫腳本注意的問題:

  1. 命令模式輸入set ff檢查文件的編碼格式。若是顯示文件的編碼格式爲doc,則須要更改成set off=unix

  2. 這個腳本須要被設置爲具有x權限,可使用chmod a+x進行更改。

筆者將這個文件保存爲/usr/local/src/nginx_check.sh。最好手動執行一遍這個腳原本排查問題,以避免稍後的keepalived不生效。

配置keepalived【謹慎配置】

keepalived的配置文件爲/etc/keepalived/keepalive.conf默認狀況下有很是多的默認配置。

從這個實例來講,咱們只須要用到如下三個部分global_defs(全局定義),vrrp_script(用於檢測腳本,須要手動添加),vrrp_instance(配置虛擬IP)。

對於用不上,或者不清楚其功能的配置項,強烈建議所有刪去,不然很是容易致使keepalived不生效。

咱們首先對主節點Master爲例進行配置:

在此,咱們不須要經過Mail的形式收發消息。所以在global_defs部分中,咱們只須要保留router_id項,來爲爲本身的主機命名。

# 配置此主機的主機名。至關於在配置/etc/hosts
global_defs {
   router_id r1
}
複製代碼

原配置文件中沒有vrrp_script選項,須要咱們手動添加上去。script的路徑是剛纔建立腳本的絕對路徑。可能會有差別,請仔細甄別。

# vrrp_script後面的名稱能夠自定義(此處爲check),可是要和稍後track_script塊配置對應。
vrrp_script check {
   script  "/usr/local/src/nginx_check.sh"
   interval 4
}
複製代碼

❗注意,這個interval的數值要比腳本中的睡眠時間sleep要大,不然腳本會執行失敗。(筆者的腳本中,睡眠時間爲2,所以在這裏配置爲了4)

其中,在vrrp_instance中咱們須要進行5處更改,筆者已經經過註釋的方式給出。實際的更改值要取決於本身的網絡環境,好比VIP的選擇。在虛擬機環境下,咱們只須要選任意一個沒有被佔用的內網IP便可。而若是是雲服務器構建的HA集羣,要向服務提供商(騰訊雲,華爲雲)另行購買一個HAVIP

筆者的物理機和宿主機集羣經過NAT方式鏈接,並處在192.168.229.0/24網絡。宿主機的系統均爲CentOS 7系統,所以網卡口名稱均爲ens33。這些都取決於我的配置,不可直接粘貼。

vrrp_instance VI_1 {
    #1 可設置BACKUP和MASTER,取決於設置。
    state MASTER
    #2 配置成本身的網卡口!centOS7通常都叫ens33.
    # 可使用ip addr來查看。
    interface ens33
    
    # 注意,同一個集羣下的virtual_router_id必須保持一致!
    virtual_router_id 51
    
    #3 保證BACKUP的優先級要比MASTER的優先級低。
    priority 100
    
    # 設置每1秒組播心跳消息,證實keepalived還存活。
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    #4 這一塊須要本身手動添加。注意要寫到virtual_ipaddress項的上面!
    # 內部的腳本名稱實際上取決於你剛纔在vrrp_script的配置名稱
    track_script {
        check
    }
    
    #5 不管是BACKUP/MASTER,爲了讓用戶經過統一域名訪問,所以都要選擇統一的VIP。
    # 這個IP根據你的局域網IP號而定。
    virtual_ipaddress {
        192.168.229.171
    }
}
複製代碼

對於配置爲BACKUP的主機,只須要把state一項修改便可。

啓動keepalived集羣

在確保以上的配置就緒以後,咱們經過systemctl命令啓動nginxkeepalived

$ systemctl start nginx
$ systemctl start keepalived.service
複製代碼

檢查Nginx和keepalived服務是否正常啓動:

$ systemctl status nginx
$ systemctl status keepalived.service
複製代碼

在集羣啓動後,正在處於工做狀態的服務器是能夠經過ip addr檢查到ens33網口綁定了VIP的。其它正在運行但處於候選狀態的Nginx主機則沒有綁定VIP。

若是不慎出了錯誤!

必定要去日誌中尋找線索,獲取在瀏覽器端開啓調試模式檢查錯誤碼。

#nginx錯誤日誌默認位置
cat /var/log/nginx/error.log
複製代碼

筆者遇到並解決了如下錯誤:

(13: Permission denied) while connecting to upstream
複製代碼

解決方案:多是因爲selinux防火牆開啓引起的問題,檢查防火牆狀態:

$ sestatus -v
複製代碼

這裏筆者姑且採用直接的方案:關閉selinux防火牆。

# SELINUX = disabled
$ vim /etc/selinux/config
# 重啓生效
$ reboot
複製代碼

另外,因爲咱們對upstream的每一個節點都設置了超時時間,所以若是由於高併發(或其它緣由)致使全部節點所有處於down狀態的話,日誌文件會打印如下錯誤:

...no live upstreams while connecting to upstream...
複製代碼

它表示沒有一個存活的上游節點,但它並非根本問題。咱們須要繼續跟蹤日誌,追查是什麼緣由致使全部的節點所有down掉。此刻在瀏覽器端,咱們能夠在調試模式下檢測到502錯誤。

公網環境下的高可用方案

阿里雲:阿里雲服務器再也不支持單獨購買IP,所以在公網環境下不能經過keepalived的方式進行熱備。不過阿里雲彷佛直接提供負載均衡服務?(要恰飯的嘛)

騰訊雲和華爲雲的實現原理相似,須要爲多個雲服務器建立一個虛擬私有云VPC,而後再另行購買用於浮動的HAVIP,進行綁定。

不一樣廠商間提供的解決方案稍有區別,所以要根據本身以後的實際狀況查找具體方案。筆者目前沒有在公網搭建HA的強烈需求(主要是窮),所以暫時不會再深刻研究公網環境下的HA搭建了(老實說,筆者認爲這些應該是運維的工做......)。筆者在此羅列一些帖子供往後參考:

Nginx補充

Master與Worker

咱們使用ps命令查看進程時,能夠留意到:Nginx服務實際上有兩個進程:

$ ps -ef | grep nginx
#root 2016 1 0 10:49 ? 00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
#nginx 2017 2016 0 10:49 ? 00:00:00 nginx: worker process
複製代碼

worker進程的數量實際上能夠有多個的。正是由咱們當時在/etc/nginx/nginx.conf中配置的worker_process數量所決定的(默認爲1,實際數量取決於機器性能)。當外部訪問到Nginx服務器的時候,worker進程之間經過競爭的方式來獲取此鏈接。

這種一個Master管理多個Worker的優點是什麼呢?

  1. 基於這種模式,咱們可使用nginx -s reload對Nginx進行熱部署:它是使得正在處理請求的worker不會被打斷已有的工做,而空閒的worker則馬上刷新了配置
  2. 每一個worker是獨立的進程(非線程)。所以節省了各類同步鎖形成的開銷。此外,即使是個別worker因異常而關閉,其它worker仍然可以正常運行,提升了Nginx總體的穩定性,下降了風險。

Nginx 的高性能依賴於 Linux 2.6 內核的 epoll 或是 BSD 內核的 kqueue 提供高效的網絡套接字狀態輪詢服務【時間複雜度爲 O(1) 】。在沒有這兩個服務的內核上則退化成爲性能低下的 select 【*nix ,Windows 都有,時間複雜度爲 O(n) 】. Windows 沒有 epoll 和 kqueue,Nginx 在 Windows 上用 select 表現天然不佳。

另外一個NoSQL數據庫軟件 Redis 也是一樣的緣由,致使它在Windows運行的性能和*nix平臺相比要低。

咱們應該設置多少個Worker數量最合適呢?通常狀況下,咱們一般將這個數值設置爲和物理機的CPU數量相等。

worker_connection

首先提一個問題:基於Http 1.1協議,一個用戶發送一個請求到一個Worker中,會佔用Worker的幾個鏈接數呢?答案是2個或者4個。

當訪問靜態資源,Nginx直接從本地返回資源時,須要佔用兩個鏈接。

當Nginx進行反向代理的時候,則須要佔用4個鏈接。

所以假設每一個worker的鏈接數值爲worker_connection,則實際的併發訪問量(指同時處理Client的請求數)是:

  1. (worker_connection * worker_process) / 2 => 做爲靜態服務器

  2. (worker_connection * worker_process) / 4 => 做爲反向代理服務器

相關文章
相關標籤/搜索