2025年7月15日 星期二

Mapleboard MP510-50 測試 (二十八) : 用子網域執行應用程式

前一篇測試中利用站台設定檔的 location 區塊定義不同的 URL 路徑來執行不同的 Flask 網頁應用程式, 如果要執行更多的 app, 只要在站台設定檔中新增子路徑轉發到 Gunicorn 監聽的埠即可. 本篇則是要改用子網域的方式來達成相同功能.  

子網域乃是主網域的一部分, 例如在 blog.example.com 網址中, blog 就是一個子網域. 在結構上, 主網域是整個域名的基礎, 而子網域是建立在主網域之上的額外層級; 在功能上, 主網域通常代表整個網站, 而子網域則用來提供特定的內容或服務 (例如部落格, 商店或客戶服務等), 它們可以在同一主網域下運行不同的應用程序或功能. 

我計畫在 Mapleboard 上佈署多個 Flask app 作為 LINE Bot 的 webhook, 但又想保留主網域 tony1966.cc 來架設個人網站或部落格, 因此初步規劃在 Namecheap 新增一個子網域 flask.tony1966.cc 當作 webhook 後台伺服器網址, 搭配子路徑可以佈署多個 Flask app. 將 app 放在個別子網域下可方便管理和區分不同類型的服務.  

本篇旨在將前一篇以獨立站台設定檔 hello 的子路徑執行兩個 app 的方式改成用 flask.tony1966.cc 子網域來達成. 

本系列全部的測試紀錄參考 :



1. 新增子網域 flask.tony1966.cc : 

首先登入 Namecheap 首頁 :


按 SIGN IN 輸入帳密登入網站 : 




按 Dashboard 下的 Domain List, 再按域名列表最右邊的 Manage 鈕進入管理頁面 :  



 
按最右邊的 Advanced DNS : 




按底下的 Add new record 新增一筆紀錄 : 




Type 欄勾選 CNAME record, Host 欄填入 flask, Value 欄填入主域名 tony1966.cc, TTL 欄用預設 Automatic 即可, 按底下的打勾勾按鈕即完成新增設定 : 






這樣就完成 Namecheap 域名管理平台上的子網域 flask.tony1966.cc 的設定了, 最快幾分鐘內就會生效, 但因為 DNS 更改需要時間在全球的 DNS 伺服器間傳播, 最高可能須等待 48 小時. 可以用 dig 指令向 DNS 伺服器查詢此域名是否已生效會查詢 flask.tony1966.cc 這個域名的 DNS 資訊, 包括其 IP 地址, 名稱伺服器, MX 記錄等 : 

tony1966@LX2438:~$ dig flask.tony1966.cc   

; <<>> DiG 9.18.24-0ubuntu0.22.04.1-Ubuntu <<>> flask.tony1966.cc
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24124
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;flask.tony1966.cc. IN A

;; ANSWER SECTION:
flask.tony1966.cc. 1799 IN CNAME tony1966.cc.
tony1966.cc. 1799 IN A 220.133.12.159

;; Query time: 126 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Mon Jul 14 09:56:40 CST 2025
;; MSG SIZE  rcvd: 76

"status: NOERROR" 表示查詢成功, 沒有錯誤. ANSWER SECTION 中的 "flask.tony1966.cc. 1799" 表示有效期為 1799 秒, 已效期就是該 DNS 記錄的 TTL (Time to Live) 值, 在這段時間內, 當其他用戶查詢相同的域名時將不需要再次向上游的 DNS 伺服器發送請求, 而是直接使用記憶體暫存的結果, 這樣可以提高查詢效率並減少網絡流量. 


2. 檢視 Gunicorn 程序 : 

在前兩篇測試中已在 ~/flask_apps 資料夾下建立兩個 Flask app : hello.py 與 hello2.py, 並且啟動 Gunicorn 程序分別監聽 127.0.0.1:8080 與 127.0.0.1:8081 埠, 當 Nginx 轉發 HTTP 請求到這兩個埠時便分別執行 hello.py 與 hello2.py 來回應. 終端機的啟動指令如下 :

gunicorn -w 4 -b 127.0.0.1:8080 hello:app   
gunicorn -w 4 -b 127.0.0.1:8081 hello2:app

可用下列指令查詢目前執行中的 Gunicorn 程序 : 

tony1966@LX2438:~$ ps aux | grep gunicorn   
tony1966  223187  0.0  0.2  34156  8960 ?        Ss   Jul06   4:35 gunicorn: master [hello:app]
tony1966  223192  0.0  0.6  42348 24188 ?        S    Jul06   0:29 gunicorn: worker [hello:app]
tony1966  223193  0.0  0.6  42344 23988 ?        S    Jul06   0:28 gunicorn: worker [hello:app]
tony1966  223194  0.0  0.5  42344 20256 ?        S    Jul06   0:29 gunicorn: worker [hello:app]
tony1966  223195  0.0  0.6  42344 24076 ?        S    Jul06   0:29 gunicorn: worker [hello:app]
tony1966  418476  0.0  0.5  34156 19908 pts/0    S+   Jul14   0:26 gunicorn: master [hello2:app]
tony1966  418477  0.0  0.6  41156 25636 pts/0    S+   Jul14   0:02 gunicorn: worker [hello2:app]
tony1966  418478  0.0  0.6  41320 26136 pts/0    S+   Jul14   0:02 gunicorn: worker [hello2:app]
tony1966  418479  0.0  0.6  41156 25604 pts/0    S+   Jul14   0:02 gunicorn: worker [hello2:app]
tony1966  418480  0.0  0.6  41156 25624 pts/0    S+   Jul14   0:02 gunicorn: worker [hello2:app]
tony1966  440626  0.0  0.0   9136  1712 pts/1    S+   11:43   0:00 grep --color=auto gunicorn

