最近在做一個需求開發:根據請求后的不同,nginx將請求分發到不同的后端服務;需要修改kubernetes的ingress-nginx-controller的源碼,調試的時候遇到了挺多問題,寫出來,有需要的老鐵可以參考。具體方案就不說了,只說一下nginx配置這一塊。
首先貼出組件版本:
ingress-nginx-controller的版本為0.9-beta.18,可以在github上找到開源的項目源碼:
nginx map配置根據請求頭不同分配流量到不同后端服務
nginx版本為:nginx version: nginx/1.13.7
map配置的一個報錯:
nginx.conf文件部分如下:
http {
include /etc/nginx/conf.d/server-map.d/*-map.conf;
include /etc/nginx/conf.d/*-upstream.conf;
include /etc/nginx/conf.d/server-map.d/*-server.conf;
....
map_hash_bucket_size 64;
....
}
在/etc/nginx/conf.d/server-map.d/目錄下的flow-ppp-map.conf:
map $http_x_group_env $svc_upstream {
default zxl-test-splitflow-old-version;
~*old zxl-test-splitflow-old-version;
~*new zxl-test-splitflow-new-version;
}
flow-ppp-server.conf
server {
listen 8998;
server_name aa.hc.harmonycloud.cn;
location /testdemo/test {
proxy_pass http://$svc_upstream;
}
}
ingressgroup-upstream.conf
upstream zxl-test-splitflow-old-version {
server 10.168.173.29:8080 max_fails=0 fail_timeout=0;
}
upstream zxl-test-splitflow-new-version {
server 10.168.177.171:8080 max_fails=0 fail_timeout=0;
}
當nginx -tc /etc/nginx/nginx.conf測試配置正確與否時報錯如下:
Error: exit status 1
nginx: [emerg] "map_hash_bucket_size" directive is duplicate in /etc/nginx/nginx.conf:60
nginx: configuration file c test failed
解決:
這是因為首次調用map時會隱式設置map_hash_bucket_size,即在nginx中map后寫map_hash_bucket_size相當于設置了兩次map_hash_bucket_size,如:
http {
...
map $status $_status {
default 42;
}
map_hash_bucket_size 64;
...
}
因此可以在map之前設置它,如下所示。
http {
map_hash_bucket_size 64;
...
map $status $_status {
default 42;
}
...
}
所以include map配置也應該放到設置map_hash_bucket_size之后:
http {
...
map_hash_bucket_size 64;
...
include /etc/nginx/conf.d/server-map.d/*-map.conf;
include /etc/nginx/conf.d/*-upstream.conf;
include /etc/nginx/conf.d/server-map.d/*-server.conf;
}
map配置說明:
通過上面的include三個配置文件,最終對nginx生效的配置應該是這樣的:
http {
...
map_hash_bucket_size 64;
...
map $http_x_group_env $svc_upstream {
default zxl-test-splitflow-old-version;
~*old zxl-test-splitflow-old-version;
~*new zxl-test-splitflow-new-version;
}
upstream zxl-test-splitflow-old-version {
server 10.168.173.29:8080 max_fails=0 fail_timeout=0;
}
upstream zxl-test-splitflow-new-version {
server 10.168.177.171:8080 max_fails=0 fail_timeout=0;
}
server {
listen 8998;
server_name aa.hc.harmonycloud.cn;
location /testdemo/test {
proxy_pass http://$svc_upstream;
}
}
}
當在電腦上hosts文件里配置了aa.hc.harmonycloud.cn域名解析后,訪問http://aa.hc.harmonycloud.cn:8998/testdemo/test時(即server的server_name和listen、location的配置),nginx將會把請求轉發到http://$svc_upstream,這個$svc_upstream具體是什么,就是通過map配置來賦值的。這里map配置如下:
map $http_x_group_env $svc_upstream {
default zxl-test-splitflow-old-version;
~*old zxl-test-splitflow-old-version;
~*new zxl-test-splitflow-new-version;
}
其中$http_x_group_env可以是nginx內置變量,也可以是自定義的header的key、請求參數名;$svc_upstream即為自定義變量名。這里的配置含義為:當請求頭里的x-group-env的值old時,$svc_upstream被賦值為zxl-test-splitflow-old-version;當請求頭里的x-group-env的值new時,$svc_upstream被賦值為zxl-test-splitflow-new-version;默認賦值為zxl-test-splitflow-old-version;
(其中正則表達式如果以 “~” 開頭,表示這個正則表達式對大小寫敏感。以 “~*”開頭,表示這個正則表達式對大小寫不敏感)。而zxl-test-splitflow-new-version和zxl-test-splitflow-old-version表示兩個upstream名稱。
因此nginx將會把請求轉發到http://$svc_upstream,這里的$svc_upstream會被替換為upstream的名稱,最終將得到upstream中的后端服務IP和Port。
注意:如果我們自定義header為X-Real-IP,通過第二個nginx獲取該header時需要這樣:$http_x_real_ip; (一律采用小寫,而且前面多了個http_,且中間用_替換)
測試
當請求頭里加x-group-env為new時,訪問后端打印出的是I am new version
當請求頭里加x-group-env為old時,訪問后端打印出的是I am old version
最終通過請求頭不同實現了將流量分配到不同的后端服務。
將請求頭的key變為X-Group-Env,value變為OLD或者NEW也沒關系: