1. 程式人生 > >[問題記錄]按url context path設定nginx反向代理禪道和Jenkins

[問題記錄]按url context path設定nginx反向代理禪道和Jenkins

問題
在公司同一臺伺服器上分別裝了禪道的開源版(linux一鍵安裝)和Jenkins(docker),覺得帶埠號的連結給上頭leader等使用有點難看……就想改成用二級url區別的方法,即目標是:

由 dev.server.com:8001 改成用 dev.server.com/zentao   訪問禪道
由 dev.server.com:8002 改成用 dev.server.com/jenkins  訪問Jenkins

然後簡單搜尋了下、寫出了下面的(有錯誤的)nginx配置:

 server {
    listen 80;
    server_name dev.server.com;  
    location /zentao {
        proxy_pass http://localhost:8001;
} location /jenkins { proxy_pass http://localhost:8002; } }

按上面/zentao、/jenkins這樣的新路徑,禪道一下子就打開了使用者登入介面,但是Jenkins卻是404。

原因
原本以為Jenkins不能用很奇怪,最後發現禪道能用只是碰巧……

為什麼打不開Jenkins?
首先發現如果使用埠號訪問的方式、瀏覽器會自動跳轉至Jenkins登入介面、url發生了變化,於是聯想到比較下不同的url訪問,整理下是:

(1) GET http://dev.server.com/jenkins 跳轉至 http://dev.server
.com/login?from=%2Fjenkins 404 NOT FOUND (2) GET http://dev.server.com:8002 跳轉至 http://dev.server.com:8002/login?from=%2F 200 OK 顯示登入介面 (3) GET http://dev.server.com:8002/login 直接 200 OK 顯示登入介面 (4) GET http://dev.server.com:8002/jenkins 跳轉至 http://dev.server.com:8002/login?from=%2Fjenkins 200 OK 顯示登入介面

於是可以知道Jenkins登入介面的訪問url是ip:port/login

,後面的from query string只是將請求url中的path貼在了後面(%2F/)。所以nginx配置不能用的原因也很明顯,Jenkins監聽的是8002埠,加了nginx proxy_pass後變成了請求預設的80埠,自然是找不到/找不對的。

為什麼能開啟禪道?
這個是幾方面的巧合:
首先如果用最上面的請求連結http://dev.server.com/zentao、沒有以/結尾,按前面的配置nginx會返回301重定向http://dev.server.com/zentao/

In response to a request with URI equal to this string, but without the trailing slash, a permanent redirect with the code 301 will be returned to the requested URI with the slash appended

然後proxy_pass的目標url http://localhost:8001也沒有以/結尾,nginx會將請求路徑path拼至目標url的後面、即變成請求http://localhost:8001/zentao/

If proxy_pass is specified without a URI, the request URI is passed to the server in the same form as sent by a client when the original request is processed, or the full normalized request URI is passed when processing the changed URI

然而使用dev.server.com:8001訪問時、其實顯示的是禪道的歡迎介面、可以選“開源版”或“專業版 試用”;選開源版、使用者登陸介面的url樣子是:http://dev.server.com:8001/zentao/user-login.html;登入之後、所有的頁面操作也都在http://dev.server.com:8001/zentao/的子級url下。

所以這個似乎上來就能用的nginx配置、其實是經過一些預設轉換後、恰好符合了禪道開源版的url mapping規則。如果將nginx的map規則改成location /zbox,以http://dev.server.com/zbox訪問時、轉換出來的路徑應該是http://localhost:8001/zbox/,那結果就應該是404(這裡有問題、測試下來以http://dev.server.com/zbox訪問並不會自動重定向、而是直接404=>其他1)。

解決方法:
比較一下正常使用時的jenkins頁面和禪道頁面,禪道自身的url、a標籤href、script src、image src等都是在/zentao目錄下,但jenkins/docker就沒有這樣的設計。所以禪道可以很方便的達到dev.server.com/zentao這樣的反向代理,但Jenkins就需要另外的考慮。

Jenkins:
一種思路是使用nginx在jenkins返回的所有資源連結前面都加上/jenkins欄位,這樣使用者接下來產生的GET請求都可以map到location /jenkins區塊。要實現這個功能需要用到nginx的ngx_http_sub_module。參考Example,可以寫出類似配置:

location /jenkins {
    sub_filter '<a href="/'  '<a href="/jenkins/';
    sub_filter '<img src="/' '<img src="/jenkins/';
    # ...其他替換規則...
    sub_filter_once off;  # 查詢並替換多次
    proxy_pass http://localhost:8002;
}

但是這樣做效率很低、且filter遺漏和出錯可能性大(比如=>其他2),所以最後還是妥協、使用了重定向:

location /jenkins {
    return 302 http://dev.server.com:8002; 
}

這樣至少使http://dev.server.com/jenkins這個(間接)訪問連結可用。

如果條件允許的話,應該是分配一個子域名比如jenkins.server.com會更合理和方便,這時nginx設定可以參考jenkins官方的wiki:Jenkins behind an NGinX reverse proxy
再或者按官方wiki的提醒、直接使用jenkins.war放到web容器裡跑,這樣也就能保證有/jenkins context path。

禪道:
雖然開頭寫成那樣也可以用、考慮了下還是寫的更明確一點:

location /zentao/ {
    proxy_pass http://localhost:8001/zentao/;
}

其他:

  1. nginx location的匹配string最後要不要加斜槓/
    這裡還是很搞不清楚。
    實測下來,Jenkins使用重定向時,nginx location的匹配欄位如果是/jenkins/,則http://dev.server.com/jenkins的請求結果是404;然而禪道配置這邊,nginx location和http請求裡、似乎/zentao/zentao/任意組合使用都是可以的,但是換成/zbox/aaa等其他不存在的path、則連301重定向都沒有、而是直接報404…有點混亂就先不管了,有空看看文件再說吧。

  2. Jenkins的“跳轉”顯示登入頁面是怎麼發生的?
    同時用postman GET jenkins測試例子裡(3)以外的url,可以看到返回的http狀態是403 FORBIDDEN,訊息體html裡有類似的:

    <head>
        <meta http-equiv='refresh' content='1;url=/login?from=%2F'/>
        <script>window.location.replace('/login?from=%2F');</script>
    </head>

    location.replace引用MDN

    The Location.replace() method replaces the current resource with the one at the provided URL. The difference from the assign() method is that after using replace() the current page will not be saved in session History, meaning the user won’t be able to use the back button to navigate to it.

    Chrome console的話要勾選preserve log才能抓到403的返回狀態,但html還是看不到…

  3. context path?
    contextPath是java servlet的說法,一般就是指http://example.com/aaa/bbb/ccc.html/aaa這個欄位。