最近我們搭了一個consul服務,開發同學想要把supervisor的web管理內建到consul中,需要根據url中給定的ip位址動态的加載該機器上的supervisor管理界面,因為服務端機器都在VPC内部,辦公網網絡不可達,是以不能簡單的rewrite url或者做個重定向來解決,是以需要通過反向代理的方式将請求轉發給後端機器,并且要實作反向代理伺服器的動态更改,由于邏輯比較複雜,是以就用lua腳本來實作了
大緻邏輯是這樣的
- upstream擷取
- 因為我們的consul服務通過域名通路,鍊到supervisor時,url中會有具體的upstream Ip,是以會先從url中看是否能match到ip,這樣可以拿到upstream,并傳回supervisor首頁資訊;
- 當做tailf,start, stop 操作時,因為頁面是從supervisor頁鍊過去的,是以可以通過http_referer header來match upstream ip
- uri擷取
- 當拿到upstream後,從request_uri做字元串截取來拿到uri
- 然後一些css樣式,image可以從本機擷取
具體實作如下:
location /supervisor/ {
set $query_uri "";
#一開始從request_uri中把/supervisor/字首去掉
if ($request_uri ~* "/supervisor/(.*)") {
set $query_uri $1;
}
set $upstreamhost "";
rewrite_by_lua_block {
ngx.var.upstreamhost = string.match(ngx.var.query_uri,"%d+.%d+.%d+.%d+")
if ngx.var.upstreamhost == nil then
ngx.var.upstreamhost = string.match(ngx.var.http_referer,"%d+.%d+.%d+.%d+")
end
index_start,index_end = string.find(ngx.var.query_uri,ngx.var.upstreamhost)
if index_end ~= nil then
tmp_uri = string.sub(ngx.var.query_uri,index_end+1)
ngx.var.query_uri = tmp_uri
end
}
proxy_pass http://$upstreamhost:8555/$query_uri;
proxy_redirect http://$upstreamhost:8555 http://consul.url/supervisor/$upstreamhost;
}
location /supervisor/images/ {
if ($request_uri ~* "/supervisor/(.*)") {
proxy_pass http://127.0.0.1:8555/$1;
}
}
location /supervisor/stylesheets/ {
if ($request_uri ~* "/supervisor/(.*)") {
proxy_pass http://127.0.0.1:8555/$1;
}
}
因為在supervisor管理頁面clear log的時候,伺服器會傳回Location header,是以通過proxy_redirect directive來做個重定向,讓client(浏覽器)還是走consul.server來發送請求,進而避免網絡不可達問題
這樣從本地辦公網通路
http://consul.url/supervisor/10.10.10.12
就可以不用登陸伺服器對服務進行啟停及檢視日志等操作了
pprof內建
同樣再加pprof(go的性能調試工具)的時候,就可以複用這個邏輯了(因為go的pprof端口不一緻,是以在url中給了ip:port, match的時候也多改成了"%d+.%d+.%d+.%d+:%d+")
location /debug/pprof/ {
set $query_uri "";
if ($request_uri ~* "/debug/pprof/(.*)") {
set $query_uri $1;
}
set $upstreamhost "";
rewrite_by_lua_block {
ngx.var.upstreamhost = string.match(ngx.var.query_uri,"%d+.%d+.%d+.%d+:%d+")
if ngx.var.upstreamhost == nil then
ngx.var.upstreamhost = string.match(ngx.var.http_referer,"%d+.%d+.%d+.%d+:%d+")
end
index_start,index_end = string.find(ngx.var.query_uri,ngx.var.upstreamhost)
if index_end ~= nil then
tmp_uri = string.sub(ngx.var.query_uri,index_end+1)
ngx.var.query_uri = tmp_uri
end
}
proxy_pass http://$upstreamhost/debug/pprof/$query_uri;
}
這樣開發從辦公環境通路
http://consul.url/pprof/10.10.10.12:203
就可以對測試、開發環境的go程式進行簡單的監控了
改進: ip全部通過url擷取
有時開發希望通過指令行直接去看某台機器的pprof debug資訊,為了友善粘貼浏覽器的url,是以希望能把host位址包含進請求url,是以做了如下改進
location /debug/pprof/ {
set $query_uri "";
if ($request_uri ~* "/debug/pprof/(.*)") {
set $query_uri $1;
}
set $upstreamhost "";
rewrite_by_lua_block {
ngx.var.upstreamhost = string.match(ngx.var.query_uri,"%d+.%d+.%d+.%d+:%d+")
if ngx.var.upstreamhost == nil then
ngx.var.upstreamhost = string.match(ngx.var.http_referer,"%d+.%d+.%d+.%d+:%d+")
end
index_start,index_end = string.find(ngx.var.query_uri,ngx.var.upstreamhost)
if index_end ~= nil then
tmp_uri = string.sub(ngx.var.query_uri,index_end+2)
ngx.var.query_uri = tmp_uri
end
local new_uri = "/debug/pprof/"..ngx.var.upstreamhost.."/"..ngx.var.query_uri
--ngx.redirect(new_uri,301)
ngx.req.set_uri(new_uri)
}
proxy_pass http://$upstreamhost/debug/pprof/$query_uri;
}
這樣從
http://consul.url/pprof/10.10.10.12:203
傳回的連結檢視goroutine資訊的時候,浏覽器會跳轉到
http://consul.url/pprof/10.10.10.12:203/goroutine?debug=1
關于多餘代碼
如果set_uri了,那麼下面這句代碼看似就顯得多餘了
if ngx.var.upstreamhost == nil then
ngx.var.upstreamhost = string.match(ngx.var.http_referer,"%d+.%d+.%d+.%d+:%d+")
end
但是浏覽器有個不好的地方就是會緩存重定向,而且單純的disable cache不管用,因為之前都是跳到
http://consul.url/pprof/goroutine?debug=1
, 是以即使set_uri, 浏覽器仍然不會跳轉到新的url,隻有先301重定向破壞它的‘記憶’後才可以正常跳轉