使用openresty替換線上nginx閘道器之openresty安裝細節

2023-06-03 15:00:27

背景

線上跑了多年的一個閘道器業務,隨著部門的拆分,逐漸有了一個痛點。該閘道器業務主要處理app端請求,app端發起的請求,採用http協定,post方法,content-type採用application/x-www-form-urlencoded,表單中有一個固定的欄位,叫功能號,即funcNo=1000100這樣,然後表單中其他業務欄位就根據funcNo的不同而各有不同。

後端有一個接入層nginx,nginx將請求轉給一個閘道器服務進行處理,閘道器會解析出funcNo欄位。我們的funcNo欄位也是有講究的,前4位元(1000)是系統編碼,後面3位(100)是介面編號。閘道器就根據系統編碼轉發給不同的系統進行處理。

閘道器採用的技術是早起的servlet容器,容器叫resin,我之前也沒有接觸過,總之就是類似於tomcat,不過輕量一些。

說起來,這個閘道器的職責和現在流行的接入層閘道器,比如現在的spring cloud gateway等,職責也差不太多。只是早期都是組態檔那種,把後端的各個系統全都先設定好;而不像現在的閘道器,會採用動態設定。

現在有啥問題呢?每次新上一個服務,都需要在這個服務中設定新服務的系統編碼、新服務的後端ip+埠等,當然了,具體細節更加複雜,它還需要設定新服務中的各個介面編號,以及各個介面需要使用哪些filter等。

總之,這個服務的程式碼架構還是穩定的,但是設定經常需要變更。

以前,這個服務歸技術部管,後來部門拆分了,多出一個部門B,這個服務還歸技術部管;但是部門B新上服務的話,就要來找技術部對這個服務加設定,變更上線。

我們就想著,讓部門B能自己上線是最好,免得總來找我們,因此,想到的辦法就是,將這個閘道器服務再部署一套給部門B用,然後前端nginx替換為openresty,openresty根據請求中的funcNo,判斷系統編碼屬於哪個部門,然後就把請求轉發到對應的閘道器服務。

大體思路

1、首先,將閘道器服務做成無狀態的,目前的閘道器,還使用了servlet session技術,是有狀態的,這一步目前已經做了修改,把使用servlet session的程式碼,換成了jwt,有待測試環境驗證,這個本篇先不講;

2、其次,開發環境模擬安裝openresty,屆時,使用openresty替換nginx。openresty相當於增強版本的nginx,可以寫lua程式碼,解析請求中的funcNo欄位,然後決定分發到哪個部門的閘道器服務。

本篇文章,主要是聚焦於第二個事情,即如何用openresty完美替換線上的nginx,說起來簡單,實則複雜,因為線上nginx執行多年,很多設定項,我們openresty屆時必須把線上nginx的設定也遷移過來才行,而且要保證功能都正常,不能說之前在nginx處理得好好的,到了openresty就不行了。

一、安裝openresty

官網推薦的安裝方式,為什麼不適用

我們首先要解決的是,線上伺服器,怎麼安裝openresty的問題。

一般來說,官網都是讓你用yum這類包管理器安裝,如下:

https://openresty.org/en/installation.html提到其提供了預編譯好的版本,yum安裝即可:

OpenResty® provides official pre-built packages for some of the common Linux distributions (Ubuntu, Debian, CentOS, RHEL, Fedora, OpenSUSE, Alpine, and Amazon Linux).

但是,由於線上伺服器都是沒有外網的,你想yum安裝是不行的(要麼就是yum中的版本太老了),所以,基本是採用rpm安裝。

rpm方式安裝,為什麼不適用

rpm安裝的話,那麼,如何獲取一個rpm包呢?

我們在開發環境機器上,可以利用yum來獲取rpm包。

先設定repo倉庫:

wget https://openresty.org/package/centos/openresty.repo
sudo mv openresty.repo /etc/yum.repos.d/

# update the yum index:
sudo yum check-update

然後,我們不執行yum install openresty,而是執行如下命令,將rpm包下載到指定目錄:

yum install --downloadonly --downloaddir=/root/mypackage/ openresty

下載的結果如下:

[root@server172 openresty-1.21.4.1]# ls /root/mypackage/*.rpm
/root/mypackage/openresty-1.21.4.1-1.el7.x86_64.rpm
/root/mypackage/openresty-pcre-8.45-1.el7.x86_64.rpm
/root/mypackage/openresty-openssl111-1.1.1s-1.el7.x86_64.rpm
/root/mypackage/openresty-zlib-1.2.13-1.el7.x86_64.rpm

下載了rpm後,進行rpm安裝:

cd /root/mypackage
rpm -ivh openresty-*

預設安裝目錄即在/usr/local/openresty下:

可以看到,其中在bin目錄下建立了一個連結檔案openresty,連結到openresty下的nginx可執行檔案,這裡也可以看出,openresty是基於nginx的增強。

我們執行下openresty -V,檢視這種安裝方式下的編譯選項:

格式化之後,如下:

[root@server172 openresty]# openresty -V
nginx version: openresty/1.21.4.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) 
built with OpenSSL 1.1.1n  15 Mar 2022 (running with OpenSSL 1.1.1s  1 Nov 2022)
TLS SNI support enabled
configure arguments: --prefix=/usr/local/openresty/nginx
 --with-cc-opt='-O2 -DNGX_LUA_ABORT_AT_PANIC -I/usr/local/openresty/zlib/include -I/usr/local/openresty/pcre/include
 -I/usr/local/openresty/openssl111/include'
 --add-module=../ngx_devel_kit-0.3.1 
 --add-module=../echo-nginx-module-0.62
 --add-module=../xss-nginx-module-0.06
 --add-module=../ngx_coolkit-0.2 
 --add-module=../set-misc-nginx-module-0.33 
 --add-module=../form-input-nginx-module-0.12 
 --add-module=../encrypted-session-nginx-module-0.09
 --add-module=../srcache-nginx-module-0.32
 --add-module=../ngx_lua-0.10.21
 --add-module=../ngx_lua_upstream-0.07 
 --add-module=../headers-more-nginx-module-0.33
 --add-module=../array-var-nginx-module-0.05
 --add-module=../memc-nginx-module-0.19
 --add-module=../redis2-nginx-module-0.15
 --add-module=../redis-nginx-module-0.3.9 
 --add-module=../ngx_stream_lua-0.0.11
 --with-ld-opt='-Wl,-rpath,/usr/local/openresty/luajit/lib 
 -L/usr/local/openresty/zlib/lib -L/usr/local/openresty/pcre/lib 
 -L/usr/local/openresty/openssl111/lib -Wl,-rpath,/usr/local/openresty/zlib/lib:/usr/local/openresty/pcre/lib:/usr/local/openresty/openssl111/lib' 
 --with-cc='ccache gcc -fdiagnostics-color=always' 
 --with-pcre-jit 
 --with-stream 
 --with-stream_ssl_module 
 --with-stream_ssl_preread_module 
 --with-http_v2_module 
 --without-mail_pop3_module 
 --without-mail_imap_module 
 --without-mail_smtp_module 
 --with-http_stub_status_module 
 --with-http_realip_module 
 --with-http_addition_module 
 --with-http_auth_request_module 
 --with-http_secure_link_module
 --with-http_random_index_module 
 --with-http_gzip_static_module 
 --with-http_sub_module 
 --with-http_dav_module
 --with-http_flv_module
 --with-http_mp4_module 
 --with-http_gunzip_module 
 --with-threads 
 --with-compat 
 --with-stream 
 --with-http_ssl_module

