Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
要: 隨著相關(guān)瀏覽器對(duì)HTTP協(xié)議的“不安全”、紅色頁(yè)面警告等嚴(yán)格措施的出臺(tái),以及向 iOS 應(yīng)用的 ATS 要求和微信、支付寶小程序強(qiáng)制 HTTPS 需求,以及在合規(guī)方面如等級(jí)保護(hù)對(duì)傳輸安全性的要求都在推動(dòng) HTTPS 的發(fā)展。
分享一個(gè)卓見云的較多客戶遇到HTTPS優(yōu)化案例。
隨著相關(guān)瀏覽器對(duì)HTTP協(xié)議的“不安全”、紅色頁(yè)面警告等嚴(yán)格措施的出臺(tái),以及向 iOS 應(yīng)用的 ATS 要求和微信、支付寶小程序強(qiáng)制 HTTPS 需求,以及在合規(guī)方面如等級(jí)保護(hù)對(duì)傳輸安全性的要求都在推動(dòng) HTTPS 的發(fā)展。
雖然 HTTPS 優(yōu)化了網(wǎng)站訪問(wèn)體驗(yàn)(防劫持)以及讓傳輸更加安全,但是很多網(wǎng)站主趕鴨子上架式的使用了 HTTPS 后往往都會(huì)遇到諸如:頁(yè)面加載速度變慢、服務(wù)器負(fù)載過(guò)高以及證書過(guò)期不及時(shí)更新等問(wèn)題。
所以本文就來(lái)探討一下 HTTPS 的優(yōu)化實(shí)踐。
其實(shí)像 Apache Httpd、LigHttpd、Canddy 等 Web 服務(wù)軟件都可以設(shè)置 HTTPS,但是在相應(yīng)的擴(kuò)展生態(tài)和更新率上都不如 Nginx。 Nginx 作為大型互聯(lián)網(wǎng)網(wǎng)站的 Web 入口軟件有著廣泛的支持率,例如阿里系的 Tengine、CloudFlare 的 cloudflare-nginx、又拍云用的 OpenResty 都是基于 Nginx 而來(lái)的,Nginx 是接受過(guò)大規(guī)模訪問(wèn)驗(yàn)證的。同時(shí)大家也將自己開發(fā)的組件回饋給 Nginx 社區(qū),讓 Nginx 有著非常良好的擴(kuò)展生態(tài)。
? 圖1-1 Nginx 在全網(wǎng)的使用情況
所以說(shuō) Nginx 是一款很好的 Web 服務(wù)軟件,選擇 Nginx 在提升性能的同時(shí)能極大的降低我們的擴(kuò)展成本。
圍繞 Web 服務(wù)已經(jīng)有非常多的新功能需要我們關(guān)注并應(yīng)用了,這里先羅列相關(guān)新功能。
HTTP/2
相比廉頗老矣的 HTTP/1.x,HTTP/2 在底層傳輸做了很大的改動(dòng)和優(yōu)化包括有:
在 Akamai 的 HTTP/2 DEMO中,加載300張圖片,HTTP/2 的優(yōu)越性極大的顯現(xiàn)了出來(lái),在 HTTP/1.X 需要 14.8s 的操作中,HTTP/2 僅需不到1s。
HTTP/2 現(xiàn)在已經(jīng)獲得了絕大多數(shù)的現(xiàn)代瀏覽器的支持。只要我們保證 Nginx 版本大于 1.9.5 即可。當(dāng)然建議保持最新的 Nginx 穩(wěn)定版本以便更新相關(guān)補(bǔ)丁。同時(shí) HTTP/2 在現(xiàn)代瀏覽器的支持上還需要 OpenSSL 版本大于 1.0.2。
TLS 1.3
和 HTTP/1.x 一樣,目前受到主流支持的 TLS 協(xié)議版本是 1.1 和 1.2,分別發(fā)布于 2006年和2008年,也都已經(jīng)落后于時(shí)代的需求了。在2018年8月份,IETF終于宣布TLS 1.3規(guī)范正式發(fā)布了,標(biāo)準(zhǔn)規(guī)范(Standards Track)定義在 rfc8446。
TLS 1.3 相較之前版本的優(yōu)化內(nèi)容有:
總結(jié)一下就是在更安全的基礎(chǔ)上還做到了更快,目前 TLS 1.3 的重要實(shí)現(xiàn)是 OpenSSL 1.1.1 開始支持了,并且 1.1.1 還是一個(gè) LTS 版本,未來(lái)的 RHEL8、Debian10 都將其作為主要支持版本。在 Nginx 上的實(shí)現(xiàn)需要 Nginx 1.13+。
Brotli
Brotli 是由 Google 于 2015 年 9 月推出的無(wú)損壓縮算法,它通過(guò)用變種的 LZ77 算法,Huffman 編碼和二階文本建模進(jìn)行數(shù)據(jù)壓縮,是一種壓縮比很高的壓縮方法。
根據(jù)Google 發(fā)布的研究報(bào)告,Brotli 具有如下特點(diǎn):
在兼容 GZIP 的同時(shí),相較 GZIP:
Brotli 的支持必須依賴 HTTPS,不過(guò)換句話說(shuō)就是只有在 HTTPS 下才能實(shí)現(xiàn) Brotli。
ECC 證書
橢圓曲線密碼學(xué)(Elliptic curve cryptography,縮寫為ECC),一種建立公開金鑰加密的算法,基于橢圓曲線數(shù)學(xué)。橢圓曲線在密碼學(xué)中的使用是在1985年由Neal Koblitz和Victor Miller分別獨(dú)立提出的。
內(nèi)置 ECDSA 公鑰的證書一般被稱之為 ECC 證書,內(nèi)置 RSA 公鑰的證書就是 RSA 證書。由于 256 位 ECC Key 在安全性上等同于 3072 位 RSA Key,加上 ECC 運(yùn)算速度更快,ECDHE 密鑰交換 + ECDSA 數(shù)字簽名無(wú)疑是最好的選擇。由于同等安全條件下,ECC 算法所需的 Key 更短,所以 ECC 證書文件體積比 RSA 證書要小一些。
ECC 證書不僅僅可以用于 HTTPS 場(chǎng)景當(dāng)中,理論上可以代替所有 RSA 證書的應(yīng)用場(chǎng)景,如 SSH 密鑰登陸、SMTP 的 TLS 發(fā)件等。
不過(guò)使用 ECC 證書有兩個(gè)點(diǎn)需要注意:
一、 并不是每一個(gè)證書類型都支持的,一般商業(yè)證書中帶增強(qiáng)型字眼的才支持ECC證書的簽發(fā)。
二、 ECC證書在一些場(chǎng)景中可能還不被支持,因?yàn)橐恍┊a(chǎn)品或者軟件可能還不支持 ECC。 這時(shí)候就要虛線解決問(wèn)題了,例如針對(duì)部分舊操作系統(tǒng)和瀏覽器不支持ECC,可以通過(guò)ECC+RSA雙證書模式來(lái)解決問(wèn)題。
下載源碼
綜合上述我們要用到的新特性,我們整合一下需求:
HTTP/2 要求 Nginx 1.9.5+,,OpenSSL 1.0.2+
TLS 1.3 要求 Nginx 1.13+,OpenSSL 1.1.1+
Brotli 要求 HTTPS,并在 Nginx 中添加擴(kuò)展支持
ECC 雙證書 要求 Nginx 1.11+
這里 Nginx,我個(gè)人推薦 1.15+,因?yàn)?1.14 雖然已經(jīng)能支持TLS1.3了,但是一些 TLS1.3 的進(jìn)階特性還只在 1.15+ 中提供。
然后我們定義一下版本號(hào):
# Version OpenSSLVersion='openssl-1.1.1a'; nginxVersion='nginx-1.14.1';
建議去官網(wǎng)隨時(shí)關(guān)注最新版:
http://nginx.org/en/download.html
https://www.openssl.org/source/
https://github.com/eustas/ngx_brotli/releases
Nginx
cd /opt wget http://nginx.org/download/$nginxVersion.tar.gz tar xzf $nginxVersion.tar.gz
OpenSSL
cd /opt wget https://www.openssl.org/source/$OpenSSLVersion.tar.gz tar xzf $OpenSSLVersion.tar.gz
Brotli
cd /opt git clone https://github.com/eustas/ngx_brotli.git cd ngx_brotli git submodule update --init --recursive
編譯
cd /opt/$nginxVersion/ ./configure \ --prefix=/usr/local/nginx \ ## 編譯后安裝的目錄位置 --with-openssl=/opt/$OpenSSLVersion \ ## 指定單獨(dú)編譯入 OpenSSL 的源碼位置 --with-openssl-opt=enable-tls1_3 \ ## 開啟 TLS 1.3 支持 --with-http_v2_module \ ## 開啟 HTTP/2 --with-http_ssl_module \ ## 開啟 HTTPS 支持 --with-http_gzip_static_module \ ## 開啟 GZip 壓縮 --add-module=/opt/ngx_brotli ## 編譯入 ngx_BroTli 擴(kuò)展 make && make install ## 編譯并安裝
后續(xù)還有相關(guān)變量設(shè)置和設(shè)置服務(wù)、開啟啟動(dòng)等步驟,篇幅限制就省略了,這篇文章有介紹在 Ubuntu 下的 Nginx 編譯:https://www.mf8.biz/ubuntu-nginx/ 。
配置
接下來(lái)我們需要修改配置文件。
HTTP2
listen 443 ssl http2;
只要在 server{} 下的lisen 443 ssl 后添加 http2 即可。而且從 1.15 開始,只要寫了這一句話就不需要再寫 ssl on 了,很多小伙伴可能用了 1.15+ 以后衍用原配置文件會(huì)報(bào)錯(cuò),就是因?yàn)檫@一點(diǎn)。
TLS 1.3
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
如果不打算繼續(xù)支持 IE8,或者一些合規(guī)的要求,可以去掉TLSv1。
然后我們?cè)傩薷膶?duì)應(yīng)的加密算法,加入TLS1.3引入的新算法:
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
如果不打算繼續(xù)支持 IE8,可以去掉包含 3DES 的 Cipher Suite。
默認(rèn)情況下 Nginx 因?yàn)榘踩?,沒有開啟 TLS 1.3 0-RTT,可以通過(guò)添加 ssl_early_data on; 指令開啟 0-RTT的支持。
————
實(shí)驗(yàn)性嘗試
眾所周知,TLS1.3 由于更新了很久,很多瀏覽器的舊版本依舊只支持 Draft 版本,如 23 26 28 分別在 Chrome、FirFox 上有支持,反而正式版由于草案出來(lái)很久,導(dǎo)致TLS1.3在瀏覽器上兼容性不少太好。
可以使用 https://github.com/hakasenyang/openssl-patch/ 提供的 OpenSSL Patch 讓 OpenSSL 1.1.1 同時(shí)支持草案23,26,28和正式版輸出。 不過(guò)由于不是官方腳本,穩(wěn)定性和安全性有待考量。
ECC雙證書
雙證書配置的很簡(jiǎn)單了,保證域名的證書有RSA和ECC各一份即可。
##證書部分 ssl_certificate /usr/local/nginx/conf/ssl/www.mf8.biz-ecc.crt; #ECC證書 ssl_certificate_key /usr/local/nginx/conf/ssl/www.mf8.biz-ecc.key; #ECC密鑰 ssl_certificate /usr/local/nginx/conf/ssl/www.mf8.biz.crt; #RSA證書 ssl_certificate_key /usr/local/nginx/conf/ssl/www.mf8.biz.key; #RSA密鑰
Brotli
需要在對(duì)應(yīng)配置文件中,添加下面代碼即可:
brotli on; brotli_comp_level 6; brotli_min_length 1k; brotli_types text/plain text/css text/xml text/javascript text/x-component application/json application/javascript application/x-javascript application/xml application/xhtml+xml application/rss+xml application/atom+xml application/x-font-ttf application/vnd.ms-fontobject image/svg+xml image/x-icon font/opentype;
為了防止大家看糊涂了,放一個(gè)完整的 server{}供大家參考:
server { listen 443 ssl http2; # 開啟 http/2 server_name mf8.biz www.mf8.biz; #證書部分 ssl_certificate /usr/local/nginx/conf/ssl/www.mf8.biz-ecc.crt; #ECC證書 ssl_certificate_key /usr/local/nginx/conf/ssl/www.mf8.biz-ecc.key; #ECC密鑰 ssl_certificate /usr/local/nginx/conf/ssl/www.mf8.biz.crt; #RSA證書 sl_certificate_key /usr/local/nginx/conf/ssl/www.mf8.biz.key; #RSA密鑰 #TLS 握手優(yōu)化 ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; keepalive_timeout 75s; keepalive_requests 100; #TLS 版本控制 ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; ssl_ciphers 'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5'; # 開啟 1.3 o-RTT ssl_early_data on; # GZip 和 Brotli gzip on; gzip_comp_level 6; gzip_min_length 1k; gzip_types text/plain text/css text/xml text/javascript text/x-component application/json application/javascript application/x-javascript application/xml application/xhtml+xml application/rss+xml application/atom+xml application/x-font-ttf application/vnd.ms-fontobject image/svg+xml image/x-icon font/opentype; brotli on; brotli_comp_level 6; brotli_min_length 1k; brotli_types text/plain text/css text/xml text/javascript text/x-component application/json application/javascript application/x-javascript application/xml application/xhtml+xml application/rss+xml application/atom+xml application/x-font-ttf application/vnd.ms-fontobject image/svg+xml image/x-icon font/opentype; location / { root html; index index.html index.htm; } }
先驗(yàn)證一下配置文件是否有誤:
nginx -t
如果反饋的是:
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
就可以重啟 Nginx ,然后到對(duì)應(yīng)網(wǎng)站中去查看效果了。
HTTP/2
通過(guò)瀏覽器的開發(fā)者工具,我們可以在 Network 欄目中看到 Protocol 中顯示 h2 有無(wú)來(lái)判斷。
TLS 1.3
老地方,我們可以通過(guò)瀏覽器的開發(fā)者工具 中的 Security 欄目看到 Connection 欄目下是否有顯示 TLS 1.3
ECC 雙證書
ECC 雙證書配置了以后無(wú)非就是在舊瀏覽器設(shè)別上的驗(yàn)證了。這里用足夠老的上古XP虛擬機(jī)來(lái)給大家證明一波。
XP系統(tǒng)上:
現(xiàn)代操作系統(tǒng)上的:
Brotli
通過(guò)瀏覽器的開發(fā)者工具,我們可以在 Network 欄目中,打開具體頁(yè)面的頭信息,看到 accept-encoding 中有 br 字眼就行。
通過(guò)上述手段應(yīng)該可以讓 HTTPS 訪問(wèn)的體驗(yàn)優(yōu)化不少,而且會(huì)比沒做 HTTPS 的網(wǎng)站訪問(wèn)可能更快。
這樣的模式比較適合云服務(wù)器單機(jī)或者簡(jiǎn)單集群上搭建,如果有應(yīng)用 SLB 七層代理、WAF、CDN 這樣的產(chǎn)品可能會(huì)讓我們的這些操作都白費(fèi)。 我們的這幾項(xiàng)操作都是自建的 Web 七層服務(wù),如果有設(shè)置 SLB 七層代理、WAF、CDN 這樣設(shè)置在云服務(wù)器之前就會(huì)被覆蓋掉。
由于 SLB 七層和CDN這樣的產(chǎn)品會(huì)更加追求廣泛的兼容性和穩(wěn)定性并不會(huì)第一時(shí)間就用上上述的這些新特性(HTTP/2 是普遍有的),但是他們都配備了阿里云的 Tengine 的外部專用算法加速硬件如 Intel? QuickAssist Technology(QAT) 加速器可以顯著提高SSL/TLS握手階段性能。 所有 HTTPS 的加密解密都在 SLB 或 CDN 上完成,而不會(huì)落到ECS上,可以顯著降低 ECS 的負(fù)載壓力,并且提升訪問(wèn)體驗(yàn)。
目前云上的網(wǎng)絡(luò)產(chǎn)品中能支持四層的都是可以繼續(xù)兼容我們這套設(shè)計(jì)的,例如:SLB 的四層轉(zhuǎn)發(fā)(TCP UDP)、DDOS高防的四層轉(zhuǎn)發(fā)。
作者:妙正灰
我們之前有分析過(guò),單臺(tái)服務(wù)器可能會(huì)遇到的問(wèn)題,比如服務(wù)器宕機(jī),或者并發(fā)用戶太多,單臺(tái)服務(wù)器不夠等問(wèn)題,所以需要集群架構(gòu),就是將項(xiàng)目部署到多臺(tái)服務(wù)器。
但這樣會(huì)出現(xiàn)新的問(wèn)題:
對(duì)于這些問(wèn)題的出現(xiàn),我們可以用Nginx來(lái)解決,除此以外,Nginx還可以幫助我們區(qū)分動(dòng)態(tài)服務(wù)器和靜態(tài)服務(wù)器。
Nginx (engine x) 是一個(gè)高性能的Http服務(wù)器和反向代理web服務(wù)器,同時(shí)也提供了IMAP/POP3/SMTP服務(wù)。它可以用做反向代理服務(wù)器,郵件服務(wù)器,實(shí)現(xiàn)負(fù)載均衡和動(dòng)靜分離。
穩(wěn)定性強(qiáng)、豐富的功能集、簡(jiǎn)單的配置文件和低系統(tǒng)資源的消耗,占用內(nèi)存少,并發(fā)能力強(qiáng)。
中國(guó)大陸使用nginx網(wǎng)站用戶有:百度、京東、新浪、網(wǎng)易、騰訊、淘寶等。
docker pull daocloud.io/library/nginx:latest
docker images
docker run -d -p 80:80 --name nginx 29
先進(jìn)入Nginx容器內(nèi)部
docker exec -it 容器id bash #進(jìn)入容器的終端,可以執(zhí)行一些如ls pwd等一些簡(jiǎn)單的shell命令
再進(jìn)入容器的etc/nginx目錄下:
將nginx.conf文件的內(nèi)容復(fù)制出來(lái)
# 全局塊
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
#events塊
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;
}
全局塊:
從配置文件開始到 events 塊之間的內(nèi)容,主要會(huì)設(shè)置一些影響nginx 服務(wù)器整體運(yùn)行的配置指令,主要包括配置運(yùn)行 Nginx 服務(wù)器的用戶(組)、允許生成的 worker process 數(shù),進(jìn)程 PID 存放路徑、日志存放路徑和類型以及配置文件的引入等。
worker_processes 1 #這是 Nginx 服務(wù)器并發(fā)處理服務(wù)的關(guān)鍵配置,worker_processes 值越大,可以支持的并發(fā)處理量也越多,但是會(huì)受到硬件、軟件等設(shè)備的制約。
events 塊:
events 塊涉及的指令主要影響 Nginx 服務(wù)器與用戶的網(wǎng)絡(luò)連接,常用的設(shè)置包括是否開啟對(duì)多 work process 下的網(wǎng)絡(luò)連接進(jìn)行序列化,是否允許同時(shí)接收多個(gè)網(wǎng)絡(luò)連接,選取哪種事件驅(qū)動(dòng)模型來(lái)處理連接請(qǐng)求,每個(gè) word process 可以同時(shí)支持的最大連接數(shù)等。 上述例子就表示每個(gè) work process 支持的最大連接數(shù)為 1024. 這部分的配置對(duì) Nginx 的性能影響較大,在實(shí)際中應(yīng)該靈活配置。
http塊:
http模塊顧名思義,就是關(guān)于http服務(wù)請(qǐng)求的配置。這些配置包括http請(qǐng)求的文件類型(MIME-TYPE)的定義,http請(qǐng)求日志的輸出,http連接的超時(shí)時(shí)長(zhǎng),單連接請(qǐng)求上限的配置。
http塊的最后一句#include /etc/nginx/conf.d/*.conf ,是指引入了conf.d目錄下的以.conf為結(jié)尾的配置文件。我們找到他們。
default.conf內(nèi)容如下:
server {
listen 80;
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;
#}
}
去掉注釋部分
server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
將其替換到nginx.conf文件中的include /etc/nginx/conf.d/*.conf,然后可以看到http塊的內(nèi)容為:
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;
server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
http塊結(jié)構(gòu)包含:
server塊: 每一個(gè)Server模塊就是一個(gè)獨(dú)立的虛擬主機(jī),每個(gè)虛擬的主機(jī)可配置不同的域名或IP地址
?
今天主要介紹nginx的模塊——ngx_http_proxy_module和ngx_http_upstream_module,嚴(yán)格來(lái)說(shuō),nginx自帶是沒有針對(duì)負(fù)載均衡后端節(jié)點(diǎn)的健康檢查的,但是可以通過(guò)默認(rèn)自帶的ngx_http_proxy_module 模塊和ngx_http_upstream_module模塊中的相關(guān)指令來(lái)完成當(dāng)后端節(jié)點(diǎn)出現(xiàn)故障時(shí),自動(dòng)切換到健康節(jié)點(diǎn)來(lái)提供訪問(wèn)。
反向代理( reverse proxy) 方式是指用代理服務(wù)器來(lái)接受 Internet 上的連接請(qǐng)求, 然后將請(qǐng)求轉(zhuǎn)發(fā)給內(nèi)部網(wǎng)絡(luò)中的上游服務(wù)器, 并將從上游服務(wù)器上得到的結(jié)果返回給 Internet 上請(qǐng)求連接的客戶端, 此時(shí)代理服務(wù)器對(duì)外的表現(xiàn)就是一個(gè) Web 服務(wù)器。 充當(dāng)反向代理服務(wù)器也是 Nginx 的一種常見用法( 反向代理服務(wù)器必須能夠處理大量并發(fā)請(qǐng)求)。
由于Nginx具有“強(qiáng)悍”的高并發(fā)高負(fù)載能力, 因此一般會(huì)作為前端的服務(wù)器直接向客戶端提供靜態(tài)文件服務(wù)。 但也有一些復(fù)雜、 多變的業(yè)務(wù)不適合放到 Nginx 服務(wù)器上, 這時(shí)會(huì)用Apache、 Tomcat 等服務(wù)器來(lái)處理。 于是, Nginx 通常會(huì)被配置為既是靜態(tài)Web服務(wù)器也是反向代理服務(wù)器( 如下圖所示), 不適合Nginx處理的請(qǐng)求就會(huì)直接轉(zhuǎn)發(fā)到上游服務(wù)器中處理。
ngx_http_proxy_module模塊允許傳送請(qǐng)求到其它服務(wù)器,也就是做反向代理。下面提供一個(gè)基本的配置示例:
location / { root /usr/share/nginx/html; proxy_redirect default; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_connect_timeout 2; proxy_send_timeout 5; proxy_read_timeout 5; proxy_buffer_size 256k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; proxy_pass http://127.0.0.1:80; }
ngx_http_proxy_module模塊常用指令解釋:
1)proxy_bind
Syntax: proxy_bind address [transparent] | off; Default: — Context: http, server, location This directive appeared in version 0.8.22.
在調(diào)用connect()前將上游socket綁定到一個(gè)本地地址,如果主機(jī)有多個(gè)網(wǎng)絡(luò)接口或別名,但是你希望代理的連接通過(guò)指定的接口或地址,可以使用這個(gè)指令。
透明傳輸模式允許傳出連接到代理服務(wù)器起源于一個(gè)非本地IP地址,例如,從一個(gè)真實(shí)的IP地址的客戶端 ︰
proxy_bind $remote_addr transparent;
為了使此參數(shù)工作,就必須以超級(jí)用戶的特權(quán)運(yùn)行nginx的工作進(jìn)程和配置內(nèi)核路由表攔截來(lái)自代理服務(wù)器的網(wǎng)絡(luò)流量。
2)proxy_buffer_size
Syntax: proxy_buffer_size size; Default:proxy_buffer_size 4k|8k; Context:http, server, location
設(shè)置緩沖區(qū)的大小為size,nginx從被代理的服務(wù)器讀取響應(yīng)時(shí),使用該緩沖區(qū)保存響應(yīng)的開始部分。這部分通常包含著一個(gè)小小的響應(yīng)頭。該緩沖區(qū)大小默認(rèn)等于proxy_buffers指令設(shè)置的一塊緩沖區(qū)的大小,但它也可以被設(shè)置得更小。
3)proxy_buffers
Syntax: proxy_buffers number size; Default:proxy_buffers 8 4k|8k; Context:http, server, location
設(shè)置用于讀取應(yīng)答(來(lái)自被代理服務(wù)器)的緩沖區(qū)數(shù)目和大小,為每個(gè)連接設(shè)置緩沖區(qū)的數(shù)量是number參數(shù),每塊緩沖區(qū)的大小是size參數(shù)。這些緩沖區(qū)用于保存從被代理的服務(wù)器讀取的響應(yīng)。每塊緩沖區(qū)默認(rèn)等于一個(gè)內(nèi)存頁(yè)的大小。這個(gè)值默認(rèn)是4K還是8K,取決于平臺(tái)。
4)proxy_buffering
Syntax: proxy_buffering on | off; Default:proxy_buffering on; Context:http, server, location
代理的時(shí)候,開啟或關(guān)閉緩沖后端服務(wù)器的響應(yīng)。
當(dāng)開啟緩沖時(shí),nginx盡可能快地從被代理的服務(wù)器接收響應(yīng),再將它存入proxy_buffer_size和proxy_buffers指令設(shè)置的緩沖區(qū)中。
如果響應(yīng)無(wú)法整個(gè)納入內(nèi)存,那么其中一部分將存入磁盤上的臨時(shí)文件。proxy_max_temp_file_size和proxy_temp_file_write_size指令可以控制臨時(shí)文件的寫入。
當(dāng)關(guān)閉緩沖時(shí),收到響應(yīng)后,nginx立即將其同步傳給客戶端。nginx不會(huì)嘗試從被代理的服務(wù)器讀取整個(gè)請(qǐng)求,而是將proxy_buffer_size指令設(shè)定的大小作為一次讀取的最大長(zhǎng)度。
響應(yīng)頭“X-Accel-Buffering”傳遞“yes”或“no”可以動(dòng)態(tài)地開啟或關(guān)閉代理的緩沖功能。 這個(gè)能力可以通過(guò)proxy_ignore_headers指令關(guān)閉。
5)proxy_busy_buffers_size
Syntax: proxy_busy_buffers_size size; Default:proxy_busy_buffers_size 8k|16k; Context:http, server, location
當(dāng)開啟緩沖響應(yīng)的功能以后,在沒有讀到全部響應(yīng)的情況下,寫緩沖到達(dá)一定大小時(shí),nginx一定會(huì)向客戶端發(fā)送響應(yīng),直到緩沖小于此值。
這條指令用來(lái)設(shè)置此值。 同時(shí),剩余的緩沖區(qū)可以用于接收響應(yīng),如果需要,一部分內(nèi)容將緩沖到臨時(shí)文件。該大小默認(rèn)是proxy_buffer_size和proxy_buffers指令設(shè)置單塊緩沖大小的兩倍。
6)proxy_max_temp_file
Syntax: proxy_max_temp_file_size size; Default:proxy_max_temp_file_size 1024m; Context:http, server, location
打開響應(yīng)緩沖以后,如果整個(gè)響應(yīng)不能存放在proxy_buffer_size和proxy_buffers指令設(shè)置的緩沖區(qū)內(nèi),部分響應(yīng)可以存放在臨時(shí)文件中。 這條指令可以設(shè)置臨時(shí)文件的最大容量。
而每次寫入臨時(shí)文件的數(shù)據(jù)量則由proxy_temp_file_write_size指令定義。
將此值設(shè)置為0將禁止響應(yīng)寫入臨時(shí)文件。
7)proxy_temp_file_write_size
Syntax: proxy_temp_file_write_size size; Default:proxy_temp_file_write_size 8k|16k; Context:http, server, location
在開啟緩沖后端服務(wù)器響應(yīng)到臨時(shí)文件的功能后,設(shè)置nginx每次寫數(shù)據(jù)到臨時(shí)文件的size(大小)限制。 size的默認(rèn)值是proxy_buffer_size指令和proxy_buffers指令定義的每塊緩沖區(qū)大小的兩倍, 而臨時(shí)文件最大容量由 proxy_max_temp_file_size指令設(shè)置。
8)proxy_temp_path
Syntax: proxy_temp_path path [level1 [level2 [level3]]]; Default:proxy_temp_path proxy_temp; Context:http, server, location
定義從后端服務(wù)器接收的臨時(shí)文件的存放路徑,可以為臨時(shí)文件路徑定義至多三層子目錄的目錄樹。 比如,下面配置
proxy_temp_path /spool/nginx/proxy_temp 1 2;
那么臨時(shí)文件的路徑看起來(lái)會(huì)是這樣:
/spool/nginx/proxy_temp/7/45/00000123457
9)proxy_connect_timeout
Syntax: proxy_connect_timeout time; Default:proxy_connect_timeout 60s; Context:http, server, location
設(shè)置與后端服務(wù)器建立連接的超時(shí)時(shí)間,應(yīng)該注意這個(gè)超時(shí)一般不可能大于75秒。默認(rèn)為60s,建議生產(chǎn)環(huán)境連接時(shí)間設(shè)置為1到2s。
10)proxy_http_version
Syntax: proxy_http_version 1.0 | 1.1; Default:proxy_http_version 1.0; Context:http, server, location This directive appeared in version 1.1.4.
設(shè)置代理使用的HTTP協(xié)議版本,默認(rèn)使用的版本是1.0,而1.1版本則推薦在使用keepalive連接時(shí)一起使用。我接觸的生產(chǎn)環(huán)境中都是設(shè)置http 1.1版本了。
11)proxy_ignore_client_abort
Syntax: proxy_ignore_client_abort on | off; Default:proxy_ignore_client_abort off; Context:http, server, location
決定當(dāng)客戶端在響應(yīng)傳輸完成前就關(guān)閉連接時(shí),nginx是否應(yīng)關(guān)閉后端連接。
12)proxy_pass
Syntax: proxy_pass URL; Default:— Context:location, if in location, limit_except
設(shè)置后端服務(wù)器的協(xié)議和地址,還可以設(shè)置可選的URI以定義本地路徑和后端服務(wù)器的映射關(guān)系。 這條指令可以設(shè)置的協(xié)議是“http”或者“https”,而地址既可以使用域名或者IP地址加端口(可選)的形式來(lái)定義:
proxy_pass http://localhost:8000/uri/;
對(duì)于URI可選,一般情況下使用是不需要指定的,除非你需要的訪問(wèn)方式就是這樣的。
也可以使用UNIX域套接字路徑來(lái)定義,該路徑接在“unix”字符串后面,兩端由冒號(hào)所包圍,比如:
proxy_pass http://unix:/tmp/backend.socket:/uri/;
如果proxy_pass沒有使用URI,傳送到后端服務(wù)器的請(qǐng)求URI一般客戶端發(fā)起的原始URI,如果nginx改變了請(qǐng)求URI,則傳送的URI是nginx改變以后的完整規(guī)范化URI:
location /path/ { proxy_pass http://127.0.0.1; }
虛擬路徑代理就是,比如說(shuō)訪問(wèn)”http://127.0.0.1/path/uri”地址,當(dāng)匹配到這個(gè)location之后,通過(guò)”proxy_pass http://127.0.0.1/”代理到后端時(shí),一個(gè)新的URL就成了”http://127.0.0.1/uri”這樣。
其中的/path就稱為虛擬路徑,虛擬給用戶的,后端沒有真正的/path路徑,這里要特別注意”proxy_pass http://127.0.0.1/”最后的”/”,如果沒有這個(gè)”/”那么訪問(wèn)就會(huì)出現(xiàn)404,因?yàn)槟銢]有給proxy_pass定義URI,所以不存在將規(guī)范化以后的請(qǐng)求路徑(原始請(qǐng)求URI)與location配置中的路徑的匹配部分將被替換為proxy_pass指令中定義的URI這一說(shuō)法,切記。Nginx實(shí)現(xiàn)虛擬路徑代理
注意
當(dāng)使用一個(gè)正則表達(dá)式(~或~*)指定localtion時(shí),在這種情況下,proxy_pass應(yīng)該是一個(gè)沒有URI的指令,如果指定了URI,那么代理到后端時(shí),URI會(huì)被去掉,從而變成了http://127.0.0.1/some/path,也就是說(shuō)原始訪問(wèn)URI不會(huì)做任何改變傳送到后端。
還有一種情況,當(dāng)URI使用rwrite重寫指令后,在這種情況下,proxy_pass應(yīng)該是一個(gè)沒有URI的指令,如果指定了URI,那么代理到后端時(shí),URI會(huì)被去掉,從而變成了http://127.0.0.1/some/path。
rewrite /name/([^/]+) /users?name=$1 break;
最后,這種以代理的工作方式,一般都會(huì)使用到Nginx upstream,以此來(lái)做負(fù)載均衡。這種情況下直接給定一個(gè)upstream的名稱即可(需要先定義一個(gè)upstream),如下:
location / { upstream test{ 127.0.0.1:80; } proxy_pass http://test; }
13)proxy_next_upstream
Syntax: proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | non_idempotent | off ...; Default:proxy_next_upstream error timeout; Context:http, server, location
當(dāng)你使用Nginx proxy代理時(shí),如果是代理到后端是使用upstream,那么這個(gè)指令就是指定在何種情況下,一個(gè)失敗的請(qǐng)求應(yīng)該被發(fā)送到下一臺(tái)后端服務(wù)器,有如下指令:
error – 和后端服務(wù)器建立連接時(shí),或者向后端服務(wù)器發(fā)送請(qǐng)求時(shí),或者從后端服務(wù)器讀取響應(yīng)時(shí),出現(xiàn)錯(cuò)誤;
timeout – 和后端服務(wù)器建立連接時(shí),或者向后端服務(wù)器發(fā)送請(qǐng)求時(shí),或者從后端服務(wù)器讀取響應(yīng)時(shí),出現(xiàn)超時(shí);
invalid_header – 后端服務(wù)器返回空響應(yīng)或者非法響應(yīng)頭;
http_500 – 后端服務(wù)器返回的響應(yīng)狀態(tài)碼為500;
http_502 – 后端服務(wù)器返回的響應(yīng)狀態(tài)碼為502;
http_503 – 后端服務(wù)器返回的響應(yīng)狀態(tài)碼為503;
http_504 – 后端服務(wù)器返回的響應(yīng)狀態(tài)碼為504;
http_404 – 后端服務(wù)器返回的響應(yīng)狀態(tài)碼為404;
off – 關(guān)閉proxy_next_upstream功能—出錯(cuò)就選擇另一臺(tái)上游服務(wù)器再次轉(zhuǎn)發(fā)。
需要理解一點(diǎn)的是,只有在沒有向客戶端發(fā)送任何數(shù)據(jù)以前,將請(qǐng)求轉(zhuǎn)給下一臺(tái)后端服務(wù)器才是可行的。也就是說(shuō),如果在傳輸響應(yīng)到客戶端時(shí)出現(xiàn)錯(cuò)誤或者超時(shí),這類錯(cuò)誤是不可能恢復(fù)的。
另外Nginx1.7開始提供了將請(qǐng)求傳遞給下一臺(tái)服務(wù)器可以通過(guò)重試的次數(shù)和時(shí)間進(jìn)行限制。
14)proxy_next_upstream_timeout
Syntax: proxy_next_upstream_timeout time; Default:proxy_next_upstream_timeout 0; Context:http, server, location This directive appeared in version 1.7.5.
限制了重試請(qǐng)求可以被傳遞給下一臺(tái)服務(wù)器的時(shí)間,默認(rèn)值為0將關(guān)閉這一限制。
15)proxy_next_upstream_tries
Syntax: proxy_next_upstream_tries number; Default:proxy_next_upstream_tries 0; Context:http, server, location This directive appeared in version 1.7.5.
限制了重試請(qǐng)求可以被傳遞給下一臺(tái)服務(wù)器的次數(shù),默認(rèn)值為0將關(guān)閉這一限制。
16)proxy_read_timeout
Syntax: proxy_read_timeout time; Default:proxy_read_timeout 60s; Context:http, server, location
定義從后端服務(wù)器讀?。ń邮眨?shù)據(jù)的超時(shí)時(shí)間(Nginx從客戶端接收到請(qǐng)求,然后把數(shù)據(jù)包轉(zhuǎn)發(fā)到后端服務(wù)器,后端服務(wù)器處理完請(qǐng)求后返回給Nginx服務(wù)器,Nginx接收后端數(shù)據(jù)包稱為一次read),此超時(shí)是指相鄰兩次讀操作之間的最長(zhǎng)時(shí)間間隔,而不是整個(gè)響應(yīng)傳輸完成的最長(zhǎng)時(shí)間。如果后端服務(wù)器在超時(shí)時(shí)間段內(nèi)沒有傳輸任何數(shù)據(jù),連接將被關(guān)閉。默認(rèn)時(shí)間為60s,建議值為2-4s。
17)proxy_send_timeout
Syntax: proxy_send_timeout time; Default:proxy_send_timeout 60s; Context:http, server, location
定義向后端服務(wù)器發(fā)送一次數(shù)據(jù)包的超時(shí)時(shí)間(Nginx從客戶端接收到請(qǐng)求,然后把數(shù)據(jù)包轉(zhuǎn)發(fā)到后端服務(wù)器稱為一次send),此超時(shí)是指相鄰兩次寫操作之間的最長(zhǎng)時(shí)間間隔,而不是整個(gè)請(qǐng)求傳輸完成的最長(zhǎng)時(shí)間。
如果再向后端服務(wù)器發(fā)送數(shù)據(jù)包時(shí),超過(guò)了超時(shí)時(shí)間的設(shè)置,那么連接將被關(guān)閉。默認(rèn)時(shí)間為60s,建議值為2-4s。
18)proxy_set_header
Syntax: proxy_set_header field value; Default:proxy_set_header Host $proxy_host; proxy_set_header Connection close; Context:http, server, location
允許重新定義或者添加發(fā)往后端服務(wù)器的請(qǐng)求頭。value可以包含文本、變量或者它們的組合。 當(dāng)且僅當(dāng)當(dāng)前配置級(jí)別中沒有定義proxy_set_header指令時(shí),會(huì)從上面的級(jí)別繼承配置。 默認(rèn)情況下,只有兩個(gè)請(qǐng)求頭會(huì)被重新定義:
proxy_set_header Host $proxy_host; proxy_set_header Connection close;
如果不想改變請(qǐng)求頭“Host”的值,可以這樣來(lái)設(shè)置:
proxy_set_header Host $http_host;
但是,如果客戶端請(qǐng)求頭中沒有攜帶這個(gè)頭部,那么傳遞到后端服務(wù)器的請(qǐng)求也不含這個(gè)頭部。 這種情況下,更好的方式是使用$host變量——它的值在請(qǐng)求包含“Host”請(qǐng)求頭時(shí)為“Host”字段的值,在請(qǐng)求未攜帶“Host”請(qǐng)求頭時(shí)為虛擬主機(jī)的主域名:
proxy_set_header Host $host;
此外,服務(wù)器名可以和后端服務(wù)器的端口一起傳送:
proxy_set_header Host $host:$proxy_port;
如果某個(gè)請(qǐng)求頭的值為空,那么這個(gè)請(qǐng)求頭將不會(huì)傳送給后端服務(wù)器:
proxy_set_header Accept-Encoding "";
proxy_add_x_forwarded_for內(nèi)置變量,將proxy_add_x_forwarded_for內(nèi)置變量,將remote_addr變量值添加在客戶端“X-Forwarded-For”請(qǐng)求頭的后面,并以逗號(hào)分隔。
如果客戶端請(qǐng)求未攜帶“X-Forwarded-For”請(qǐng)求頭,proxy_add_x_forwarded_for變量值將與proxy_add_x_forwarded_for變量值將與remote_addr變量相同。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
19)proxy_pass_header
Syntax: proxy_pass_header field; Default:— Context:http, server, location
允許指定跳過(guò)某些字段從代理服務(wù)器到客戶端,一般用在CDN中傳回來(lái)的字段信息,但不需要傳回到后端服務(wù)器,就可以使用proxy_pass_header指令跳過(guò)。
20)proxy_redirect
Syntax: proxy_redirect default; proxy_redirect off; proxy_redirect redirect replacement; Default:proxy_redirect default; Context:http, server, location
設(shè)置后端服務(wù)器“Location”響應(yīng)頭和“Refresh”響應(yīng)頭的替換文本。 假設(shè)后端服務(wù)器返回的響應(yīng)頭是 “Location: http://localhost:8000/two/some/uri/”,那么指令
proxy_redirect http://localhost:8000/two/ http://frontend/one/;
將把字符串改寫為 “Location: http://frontend/one/some/uri/”。
實(shí)例
upstream appservers { zone appservers 64k; #默認(rèn)權(quán)重為1 server appserv1.example.com weight=5; server appserv2.example.com:8080 fail_timeout=5s slow_start=30s; server 192.0.2.1 max_fails=3; server reserve1.example.com:8080 backup; server reserve2.example.com:8080 backup; } server { location / { proxy_pass http://appservers; health_check; } location /upstream_conf { upstream_conf; allow 127.0.0.1; deny all; } }
參數(shù)解釋:
1.weight=number
設(shè)定服務(wù)器的權(quán)重,默認(rèn)是1。
2.max_fails=number
設(shè)定Nginx與服務(wù)器通信的嘗試失敗的次數(shù)。
3.fail_timeout=time
設(shè)定
默認(rèn)情況下,該超時(shí)時(shí)間是10秒。backup標(biāo)記為備用服務(wù)器。當(dāng)主服務(wù)器不可用以后,請(qǐng)求會(huì)被傳給這些服務(wù)器。
4.down
標(biāo)記服務(wù)器永久不可用,可以跟ip_hash指令一起使用。
5.route=string
設(shè)置服務(wù)器路由名稱。
后面會(huì)分享更多devops和DBA方面的內(nèi)容,感興趣的朋友可以關(guān)注一下~
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。