也可以將啟動 Gunicorn 程序設為系統服務 (systemd), 這樣就不會占用一個終端機, 且萬一系統當機重啟後也會自動啟動, 參考 :



3. 建立網站設定檔 flask.tony1966.cc :   

接下來要為子域名 flask.tony1966.cc 建立一個獨立的網站設定檔, 為了清楚辨識與日後維護方便, 檔名就取與子域名相同的  flask.tony1966.cc, 這樣一眼就知道這個設定檔是對應哪個子網域. 

用 nano 編輯 /etc/nginx/sites-available/flask.tony1966.cc 設定檔 : 

tony1966@LX2438:~$ sudo nano /etc/nginx/sites-available/flask.tony1966.cc    
[sudo] tony1966 的密碼: 

輸入如下之初始內容 : 

server {
    listen 80;
    server_name flask.tony1966.cc;

    location /hello/ {
        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;
    }
}

此初始內容為對子網域 flask.tony1966.cc 的 HTTP (80 埠) 請求之轉發設定, 利用 proxy_pass 指令將不同子路徑 (/hello/ 與 /hello2/) 的請求轉發到上游 Gunicorn 伺服器的不同端口埠號 (8080 與 8081). 


4. 建立符號鏈結啟用網站設定檔 flask.tony1966.cc : 

接下來用 ln -s 指令建立網站設定檔 flask.tony1966.cc 在 /etc/nginx/sites-enabled/ 下的符號連結來啟用此子網域站台 : 

tony1966@LX2438:~$ sudo ln -s /etc/nginx/sites-available/flask.tony1966.cc /etc/nginx/sites-enabled/  
[sudo] tony1966 的密碼: 

用 ls -ls 檢視 /etc/nginx/sites-enabled/ 下的已啟用站台 : 

tony1966@LX2438:~$ ls -ls /etc/nginx/sites-enabled/ 
總用量 0
0 lrwxrwxrwx 1 root root 44 Jul 15 14:09 flask.tony1966.cc -> /etc/nginx/sites-available/flask.tony1966.cc 
0 lrwxrwxrwx 1 root root 32 Jul  7 15:23 hello -> /etc/nginx/sites-available/hello

可見已啟用了 flask.tony1966 站台. 


5. 檢查網站設定檔與重新載入 Nginx :   

用 nginx -t 指令測試 Nginx 在 /etc/nginx/sites-available/ 下的所有網站設定檔, 看看是否有語法錯誤以及配置是否正確 : 

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   


6. 使用 Certbot 自動加入 HTTPS 設定 :   

因為主網域 tony1966.cc 有向 Let's Encrypt 註冊來取得 HTTPS (443 埠) 安全網站連線 SSL/TLS 憑證, 上面的子網域站台設定檔僅配置了 HTTP 的設定, 必須用 Let's Encrypt 提供的工具程式 Certbot 來為子網域 flask.tony1966.cc 加上 HTTPS 設定, 指令如下 :

sudo certbot --nginx -d flask.tony1966.cc    

tony1966@LX2438:~$ sudo certbot --nginx -d flask.tony1966.cc   
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for flask.tony1966.cc

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/flask.tony1966.cc/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/flask.tony1966.cc/privkey.pem
This certificate expires on 2025-10-13.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

Deploying certificate
Successfully deployed certificate for flask.tony1966.cc to /etc/nginx/sites-enabled/flask.tony1966.cc
Congratulations! You have successfully enabled HTTPS on https://flask.tony1966.cc

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

用 cat 檢視 Certbot 修改過的站台設定檔 flask.tony1966.cc : 

tony1966@LX2438:~$ cat /etc/nginx/sites-available/flask.tony1966.cc    
server {
    server_name flask.tony1966.cc;

    location /hello/ {
        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/flask.tony1966.cc/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/flask.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 = flask.tony1966.cc) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    server_name flask.tony1966.cc;
    return 404; # managed by Certbot


}

可見 Certbot 已加入 HTTPS 的安全連線設定了. 

目前啟用的 hello 與 flask.tony1966.cc 這兩個站台對來自客戶端的請求之轉發流程如下 :

客戶端的 HTTP/HTTPS 請求
    ↓
Nginx (80/443 埠)
    ↓ 根據 server_name 判斷是哪個網域
     |__ tony1966.cc (設定檔 hello)
     |        ↓ 根據 location 的路徑判斷要轉發到哪個端口
     |        |__  /             → http://127.0.0.1:8080 (執行 hello.py)
     |        |__  /hello2/  → http://127.0.0.1:8081 (執行 hello2.py)
     |
     |__ flask.tony1966.cc (設定檔 flask.tony1966.cc)
              ↓ 根據 location 的路徑判斷要轉發到哪個端口
              |__  /hello/    → http://127.0.0.1:8080 (執行 hello.py)
              |__  /hello2/  → http://127.0.0.1:8081 (執行 hello2.py)

可見這兩個站台其實都把請求轉發到相同的 Gunicorn 端口. 


7. 測試網站 :     

以上完成子網域站台 flask.tony1966.cc 的設定後測試下列網址 :









子網域 flask.tony1966.cc 站台建置好後就可以在此子網域下寫 app 了 (例如 LINE Bot 的 webhook), 每新增一個 app 就要在網站設定檔 flask.tony1966.cc 中新增一個 location 區塊並為此 app 指定一個 Gunicorn 轉發端口埠號. 

沒有留言 :