但是,我們還是不能用rpm方式安裝,因為,因為目前在跑的nginx,我支援是支援ipv6的,而上面rpm安裝的這個openresty,沒看到ipv6的選項啊。這種安裝方式,好像也沒辦法再去增加對其他模組的支援,如ipv6.

那隻能利用原始碼方式來安裝了。

原始碼方式安裝--獲取線上configure選項

要編譯,得拿到編譯選項,等到運維同事有空後,終於拿到了線上nginx的設定:

線上一共兩臺nginx機器,在第一臺執行nginx -V檢視設定:

nginx version: nginx/1.16.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) 
built with OpenSSL 1.1.1d  10 Sep 2019
TLS SNI support enabled
configure arguments: --prefix=/opt/software/nginx --with-http_stub_status_module --with-stream --with-http_ssl_module --with-http_realip_module --with-ipv6 --add-module=./nginx-http-concat-master --with-openssl=/opt/software/openssl-1.1.1d --with-pcre=/opt/software/pcre-8.10

第二臺執行後,檢視設定:

nginx version: nginx/1.12.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC) 
built with OpenSSL 1.0.2k  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/opt/software/nginx --with-http_stub_status_module --with-stream --with-http_ssl_module --with-http_realip_module --add-module=./nginx-http-concat-master --with-openssl=/opt/software/openssl-1.0.2k --with-pcre=/opt/install/pcre-8.10

兩臺的差別,主要是:

  • nginx版本不一致

  • --with-openssl中ssl路徑不同

  • 第一臺多了--with-ipv6選項

ok,編譯選項拿到了,接下來,就開始準備原始碼編譯。

原始碼方式安裝--原始碼準備

先下載openresty原始碼包(自己在官網看檔案找):

wget https://openresty.org/download/openresty-1.21.4.1.tar.gz

tar -xvf openresty-1.21.4.1.tar.gz 

cd openresty-1.21.4.1/

另外,由於線上nginx的如下編譯選項還依賴了openssl、pcre、nginx-http-concat-maste等,而當時也沒注意要運維同事幫我們把這些線上目錄拿下來,所以,我們還要在網上找下這些模組的原始碼。

--add-module=./nginx-http-concat-master --with-openssl=/opt/software/openssl-1.0.2k --with-pcre=/opt/install/pcre-8.10

新增的第三方模組原始碼:

openssl:

wget --no-check-certificate https://www.openssl.org/source/openssl-1.1.1d.tar.gz
tar -zxvf openssl-1.1.1d.tar.gz 
解壓後存放到:
/root/mypackage/openresty-source/openssl-1.1.1d 

pcre:

wget https://sourceforge.net/projects/pcre/files/pcre/8.10/pcre-8.10.tar.gz/download --no-check-certificate
tar -zxvf pcre-8.10.tar.gz
解壓後存放到:
/root/mypackage/openresty-source/pcre-8.10

nginx-http-concat:

https://github.com/alibaba/nginx-http-concat
/root/mypackage/openresty-source/nginx-http-concat-master
解壓後存放到:
/root/mypackage/openresty-source/nginx-http-concat-master

準備好上面的依賴模組後,編輯好下面的命令:

./configure --with-http_stub_status_module --with-stream --with-http_ssl_module --with-http_realip_module --with-ipv6  --with-openssl=/root/mypackage/openresty-source/openssl-1.1.1d --with-pcre=/opt/software/pcre-8.10  --add-module=/root/mypackage/openresty-source/nginx-http-concat-master

configure完成後,發現:

其中提示--with-ipv6選項已經過期,這個不影響,因為nginx的高版本已經預設開啟了ipv6。

那是不是意味著直接用rpm的方式安裝也可以呢,這個呢,反正就是看rpm方式安裝出來的nginx -V的編譯選項,到底是不是包含了線上nginx的全部選項,如果是的話,也可以。我這邊就還是用原始碼方式算了,更靈活一些。

執行make,可能需要等待幾分鐘
make

make完成後,make install即可。

make install

