在前一篇測試中, 我用獨立的站台 hello 取代預設站台 default 來執行一個 Flask 網頁應用程式 hello.py, 如果網站有多個 app 要執行, 是不是要為每個 app 創建一個站台呢? 對於相同的網域來說這是行不通的, 會有域名衝突 (server name conflicting) 問題.
如果在 /etc/nginx/sites-enabled/ 底下有兩個網站設定檔 server name 相同 (例如 tony1966.cc 與 www.tony1966.cc), 也都是監聽 80 埠, 則在用 sudo nginx -t 檢測全部網站設定檔時會出現域名衝突的警告, 例如 :
ony1966@LX2438:~$ sudo nginx -t
nginx: [warn] conflicting server name "tony1966.cc" on 0.0.0.0:80, ignored
這是因為 Nginx 不允許多個 server 區塊 (Context blocks) 使用相同的 server_name (例如 tony1966.cc) 且監聽相同的埠之故. 這時 Nginx 會以特定規則 (如果域名與埠號雷同, 就以設定檔名稱的字典順序排序後尋找第一個) 選擇其中一個網站設定檔使其生效, 其他相衝突的設定檔無效 (因此這些站台不會被啟動). 總之, Nginx 網站設定檔 server 區塊的 server_name 與 listen 組合必須是獨一無二的.
如果要在同一域名下執行多個 web app, 解決方案是在網站設定檔中使用子路徑 (URL path), 也就是用 location 區塊為各個 app 指定不同的 URL 路徑, 並以 proxy_pass 指令將對此 URL 的請求轉發到上游的應用伺服器 (例如 Gunicorn 或 uWSGI), 例如 :
server { # 區塊
listen 80; # 監聽 80 埠 (HTTP) 的指令
server_name tony1966.cc www.tony1966.cc; # 域名的指令
# 第一個應用程式:app1.py
location /app1/ { # 位置區塊 1 : URL 路徑 : tony1966.cc/app1/
proxy_pass http://127.0.0.1:8080/; # 轉發至 Gunicorn 伺服器
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 第二個應用程式:app2.py
location /app2/ { # 位置區塊 2 : URL 路徑 : tony1966.cc/app2/
proxy_pass http://127.0.0.1:8081/; # 轉發至 Gunicorn 伺服器
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
此例會將對 tony1966.cc/app1/ 的請求轉發至 Gunicorn 的 http://127.0.0.1:8080/ 執行監聽該埠之應用程式; 將 tony1966.cc/app2/ 的請求轉發至 Gunicorn 的 http://127.0.0.1:8081/ 執行監聽該埠之應用程式.
本篇旨在前一篇執行 hello.py 基礎上新增另一個應用程式 hello2.py, 透過修改站台設定檔 location 指令的 URL 路徑來執行這兩個 web app.
1. 建立 Flask 網頁應用程式 hello2.py :
先切換到 flask_apps 資料夾, 用 nano 編輯一個 hello2.py, 把之前的 hello.py 內容複製過來, 將英文回應改成中文回應, 並將測試用的開發伺服器監聽埠改為 8081 :
tony1966@LX2438:~$ cd flask_apps
tony1966@LX2438:~/flask_apps$ nano hello2.py
# hello2.py
from flask import Flask
app = Flask(__name__)
app.debug=True
@app.route("/")
def hello():
return "<h1>歡迎來到我的網站!</h1>"
@app.route("/hello/<name>")
def hello_name(name):
return "<h1>您好, %s</h1>" % name
if __name__ == "__main__":
app.run(host='127.0.0.1', port=8081) # 啟動 Werkzeug 開發伺服器
然後用 python3 執行此 app :
tony1966@LX2438:~/flask_apps$ python3 hello2.py
* Serving Flask app 'hello2'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:8081
Press CTRL+C to quit
* Restarting with watchdog (inotify)
* Debugger is active!
* Debugger PIN: 650-986-211
127.0.0.1 - - [11/Jul/2025 16:02:20] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [11/Jul/2025 16:02:20] "GET /favicon.ico HTTP/1.1" 404 -
127.0.0.1 - - [11/Jul/2025 16:02:37] "GET /hello/Tony HTTP/1.1" 200 -
用瀏覽器拜訪 127.0.0.1:8081 與 127.0.0.1:8081/hello/Tony 可看到網頁結果如下 :
2. 啟動 Gunicorn 程序執行 hello2.py :
在前一篇測試中我們已啟動 Gunicorn 來監聽 127.0.0.1:8080 埠, 只要 Nginx 轉發 HTTP 請求就會執行 hello.py 來產生回應. 可用下列指令查詢目前執行中的 Gunicorn 程序 :
tony1966@LX2438:~/flask_apps$ ps aux | grep gunicorn
tony1966 223187 0.0 0.2 34156 9840 ? Ss Jul06 4:03 gunicorn: master [hello:app]
tony1966 223192 0.0 0.6 42348 24688 ? S Jul06 0:25 gunicorn: worker [hello:app]
tony1966 223193 0.0 0.6 42344 24492 ? S Jul06 0:25 gunicorn: worker [hello:app]
tony1966 223194 0.0 0.6 42344 24612 ? S Jul06 0:25 gunicorn: worker [hello:app]
tony1966 223195 0.0 0.6 42344 24640 ? S Jul06 0:25 gunicorn: worker [hello:app]
tony1966 413781 0.0 0.0 9136 1712 pts/0 S+ 11:07 0:00 grep --color=auto gunicorn
接下來用下列指令啟動 Gunicorn 伺服器監聽 127.0.0.1 的 8081 埠, 當收到請求時就執行 hello2.py 的 app 物件 :
tony1966@LX2438:~/flask_apps$ gunicorn -w 4 -b 127.0.0.1:8081 hello2:app
[2025-07-14 15:32:39 +0800] [418476] [INFO] Starting gunicorn 23.0.0
[2025-07-14 15:32:39 +0800] [418476] [INFO] Listening at: http://127.0.0.1:8081 (418476)
[2025-07-14 15:32:39 +0800] [418476] [INFO] Using worker: sync
[2025-07-14 15:32:39 +0800] [418477] [INFO] Booting worker with pid: 418477
[2025-07-14 15:32:39 +0800] [418478] [INFO] Booting worker with pid: 418478
[2025-07-14 15:32:39 +0800] [418479] [INFO] Booting worker with pid: 418479
[2025-07-14 15:32:39 +0800] [418480] [INFO] Booting worker with pid: 418480
下列指令可確認 Gunicorn 正在監聽 8081 埠 :
tony1966@LX2438:~$ sudo lsof -i :8081
[sudo] tony1966 的密碼:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
gunicorn: 418476 tony1966 5u IPv4 5927413 0t0 TCP localhost:tproxy (LISTEN)
gunicorn: 418477 tony1966 5u IPv4 5927413 0t0 TCP localhost:tproxy (LISTEN)
gunicorn: 418478 tony1966 5u IPv4 5927413 0t0 TCP localhost:tproxy (LISTEN)
gunicorn: 418479 tony1966 5u IPv4 5927413 0t0 TCP localhost:tproxy (LISTEN)
gunicorn: 418480 tony1966 5u IPv4 5927413 0t0 TCP localhost:tproxy (LISTEN)
目前 Gunicorn 共有兩組程序分別監聽 8080 與 8081 埠了 :
tony1966@LX2438:~$ ps aux | grep gunicorn
tony1966 223187 0.0 0.2 34156 9192 ? Ss Jul06 4:09 gunicorn: master [hello:app]
tony1966 223192 0.0 0.6 42348 24424 ? S Jul06 0:26 gunicorn: worker [hello:app]
tony1966 223193 0.0 0.6 42344 24224 ? S Jul06 0:26 gunicorn: worker [hello:app]
tony1966 223194 0.0 0.6 42344 24308 ? S Jul06 0:26 gunicorn: worker [hello:app]
tony1966 223195 0.0 0.6 42344 24316 ? S Jul06 0:26 gunicorn: worker [hello:app]
tony1966 418476 0.0 0.5 34156 20248 pts/0 S+ 15:32 0:01 gunicorn: master [hello2:app]
tony1966 418477 0.0 0.6 41156 26008 pts/0 S+ 15:32 0:00 gunicorn: worker [hello2:app]
tony1966 418478 0.0 0.6 41156 25984 pts/0 S+ 15:32 0:00 gunicorn: worker [hello2:app]
tony1966 418479 0.0 0.6 41156 26004 pts/0 S+ 15:32 0:00 gunicorn: worker [hello2:app]
tony1966 418480 0.0 0.6 41156 26024 pts/0 S+ 15:32 0:00 gunicorn: worker [hello2:app]
tony1966 419855 0.0 0.0 9136 1700 pts/1 S+ 16:01 0:00 grep --color=auto gunicorn
3. 修改站台設定檔 hello :
用 nano 編輯前一篇測試的站台設定檔 hello, 加入第二個 location 指令, 將對 https://tony1966.cc/hello2/ 的請求轉發至 127.0.0.1 的 8081 埠 :
tony1966@LX2438:~$ sudo nano /etc/nginx/sites-available/hello
[sudo] tony1966 的密碼:
server {
server_name tony1966.cc www.tony1966.cc;
location / {
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /hello2/ {
proxy_pass http://127.0.0.1:8081/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/tony1966.cc/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/tony1966.cc/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = www.tony1966.cc) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = tony1966.cc) {
return 301 https://$host$request_uri;
} # managed by Certbot
}
其中 location / 根目錄路徑是轉發到前一篇的 127.0.0.1:8080 來執行 hello.py; 藍色字的部分為新增的 URL 子路徑, 會將對 /hello2/ 子路徑的請求轉發到 127.0.0.1:8081 來執行 hello2.py.
然後用 nginx -t 檢查所有站台設定檔 :
tony1966@LX2438:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
沒有問題就重啟 Nginx 伺服器 :
tony1966@LX2438:~$ sudo systemctl reload nginx
這樣便完成所有設定, 用瀏覽器連線 https://tony1966.cc 與 https://tony1966.cc/hello2 即可分別執行不同的應用程式 hello.py 與 hello2.py :



沒有留言 :
張貼留言