天天看點

nginx-通過lua動态更改upstream

最近我們搭了一個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重定向破壞它的‘記憶’後才可以正常跳轉

繼續閱讀