完成後,即在/opt/software/openresty目錄下安裝好了,切換到bin目錄下,裡面有可執行openresty,其實就是個指向/opt/software/openresty/nginx/sbin/nginx的軟連線。

二、準備openresty設定

大體思路

這一步的大體思路,就是找運維同事拿到線上的nginx設定,然後根據這份設定,來修改openresty的設定。

這塊有個簡單的方式,使用beyond compare進行對比(nginx.conf),看看有哪些差異,然後設定成和線上一致,測試openresty即可。

線上陌生nginx設定解讀

with-http_stub_status_module

原始檔就不看了,這裡說下看到的陌生的設定。看到nginx.conf裡http塊下有:

    #設定監控nginx狀態URL
    location /__nginxstatus
    {
       stub_status on;
       access_log off;
    }

請求這個介面,發現響應如下:

看起來,就是一些狀態資訊,不知道是拿來幹啥的,難道是健康監測?

我網上查了下,原來這個不是nginx預設模組,是需要在configure的時候,使用如下模組才有的:

./configure  --with-http_stub_status_module

參考檔案如下:

http://nginx.org/en/docs/http/ngx_http_stub_status_module.html

https://blog.redis.com.cn/doc/index.html

https://blog.redis.com.cn/doc/optional/stubstatus.html

with-http_realip_module

這個模組也線上上nginx的configure中出現了。

這個模組的作用是,有時候nginx是部署在l5這類硬體負載均衡軟體後的,nginx正常獲取使用者端ip的話,那可能就拿到的是l5的ip,要怎麼獲取真實使用者端的ip呢?

一般l5這種,會在請求nginx時,往header里加一個X-Real-IP或者X-Forwarded-For這樣的header,裡面的value就是使用者端的真實ip。

所以,nginx啟用這個模組後,就會從這些header裡獲取使用者端ip。

http://nginx.org/en/docs/http/ngx_http_realip_module.html

https://blog.redis.com.cn/doc/optional/realip.html

nginx-http-concat

這個模組在configure裡有,是淘寶開發的,https://github.com/alibaba/nginx-http-concat。

用途是,當nginx作為一個靜態檔案伺服器時,如使用者端請求1.js、2.js,就會是兩個請求和響應,當檔案多的時候,比較耗效能。

所以,nginx開啟這個模組後,支援如下方式存取:http://host:80/??1.js,2.js,此時,該模組就可以一把拿到1.js、2.js,合併後返回給使用者端。

效果如下(來自於https://blog.csdn.net/qq_34556414/article/details/105892602):

# curl http://www.ttlsa.com/static/??css/ttlsa_concat.css,css/a.css
/** this is css ttlsa_concat.css **/
/** this is css a.css *

但我在組態檔中,沒找到相關的設定項,不知道是不是線上從未使用過這個功能。

ssl支援

#開啟SSL支援
ssl     on;
ssl_certificate              test.crt;
ssl_certificate_key          test.key;
ssl_session_timeout          5m;
ssl_protocols                 TLSv1 TLSv1.1 TLSv1.2;

ssi (Server Side Include)

ssi on;
ssi_silent_errors on;
ssi_types text/shtml;	

應該是沒在使用了,老掉牙的技術了,有興趣隨便看看:

https://blog.csdn.net/qq_33616529/article/details/79061608

https://cloud.tencent.com/developer/article/1915087

gzip

gzip  on;
gzip_min_length  1K;
gzip_buffers     4 8k;
gzip_types       text/* text/css application/javascript application/x-javascript application/xml;

gzip_comp_level  9;
gzip_proxied     any;
gzip_vary        on;
gzip_http_version 1.1;

參考檔案:

http://nginx.org/en/docs/http/ngx_http_gzip_module.html

三、驗證功能

這部分就主要包括,https驗證、功能驗證,具體就不展開了,和業務強相關。如果一切驗證ok,就可以準備上線了。