LVS+Nginx實現高可用集羣
- 常見服務器
- 安裝 Nginx(CentOS)
- Nginx 的進程模型
- Nginx 事件處理
- nginx.conf 核心配置文件
- Nginx 的跨域配置和防盜鏈
- 負載均衡和集羣配置
- Nginx集羣配置
- Jmeter
- 負載均衡 – 輪詢&權重
- upstream指令參數
- 負載均衡 – ip_hash
- 一致性哈希算法
- 負載均衡 – url_hash,least_conn
- 緩存
- Nginx配置SSL(HTTPS)
- 動靜分離
- Nginx高可用HA
- Keepalived 雙主熱備
- LVS負載均衡
- LVS-DR模式的其它配置
- Keepalived+LVS高可用
Redis緩存雪崩,穿透
LVS+Nginx實現高可用集羣
Nginx(engine x)是一個高性能的 HTTP 和反向代理web 服務器,同時也提供 IMAP/POP3/SMTP服務。
- 反向代理
- 通過配置文件實現集羣和負載均衡
- 靜態資源虛擬化
常見服務器
- MS IIS asp.net
- Weblogic、Jboss 傳統行業 ERP/物流/金融/電信
- Tomcat、Jetty J2EE
- Apache、Nginx 靜態服務、反向代理
- Netty 高性能服務器編程
來源:Netcraft
安裝 Nginx(CentOS)
# 安裝依賴 yum install -y gcc-c++ # 安裝 gcc 環境 yum install -y pcre pcre-devel # 安裝 PRCE 庫,用於解析正則表達式 yum install -y zlib zlib-devel yum install -y openssl openssl-devel # https協議 wget http://nginx.org/download/nginx-1.16.1.tar.gz # 下載穩定版 tar -zxvf nginx-1.16.1.tar.gz # 解壓 mkdir -p /var/temp/nginx # 創建臨時目錄 cd nginx-1.16.1/ ./configure --prefix=/usr/local/nginx --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_gzip_static_module --http-client-body-temp-path=/var/temp/nginx/client --http-proxy-temp-path=/var/temp/nginx/proxy --http-fastcgi-temp-path=/var/temp/nginx/fastcgi --http-uwsgi-temp-path=/var/temp/nginx/uwsgi --http-scgi-temp-path=/var/temp/nginx/scgi --with-http_ssl_module make && make install cd /usr/local/nginx/sbin/ # conf 等目錄均位於/usr/local/nginx/中 ./nginx # 啓動 ./nginx -s stop # 直接停止 ./nginx -s quit # 優雅停止 ./nginx -s reload # 重新加載 ./nginx -t # 檢測配置文件 ./nginx -v # 查看 Nginx 版本 ./nginx -V # 查看版本及編譯信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# 安裝依賴 yum install -y gcc-c++ # 安裝 gcc 環境 yum install -y pcre pcre-devel # 安裝 PRCE 庫,用於解析正則表達式 yum install -y zlib zlib-devel yum install -y openssl openssl-devel # https協議 wget http://nginx.org/download/nginx-1.16.1.tar.gz # 下載穩定版 tar -zxvf nginx-1.16.1.tar.gz # 解壓 mkdir -p /var/temp/nginx # 創建臨時目錄 cd nginx-1.16.1/ ./configure --prefix=/usr/local/nginx --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_gzip_static_module --http-client-body-temp-path=/var/temp/nginx/client --http-proxy-temp-path=/var/temp/nginx/proxy --http-fastcgi-temp-path=/var/temp/nginx/fastcgi --http-uwsgi-temp-path=/var/temp/nginx/uwsgi --http-scgi-temp-path=/var/temp/nginx/scgi --with-http_ssl_module make && make install cd /usr/local/nginx/sbin/ # conf 等目錄均位於/usr/local/nginx/中 ./nginx # 啓動 ./nginx -s stop # 直接停止 ./nginx -s quit # 優雅停止 ./nginx -s reload # 重新加載 ./nginx -t # 檢測配置文件 ./nginx -v # 查看 Nginx 版本 ./nginx -V # 查看版本及編譯信息 |
命令 | 解釋 |
---|---|
–prefix | 指定nginx安裝目錄 |
–pid-path | 指向nginx的pid |
–lock-path | 鎖定安裝文件,防止被惡意篡改或誤操作 |
–error-log | 錯誤日誌 |
–http-log-path | http日誌 |
–with-http_gzip_static_module | 啓用gzip模塊,在線實時壓縮輸出數據流 |
–http-client-body-temp-path | 設定客戶端請求的臨時目錄 |
–http-proxy-temp-path | 設定http代理臨時目錄 |
–http-fastcgi-temp-path | 設定fastcgi臨時目錄 |
–http-uwsgi-temp-path | 設定uwsgi臨時目錄 |
–http-scgi-temp-path | 設定scgi臨時目錄 |
Nginx 的進程模型
- master 進程:主進程
- worker 進程:工作進程
ps -ef|grep nginx # 查看 Nginx 進程 # 修改 worker 進程數 vi conf/nginx.conf worker_processes 2;
1 2 3 4 |
ps -ef|grep nginx # 查看 Nginx 進程 # 修改 worker 進程數 vi conf/nginx.conf worker_processes 2; |
Nginx 事件處理
# vi conf/nginx.conf # 設置工作模式 events { # 默認使用 epoll,因此可省略 use epoll; # 每個 worker 允許連接客戶端最大連接數,可根據實際情況進行修改 worker_connections 10240; }
1 2 3 4 5 6 7 8 |
# vi conf/nginx.conf # 設置工作模式 events { # 默認使用 epoll,因此可省略 use epoll; # 每個 worker 允許連接客戶端最大連接數,可根據實際情況進行修改 worker_connections 10240; } |
使用了多路徑複用器,在出現了阻塞時一個 worker 可以處理多個客戶端請求
nginx.conf 配置結構
main 全局配置 events 配置工作模式及連接數 http http 模塊相關配置 |_server 虛擬主機配置,可以有多個 |_location 路由規則,表達式 |_upstream 集羣,內網服務器
1 2 3 4 5 6 |
main 全局配置 events 配置工作模式及連接數 http http 模塊相關配置 |_server 虛擬主機配置,可以有多個 |_location 路由規則,表達式 |_upstream 集羣,內網服務器 |
BIO:同步阻塞
NIO:同步非阻塞
AIO:異步非阻塞
nginx.conf 核心配置文件
user root; # worker進程的用戶,默認爲 nobody worker_processes 1; # 工作進程數,通常同 CPU 數量或 N-1 # error_log日誌級別 debug|info|notice|warn|error|crit|alert|emerg,級別從左到右越來越大 pid logs/nginx.pid; # Nginx 進程 pid # http 指令塊,對 http 網絡傳輸的一些指令配置 http { } include mime.types; # include 引入外部配置,提高可讀性,避免單個配置文件過大 # 日誌格式中main爲格式名稱,供access_log等調用,日誌中各項請見下表 # sendfile 使用高效文件傳輸,提升傳輸性能;啓用後才能使用tcp_nopush,是指當數據表累積一定大小後才發送,提高了效率 sendfile on; tcp_nopush on; # keepalive_timeout設置客戶端與服務端請求的超時時間,保證客戶端多次請求的時候不會重複建立新的連接,節約資源損耗 keepalive_timeout 65;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
user root; # worker進程的用戶,默認爲 nobody worker_processes 1; # 工作進程數,通常同 CPU 數量或 N-1 # error_log日誌級別 debug|info|notice|warn|error|crit|alert|emerg,級別從左到右越來越大 pid logs/nginx.pid; # Nginx 進程 pid # http 指令塊,對 http 網絡傳輸的一些指令配置 http { }
include mime.types; # include 引入外部配置,提高可讀性,避免單個配置文件過大 # 日誌格式中main爲格式名稱,供access_log等調用,日誌中各項請見下表 # sendfile 使用高效文件傳輸,提升傳輸性能;啓用後才能使用tcp_nopush,是指當數據表累積一定大小後才發送,提高了效率 sendfile on; tcp_nopush on; # keepalive_timeout設置客戶端與服務端請求的超時時間,保證客戶端多次請求的時候不會重複建立新的連接,節約資源損耗 keepalive_timeout 65; |
參數名 | 參數意義 |
---|---|
$remote_addr | 客戶端ip |
$remote_user | 遠程客戶端用戶名,一般爲:’-’ |
$time_local | 時間和時區 |
$request | 請求的url以及method |
$status | 響應狀態碼 |
$body_bytes_send | 響應客戶端內容字節數 |
$http_referer | 記錄用戶從哪個鏈接跳轉過來的 |
$http_user_agent | 用戶所使用的代理,一般來說都是瀏覽器 |
$http_x_forwarded_for | 通過代理服務器來記錄客戶端的ip |
# 日誌切割腳本 # vi cut_my_log.sh #!/bin/bash LOG_PATH="/var/log/nginx/" RECORD_TIME=$(date -d "yesterday" +%Y-%m-%d) PID=/var/run/nginx/nginx.pid mv ${LOG_PATH}/access.log ${LOG_PATH}/access.${RECORD_TIME}.log mv ${LOG_PATH}/error.log ${LOG_PATH}/error.${RECORD_TIME}.log #向Nginx主進程發送信號,用於重新打開日誌文件 kill -USR1 `cat $PID` # 手動切割 chmod u+x cut_my_log.sh ./cut_my_log.sh # 自動切割可通過定時任務,如未安裝yum install -y crontabs crontab -e * * */1 * * /usr/local/nginx/sbin/cut_my_log.sh # crontab -l 查看 service crond restart # 重啓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# 日誌切割腳本 # vi cut_my_log.sh
#!/bin/bash LOG_PATH="/var/log/nginx/" RECORD_TIME=$(date -d "yesterday" +%Y-%m-%d) PID=/var/run/nginx/nginx.pid mv ${LOG_PATH}/access.log ${LOG_PATH}/access.${RECORD_TIME}.log mv ${LOG_PATH}/error.log ${LOG_PATH}/error.${RECORD_TIME}.log #向Nginx主進程發送信號,用於重新打開日誌文件 kill -USR1 `cat $PID`
# 手動切割 chmod u+x cut_my_log.sh ./cut_my_log.sh # 自動切割可通過定時任務,如未安裝yum install -y crontabs crontab -e * * */1 * * /usr/local/nginx/sbin/cut_my_log.sh # crontab -l 查看 service crond restart # 重啓 |
靜態文件兩種配置方式(其中 xxx 爲服務器中/home 下的靜態文件目錄名):
server { .. location /xxx{ root /home; } location /static{ alias /home/xxx; } }
1 2 3 4 5 6 7 8 9 10 |
server { .. location /xxx{ root /home; }
location /static{ alias /home/xxx; } } |
gzip 壓縮配置示例
#開啓 gzip 壓縮功能以提高傳輸效率、節約帶寬 gzip on; #限制最小壓縮,小於1字節文件不進行壓縮 gzip_min_length 1; #壓縮比,級別爲1-9,級別越高相應CPU 佔用也越多 gzip_comp_level 3; #壓縮文件類型 gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/json;
1 2 3 4 5 6 7 8 |
#開啓 gzip 壓縮功能以提高傳輸效率、節約帶寬 gzip on; #限制最小壓縮,小於1字節文件不進行壓縮 gzip_min_length 1; #壓縮比,級別爲1-9,級別越高相應CPU 佔用也越多 gzip_comp_level 3; #壓縮文件類型 gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/json; |
location的匹配規則
空格:默認匹配、普通匹配 location / { root /home; } =:精確匹配 location = /xxx.png { root /home; } ~*:匹配正則表達式,不區分大小寫 location ~* \.(GIF|jpg|png|jpeg) { root /home; } ~:匹配正則表達式,區分大小寫 location ~ \.(GIF|jpg|png|jpeg) { root /home; } ^~:以某個字符路徑開頭 location ^~ /xxx/img { root /home; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
空格:默認匹配、普通匹配 location / { root /home; } =:精確匹配 location = /xxx.png { root /home; } ~*:匹配正則表達式,不區分大小寫 location ~* \.(GIF|jpg|png|jpeg) { root /home; } ~:匹配正則表達式,區分大小寫 location ~ \.(GIF|jpg|png|jpeg) { root /home; } ^~:以某個字符路徑開頭 location ^~ /xxx/img { root /home; } |
Hosts 文件管理工具(兼容多平臺):SwitchHosts,通過它可避免手動去編輯系統 hosts 文件
Nginx 的跨域
CORS(Cross-Origin Resource Sharing)
解決跨域的方案Jsonp, SpingBoot Cors, Nginx
# SpringBoot CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("http://www.xxx.com"); # 設置是否發送 cookie 信息 config.addAllowCredentials(true); # 設置允許請求的方式 config.addAllowedMethod("*"); #設置允許的 header config.addAllowedHeader("*"); # Nginx server{ ... # 允許跨域請求的域,*代表所有 add_header 'Access-Control-Allow-Origin' *; # 允許帶上 cookie 請求 add_header 'Access-Control-Allow-Credentials' 'true'; # 允許請求的方法,比如 GET/POST/PUT/DELETE add_header 'Access-Control-Allow-Methods' *; # 允許請求的 header add_header 'Access-Control-Allow-Headers' *; ... }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# SpringBoot CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("http://www.xxx.com"); # 設置是否發送 cookie 信息 config.addAllowCredentials(true); # 設置允許請求的方式 config.addAllowedMethod("*"); #設置允許的 header config.addAllowedHeader("*");
# Nginx server{ ... # 允許跨域請求的域,*代表所有 add_header 'Access-Control-Allow-Origin' *; # 允許帶上 cookie 請求 add_header 'Access-Control-Allow-Credentials' 'true'; # 允許請求的方法,比如 GET/POST/PUT/DELETE add_header 'Access-Control-Allow-Methods' *; # 允許請求的 header add_header 'Access-Control-Allow-Headers' *; ... } |
Nginx靜態資源防盜鏈
server{ .. # 對源站點驗證 valid_referers *.xxx.com; # 非法引入會進入下方判斷 if($invalid_referer) { return 404; } ... }
1 2 3 4 5 6 7 8 9 10 |
server{ .. # 對源站點驗證 valid_referers *.xxx.com; # 非法引入會進入下方判斷 if($invalid_referer) { return 404; } ... } |
負載均衡
四層負載均衡
- F5硬負載均衡(基於硬件)
- LVS四層負載均衡
- HAProxy 四層負載均衡
- Nginx 四層負載均衡
七層負載均衡
- Nginx 七層負載均衡
- HAProxy 七層負載均衡
- Apache 七層負載均衡
DNS地域負載均衡
層級 | 名稱 | 說明 |
---|---|---|
第七層 | 應用層 | 與用戶行爲交互 |
第六層 | 表示層 | 定義數據格式以及數據加密 |
第五層 | 會話層 | 創建、管理以及銷燬會話 |
第四層 | 傳輸層 | 創建、管理請求端到響應端(端到端)的連接 |
第三層 | 網絡層 | 請求端的IP地址 |
第二層 | 數據鏈路層 | 提供介質訪問與鏈路管理 |
第一層 | 物理層 | 傳輸介質,物理媒介 |
集羣配置
本地集羣可使用多臺虛擬機,或直接在單臺Linux機器上使用 Docker
docker run -d -it --rm tomcat:8.0
1 |
docker run -d -it --rm tomcat:8.0 |
我們連續啓動了3個Docker
#配置上游服務器 upstream xxx { server 172.17.0.2:8080; server 172.17.0.3:8080; server 172.17.0.4:8080; } server { listen: 80; server_name www.xxx.com; location / { proxy_pass http://xxx; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#配置上游服務器 upstream xxx { server 172.17.0.2:8080; server 172.17.0.3:8080; server 172.17.0.4:8080; }
server { listen: 80; server_name www.xxx.com; location / { proxy_pass http://xxx; } } |
Jmeter
下載鏈接:https://jmeter.apache.org/download_jmeter.cgi
下載後 Windows 雙擊 jmeter.bat,macOS雙擊 jmeter 執行文件即可打開可視化窗口
1、測試計劃>線程組:配置線程(用戶)數和循環次數
2、線程組>採樣器>HTTP 請求:配置請求的域名、端口等
3、測試計劃>監聽器:添加查看結果樹、聚合報告、用表格查看結果等
着重對比單機和集羣的異常率(聚合報告),以瞭解所能負載的用戶數,一般超過20%即超出了異常極限
負載均衡 – 輪詢、權重
負載均衡默認使用輪詢,平均逐一分配給集羣內的服務器;可通過配置來設置加權輪詢
# 加權輪詢配置(數值越小,分配的流量越少) upstream xxx { server 172.17.0.2:8080 weight=1; server 172.17.0.3:8080 weight=3; server 172.17.0.4:8080 weight=5; }
1 2 3 4 5 6 |
# 加權輪詢配置(數值越小,分配的流量越少) upstream xxx { server 172.17.0.2:8080 weight=1; server 172.17.0.3:8080 weight=3; server 172.17.0.4:8080 weight=5; } |
upstream指令參數
更多內容參見:官方文檔,其中各參數的配置方法同上述的 weight,以下設置均位於 upstream 中
- max_conns
限制一臺服務器的最大連接數,在有多個 worker 進程時因共享內存實際會超出
默認爲0,不做任何限制
通過將 worker 設置爲1,使用 Jmeter 可進行測試(線程組Ramp-Up 時間設置爲0)
示例:server 172.17.0.2:8080 max_conns=2; - slow_start
讓服務器緩慢加入集羣,配合 weight 使用,當前僅適用於商業版
示例:server 172.17.0.4:8080 weight=5 slow_start=60s; - down
標識服務器爲不可用
示例:server 172.17.0.2:8080 down; - backup
標識服務器爲備用服務器,在主服務器都掛掉時啓用
示例:server 172.17.0.2:8080 backup; - max_fails
fail_timeout所設置時間內失敗嘗試次數,默認值爲1,若達到失敗嘗試上限次數,則判定爲宕機 - fail_timeout
配合 max_fails 使用,默認爲10秒,對判定爲宕機的服務在達到 fail_timeout 的時長後會重新嘗試連接
示例:server 172.17.0.2:8080 backup max_fails=2 fail_timout=10s;
keepalive吞吐量,用於設置長連接處理的數量
示例:keepalive 32;
對於 http,還應進行如下設置(location 內):
proxy_http_version 1.1; # 設置長連接 http 版本爲1.1 proxy_set_header Connection ""; # 清除 connection header 信息
1 2 |
proxy_http_version 1.1; # 設置長連接 http 版本爲1.1 proxy_set_header Connection ""; # 清除 connection header 信息 |
負載均衡 – ip_hash
hash 算法:用戶ip哈希對服務端節點數取模獲取下標:
hash(ip) % node_counts = index
數據庫分表哈希算法同理,如將 ip 替換爲 pid
配置:
upstream xxx { ip_hash; server ... }
1 2 3 4 |
upstream xxx { ip_hash; server ... } |
nginx/src/http/modules/ngx_http_upstream_ip_hash_module.c
... iphp->addrlen = 3; ... for (i = 0; i < (ngx_uint_t) iphp->addrlen; i++) { hash = (hash * 113 + iphp->addr[i]) % 6271; } ...
1 2 3 4 5 6 7 |
... iphp->addrlen = 3; ... for (i = 0; i < (ngx_uint_t) iphp->addrlen; i++) { hash = (hash * 113 + iphp->addr[i]) % 6271; } ... |
根據以上 Nginx 的源碼文件可以分析出 ip_hash實際上取的是 IP 的前3段,因此通過內網(如hash( 192 168 1 ))將訪問同一臺主機。
注意:使用 ip_hash 如果有服務器出現故障不能直接移除,而是應將其標記爲 down
一致性哈希算法
ip_hash 在有節點宕機時節點數就會發生變化,自然所有下標也即訪問的主機也會發生變化 。這樣用戶會丟失原有的 session,緩存無效,基於這一缺點引入了一致性哈希算法。
一致性哈希算法在0到232-1之間,根據用戶和服務器節點的哈希值按順時針就近原則決定用戶所訪問的服務器節點,這樣不論是增加服務器節點還是減少服務器節點,都只有少數用戶受到影響,並且依然保持相同的原則。
負載均衡 – url_hash,least_conn
url_hash 是根據請求 url 進行哈希,然後與節點數取模得出下標
hash(url) % node_counts = index
upstream xxx { hash $request_uri; server ... }
1 2 3 4 |
upstream xxx { hash $request_uri; server ... } |
url_hash 在 url 發生變化時(如請求鏈接後多一個/)請求的服務器就會發生變化
least_conn 是最小連接數,實際含義是將請求發送到 連接數/權重 值最小的服務器上,以避免有些服務器節點出現閒置的狀況:
upstream xxx { least_conn; server ... }
1 2 3 4 |
upstream xxx { least_conn; server ... } |
緩存
- 靜態資源緩存瀏覽器
expires 指令location /static{ alias /xxx/yyy; #expires 10s; # 緩存10s後過期 expires @22h30m; # 指定緩存過期時間 # expires -1h; # 緩存1小時前失效,即不緩存 # expires epoch; # 過期時間爲1 January,1970,00:00:01 GMT,同樣不進行緩存 # expires off; # 不設置,會使用瀏覽器默認值 # expires max; # 過期時間設置爲31 December 2037 23:59:59 GMT,“Cache-Control”的值爲10年 }
1
2
3
4
5
6
7
8
9
location /static{
alias /xxx/yyy;
#expires 10s; # 緩存10s後過期
expires @22h30m; # 指定緩存過期時間
# expires -1h; # 緩存1小時前失效,即不緩存
# expires epoch; # 過期時間爲1 January,1970,00:00:01 GMT,同樣不進行緩存
# expires off; # 不設置,會使用瀏覽器默認值
# expires max; # 過期時間設置爲31 December 2037 23:59:59 GMT,“Cache-Control”的值爲10年
}
- 上游服務器資源緩存到 Nginx端
# 設置緩存保存的目錄 # keys_zone 設置共享內存及佔用的空間大小,mycache爲自定義名稱,在下方需使用 # max_size 設置緩存總大小 # inactive 緩存超出所指定的時間進行清理 proxy_cache_path /usr/local/nginx/upstream_cache keys_zone=mycache:5m max_size=1g inactive=30m use_temp_path=off; server { ... # 開啓並使用緩存,mycache爲上方所指定的名稱 proxy_cache mycache; # 針對指定狀態碼的緩存過期時間 proxy_cache_valid 200 304 8h; ... }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 設置緩存保存的目錄
# keys_zone 設置共享內存及佔用的空間大小,mycache爲自定義名稱,在下方需使用
# max_size 設置緩存總大小
# inactive 緩存超出所指定的時間進行清理
proxy_cache_path /usr/local/nginx/upstream_cache keys_zone=mycache:5m max_size=1g inactive=30m use_temp_path=off;
server {
...
# 開啓並使用緩存,mycache爲上方所指定的名稱
proxy_cache mycache;
# 針對指定狀態碼的緩存過期時間
proxy_cache_valid 200 304 8h;
...
}
Nginx配置SSL(HTTPS)
首先要確定 Nginx有沒安裝了ssl 模塊
nginx -V # 如未安裝需進入Nginx 源碼目錄重新編譯,在原命令基礎上添加--with-http_ssl_module再進行編譯安裝 ./configure --prefix=/usr/local/nginx ... --with-http_ssl_module
1 2 3 4 |
nginx -V # 如未安裝需進入Nginx 源碼目錄重新編譯,在原命令基礎上添加--with-http_ssl_module再進行編譯安裝
./configure --prefix=/usr/local/nginx ... --with-http_ssl_module |
配置示例
server { listen 443; ... # 開啓ssl ssl on; # 配置ssl證書 ssl_certificate xxx.crt; ssl_certificate_key xxx.key; # ssl會話緩存 ssl_session_cache shared:SSL:1m; # ssl會話超時時間 ssl_session_timeout 5m; # 配置加密套件,寫法遵循 openssl 標準 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; ssl_prefer_server_ciphers on; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
server { listen 443; ...
# 開啓ssl ssl on; # 配置ssl證書 ssl_certificate xxx.crt; ssl_certificate_key xxx.key;
# ssl會話緩存 ssl_session_cache shared:SSL:1m; # ssl會話超時時間 ssl_session_timeout 5m;
# 配置加密套件,寫法遵循 openssl 標準 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; ssl_prefer_server_ciphers on; } |
動靜分離
- 分佈式
- 前後端解耦
- 靜態歸 Nginx
- 接口服務化
靜態數據:css/js/html/images/audios/videos/…
動態數據:得到的響應可能會和上一次不同
實現方式
- CDN:將靜態資源放到第三方CDN 平臺
- Nginx:將靜態資源放到 Nginx 服務器或上游集羣服務器
動靜分離的問題
- 跨域
- SpringBoot
- Nginx
- Jsonp
- 分佈式會話
- 分佈式緩存中間件Redis
Nginx高可用HA
主、備 Nginx
Keepalived:
- 解決單點故障
- 組件免費
- 可以實現高可用HA機制
- 基於VRRP協議(Virtual Router Redundancy Protocol)
- 解決內網單機故障的路由協議
- 構建有多個路由器 MASTER BACKUP
- 虛擬 IP – VIP(Virtual IP Address)
Keepalived安裝
下載地址:keepalived.org
以當前版本2.0.19爲例:
tar -zxvf keepalived-2.0.19.tar.gz cd keepalived-2.0.19/ ./configure --prefix=/usr/local/keepalived --sysconf=/etc make && make install
1 2 3 4 |
tar -zxvf keepalived-2.0.19.tar.gz cd keepalived-2.0.19/ ./configure --prefix=/usr/local/keepalived --sysconf=/etc make && make install |
主要配置
# vi /etc/keepalived/keepalived.conf lobal_defs { # 路由id: 當前安裝Keepalived 節點主機的標識符,要求全局唯一 router_id LVS_151 } # 計算機節點 vrrp_instance VI_1 { # 表示狀態,MASTER/BACKUP state MASTER # 當前實例綁定的網卡,根據實際狀況配置 interface eth0 # 保證主備節點一致即可 virtual_router_id 51 # 優先級/權重,優先級高的在主節點故障時優先成爲主機點 priority 100 # 主備之間同步檢查的時間間隔,默認1s advert_int 1 # 認證授權的密碼,防止非法節點的進入 authentication { auth_type PASS auth_pass 1111 } # 虛擬IP地址,可以有多個 virtual_ipaddress { 192.168.1.161 } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# vi /etc/keepalived/keepalived.conf lobal_defs { # 路由id: 當前安裝Keepalived 節點主機的標識符,要求全局唯一 router_id LVS_151 }
# 計算機節點 vrrp_instance VI_1 { # 表示狀態,MASTER/BACKUP state MASTER # 當前實例綁定的網卡,根據實際狀況配置 interface eth0 # 保證主備節點一致即可 virtual_router_id 51 # 優先級/權重,優先級高的在主節點故障時優先成爲主機點 priority 100 # 主備之間同步檢查的時間間隔,默認1s advert_int 1 # 認證授權的密碼,防止非法節點的進入 authentication { auth_type PASS auth_pass 1111 } # 虛擬IP地址,可以有多個 virtual_ipaddress { 192.168.1.161 } } |
啓動和關閉服務
cd /usr/local/keepalived/sbin/ ./keepalived # 此時停止服務需使用 ps -ef|grep keepalived 獲取進程號然後執行 kill -9 [pid] # 註冊爲系統服務,需進入源碼目錄拷貝一些文件 cd keepalived-2.0.19/keepalived/etc/ cp init.d/keepalived /etc/init.d/ cp sysconfig/keepalived /etc/sysconfig/ systemctl daemon-reload # 刷新服務 # 此時即可以系統服務操作 keepalived 了 systemctl start keepalived.service
1 2 3 4 5 6 7 8 9 10 |
cd /usr/local/keepalived/sbin/ ./keepalived # 此時停止服務需使用 ps -ef|grep keepalived 獲取進程號然後執行 kill -9 [pid] # 註冊爲系統服務,需進入源碼目錄拷貝一些文件 cd keepalived-2.0.19/keepalived/etc/ cp init.d/keepalived /etc/init.d/ cp sysconfig/keepalived /etc/sysconfig/ systemctl daemon-reload # 刷新服務 # 此時即可以系統服務操作 keepalived 了 systemctl start keepalived.service |
本地測試可通過綁定hosts 文件指向虛擬 IP 地址,備服參照主服配置,state 設置爲BACKUP、權重設置低於主服務器即可
編寫檢測腳本/etc/keepalived/check_nginx_alive_or_not.sh,在 Nginx 中斷時自動啓動服務:
#!/bin/bash A=`ps -C nginx --no-header |wc -l` # 判斷Nginx是否宕機,如果宕機了,嘗試重啓 if [ $A -eq 0 ];then /usr/local/nginx/sbin/nginx # 等待一會再次檢查Nginx,如果沒有啓動成功,則停止Keepalived,使其啓動備用機 sleep 3 if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then killall keepalived fi fi
1 2 3 4 5 6 7 8 9 10 11 12 |
#!/bin/bash
A=`ps -C nginx --no-header |wc -l` # 判斷Nginx是否宕機,如果宕機了,嘗試重啓 if [ $A -eq 0 ];then /usr/local/nginx/sbin/nginx # 等待一會再次檢查Nginx,如果沒有啓動成功,則停止Keepalived,使其啓動備用機 sleep 3 if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then killall keepalived fi fi |
添加配置
chmod +x check_nginx_alive_or_not.sh # vi /etc/keepalived/keepalived.conf vrrp_script check_nginx_alive { script "/etc/keepalived/check_nginx_alive_or_not.sh" interval 2 # 每隔兩秒運行上一行腳本 # weight 10 # 如果腳本運行成功,則升級權重+10 weight -10 # 如果腳本運行失敗,則降低權重-10 } vrrp_instance VI_1 { ... track_script { check_nginx_alive # 追蹤 Nginx 腳本 } ... }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
chmod +x check_nginx_alive_or_not.sh # vi /etc/keepalived/keepalived.conf
vrrp_script check_nginx_alive { script "/etc/keepalived/check_nginx_alive_or_not.sh" interval 2 # 每隔兩秒運行上一行腳本 # weight 10 # 如果腳本運行成功,則升級權重+10 weight -10 # 如果腳本運行失敗,則降低權重-10 }
vrrp_instance VI_1 { ... track_script { check_nginx_alive # 追蹤 Nginx 腳本 } ... } |
Keepalived 雙主熱備
以上使用的Keepalived 雙機主備一個主要的缺點是在MASTER 運行正常的情況下,從服務器將保持閒置的狀態,因此讓我們來了解一下雙主熱備,即互爲主備:
通過設置多個虛擬 IP 並使用 DNS 輪詢的方式實現(阿里、騰訊等平臺的域名解析中均可將A記錄如 www 解析到不同域名並設置權重)
配置類似前面,將原有的配置拷貝一份,比如在備用服務器上再使用另一個虛擬 IP 配置其爲主服務器:
vrrp_instance VI_2 { state MASTER interface eth0 virtual_router_id 52 priority 100 ... virtual_ipaddress { 192.168.1.162 } }
1 2 3 4 5 6 7 8 9 10 |
vrrp_instance VI_2 { state MASTER interface eth0 virtual_router_id 52 priority 100 ... virtual_ipaddress { 192.168.1.162 } } |
相應地原主服務器也需要再進行一份備用服務器的配置
LVS負載均衡
- Linux Virtual Server
- 章文嵩博士主導的開源負載均衡項目
- LVS(ipvs)已被集成到 Linux 內核中
- 負載均衡調度器(四層)
官方網站:http://linux-vs.org/
爲什麼要使用LVS+Nginx?
- LVS基於四層,工作效率高
- 單個 Nginx 承受壓力有限,需要集羣
- LVS 充當 Nginx集羣的調度者
- Nginx 接受請求來回,LVS 可以只接受不響應
LVS 的三種模式
- NAT
請求通過 LVS,響應通過 LVS - TUN
請求(上行)經過 LVS,響應(下行)不經過 LVS,每個節點必須要有網卡,節點對公網暴露 - DR(Direct Routing)
請求經過 LVS,響應通過路由,推薦使用正在上傳…重新上傳取消
LVS DR模式搭建
服務器示例如圖所示
VIP:虛擬 IP
RIP:真實服務器 IP
# LVS及Nginx服務器上關閉網絡配置管理器,避免本地服務器的網絡接口衝突 systemctl stop NetworkManager systemctl disable NetworkManager # 虛擬IP配置(LVS) cd /etc/sysconfig/network-scripts/ # vi ifcfg-eth0:1(可拷貝ifcfg-eth0進行修改,根據不同機器eth0可能會存在變化,只需保留如下幾行) BOOTPROTO="static" DEVICE="eth0:1" ONBOOT="yes" IPADDR=192.168.1.150 NETMASK=255.255.255.0 # 重啓網絡讓配置生效 service network restart # 安裝集羣管理工具(LVS服務器) yum install -y ipvsadm # 查看集羣信息 ipvsadm -Ln # 虛擬 IP 配置(Nginx) cd /etc/sysconfig/network-scripts/ cp ifcfg-lo ifcfg-lo:1 # vi ifcfg-lo:1,僅修改如下行,其餘保留不變 DEVICE=lo:1 IPADDR=192.168.1.150 NETMASK=255.255.255.255 # 重啓網絡 ifup lo # 或 systemctl restart network
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# LVS及Nginx服務器上關閉網絡配置管理器,避免本地服務器的網絡接口衝突 systemctl stop NetworkManager systemctl disable NetworkManager
# 虛擬IP配置(LVS) cd /etc/sysconfig/network-scripts/ # vi ifcfg-eth0:1(可拷貝ifcfg-eth0進行修改,根據不同機器eth0可能會存在變化,只需保留如下幾行) BOOTPROTO="static" DEVICE="eth0:1" ONBOOT="yes" IPADDR=192.168.1.150 NETMASK=255.255.255.0 # 重啓網絡讓配置生效 service network restart
# 安裝集羣管理工具(LVS服務器) yum install -y ipvsadm # 查看集羣信息 ipvsadm -Ln
# 虛擬 IP 配置(Nginx) cd /etc/sysconfig/network-scripts/ cp ifcfg-lo ifcfg-lo:1 # vi ifcfg-lo:1,僅修改如下行,其餘保留不變 DEVICE=lo:1 IPADDR=192.168.1.150 NETMASK=255.255.255.255 # 重啓網絡 ifup lo # 或 systemctl restart network |
注:雲服務器需購買負載均衡或虛擬 IP 纔可實現配置, Nginx 服務器配置對應本地迴環(lo)的原因是其處理響應
LVS-DR模式的其它配置
arp-ignore:ARP 響應級別(處理請求)
- 0:只要本機配置了 ip,就能響應請求
- 1:請求的目標地址到達對應的網絡接口,纔會響應請求
推薦配置爲1
arp-announce:ARP通告行爲(返回響應)
- 0:本地上任何網絡接口都向外通告,所有的網卡都能接收到通告
- 1:儘可能避免本網卡與不匹配的目標進行通告
- 2:只在本網卡通告
推薦配置爲2
# vi /etc/sysctl.conf(Nginx服務器上) net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.default.arp_ignore = 1 net.ipv4.conf.lo.arp_ignore = 1 net.ipv4.conf.all.arp_announce = 2 net.ipv4.conf.default.arp_announce = 2 net.ipv4.conf.lo.arp_announce = 2 # 刷新配置 sysctl -p # 增加網關,用於接收數據報文,將到達本地的請求交給 lo 處理 route add -host 192.168.0.150 dev lo:1 # 若無命令執行yum install net-tools -y安裝 route -n # 查看 route delete # 刪除 # 設置開機配置,避免重啓後配置失效 echo "route add -host 192.168.0.150 dev lo:1" >> /etc/rc.local
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# vi /etc/sysctl.conf(Nginx服務器上) net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.default.arp_ignore = 1 net.ipv4.conf.lo.arp_ignore = 1 net.ipv4.conf.all.arp_announce = 2 net.ipv4.conf.default.arp_announce = 2 net.ipv4.conf.lo.arp_announce = 2
# 刷新配置 sysctl -p
# 增加網關,用於接收數據報文,將到達本地的請求交給 lo 處理 route add -host 192.168.0.150 dev lo:1 # 若無命令執行yum install net-tools -y安裝 route -n # 查看 route delete # 刪除 # 設置開機配置,避免重啓後配置失效 echo "route add -host 192.168.0.150 dev lo:1" >> /etc/rc.local |
LVS配置
ipvsadm -A -t 192.168.1.150:80 -s rr # 編輯:ipvsadm -E -t 192.168.1.150:80 -s rr -p 5 ipvsadm -a -t 192.168.1.150:80 -r 192.168.1.171:80 -g ipvsadm -a -t 192.168.1.150:80 -r 192.168.1.172:80 -g ipvsadm -S # 保存規則避免重啓失效 # 相關參數說明請參見 ipvsadm -h # 查看 ipvsadm -Ln # 查看持久化連接: ipvsadm -Ln --persistent-conn # 查看請求過期時間及源IP和目標IP: ipvsadm -Lnc # 查看狀態: ipvsadm -Ln --status # 設置tcp tcpfin udp 的過期時間(通常保持默認,以下設置用於測試) ipvsadm --set 1 1 1 # 查看過期時間 ipvsadm -Ln --timeout
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
ipvsadm -A -t 192.168.1.150:80 -s rr # 編輯:ipvsadm -E -t 192.168.1.150:80 -s rr -p 5 ipvsadm -a -t 192.168.1.150:80 -r 192.168.1.171:80 -g ipvsadm -a -t 192.168.1.150:80 -r 192.168.1.172:80 -g ipvsadm -S # 保存規則避免重啓失效 # 相關參數說明請參見 ipvsadm -h # 查看 ipvsadm -Ln # 查看持久化連接: ipvsadm -Ln --persistent-conn # 查看請求過期時間及源IP和目標IP: ipvsadm -Lnc # 查看狀態: ipvsadm -Ln --status
# 設置tcp tcpfin udp 的過期時間(通常保持默認,以下設置用於測試) ipvsadm --set 1 1 1 # 查看過期時間 ipvsadm -Ln --timeout |
-A:添加集羣
-t:tcp協議
ip地址:設定集羣的訪問ip,也就是LVS的虛擬ip
-s:設置負載均衡的算法,rr表示輪詢
-p:設置連接持久化的時間,默認300秒
-a:添加真實服務器
-r:真實服務器的ip地址
-g:設定DR模式
Keepalived+LVS高可用
在主備 LVS 服務器上安裝 Keepalived,方法參見上方Keepalived安裝
global_defs { ... } vrrp_instance VI_1 { ... } # 配置集羣地址訪問的 IP+端口,端口和 Nginx 保持一致 virtual_server 192.168.1.150 80 { # 健康檢查時間,單位:秒 delay_loop 6 # 配置負載均衡算法,默認是輪詢 lb_algo rr # 設置LVS的模式:NAT|TUN|DR lb_kind DR # 設置會話持久化時間 persistence_timeout 10 # 協議 protocol TCP # 負載均衡真實服務器,即Nginx節點 IP real_server 192.168.1.171 80 { # 輪詢的默認權重配比 weight 1 # 設置健康檢查 TCP_CHECK { # 檢查的端口 connect_port 80 # 超時時間 connect_timeout 2 # 重試次數(s) nb_get_retry 5 # 間隔時間(s) delay_before_retry 3 } } real_server 192.168.1.172 80 { weight 1 ... } } # 清除此前規則 ipvsadm -C systemctl restart keepalived
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
global_defs { ... }
vrrp_instance VI_1 { ... }
# 配置集羣地址訪問的 IP+端口,端口和 Nginx 保持一致 virtual_server 192.168.1.150 80 { # 健康檢查時間,單位:秒 delay_loop 6 # 配置負載均衡算法,默認是輪詢 lb_algo rr # 設置LVS的模式:NAT|TUN|DR lb_kind DR # 設置會話持久化時間 persistence_timeout 10 # 協議 protocol TCP # 負載均衡真實服務器,即Nginx節點 IP real_server 192.168.1.171 80 { # 輪詢的默認權重配比 weight 1 # 設置健康檢查 TCP_CHECK { # 檢查的端口 connect_port 80 # 超時時間 connect_timeout 2 # 重試次數(s) nb_get_retry 5 # 間隔時間(s) delay_before_retry 3 } }
real_server 192.168.1.172 80 { weight 1 ... } }
# 清除此前規則 ipvsadm -C systemctl restart keepalived |
以上即實現了高可用的 LVS+Keepalived 方案
LVS 負載均衡算法
靜態算法
根據 LVS 自有固定算法分發用戶請求
1、輪詢(Round Robin – rr):平均分配請求(同 Nginx的輪詢)
2、加權輪詢(Weight Round Robin – wrr): 按照權重比例分配用戶請求,權重越高,分配請求越多(同 Nginx 的權重)
3、源地址散列(Source Hash – sh):同 IP 用戶由相同 RS 處理(同 Nginx 的 ip_hash)
4、目標地址散列(Destination Hash – dh):根據不同 url 請求不同的 RS(同 Nginx的 url_hash)
動態算法
根據流量或服務器壓力不同分配用戶請求
1、最小連接數(Least Connections – lc):將請求分配給連接最小的服務器
2、 加權最小連接數(Weight Least Connections – wlc):用數值表示服務器處理性能,將請求分發到性能好且空閒的服務器
3、 最短期望延遲(Shortest Expected Delay – sed):是一種特殊的 wlc 算法,將請求交給運算結果最小的服務器,設服務器 A、B、C的權重爲1、2、3,計算方式如下:
- A: (1+1)/1=2
- B: (1+2)/2=3/2
- C: (1+3)/3=4/3
4、 最少隊列調試(Never Queue = nq):如有 RS 的連接數等於0,直接將請求分配過去,無需排隊等待運算
注:RS(Real Server)
LVS 最常使用的負載均衡算法爲 wlc 或 wrr
常見問題
1、nginx: [error] open() “/var/run/nginx/nginx.pid” failed (2: No such file or directory)
mkdir /var/run/nginx /usr/local/nginx/sbin/nginx -s reload # 未啓動則直接執行/usr/local/nginx/sbin/nginx
1 2 |
mkdir /var/run/nginx /usr/local/nginx/sbin/nginx -s reload # 未啓動則直接執行/usr/local/nginx/sbin/nginx |
2、nginx: [error] invalid PID number “” in “/var/run/nginx/nginx.pid”
/usr/local/nginx sbin/nginx -c conf/nginx.conf # 指定配置文件 sbin/nginx -s reload
1 2 3 |
/usr/local/nginx sbin/nginx -c conf/nginx.conf # 指定配置文件 sbin/nginx -s reload |
3、默認 Docker 容器中沒有 vi 等編輯器,可進行安裝,也可在宿主機執行如下命令,然後根據返回的路徑編輯容器中的文件
docker inspect --format='{{.GraphDriver.Data.MergedDir}}' 容器 ID 或名稱
1 |
docker inspect --format='{{.GraphDriver.Data.MergedDir}}' 容器 ID 或名稱 |
4、*** WARNING – this build will not support IPVS with IPv6. Please install libnl/libnl-3 dev libraries to support IPv6 with IPVS.
yum -y install libnl libnl-devel
1 |
yum -y install libnl libnl-devel |