nginx-通過lua動態更改upstream

巨集量發表於2018-07-16

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


相關文章