Traefik 路由規則與負載均衡

Traefik 最強大的功能之一就是其靈活的路由系統。在 Docker 環境中,我們主要透過 Container Labels 來定義這些規則。

基本路由規則 (Matchers)

在深入各種匹配規則之前,我們先了解 Traefik 路由規則 (Rule) 的基本語法。

語法結構

Traefik 的路由規則是由 Matcher (匹配器)Logical Operators (邏輯運算子) 組成的表達式:

  • 基本格式:Matcher(`參數`)
    • 例如:Host(`example.com`)
    • 注意:參數建議使用 Backticks (反引號 `) 包裹,這在 TOML/YAML 設定檔中可以避免繁瑣的跳脫字元 (Escape) 問題。
  • 邏輯運算
    • && (AND):同時符合。例如 Host(`example.com`) && PathPrefix(`/api`)
    • || (OR):符合其中之一。例如 PathPrefix(`/api`) || PathPrefix(`/dashboard`)
    • ! (NOT):反向匹配。
  • 優先級:使用括號 () 來控制運算優先順序。
    • 例如:Host(`example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))

Label 命名規則

除了規則內容的語法,我們也需要知道如何在 docker-compose.ymllabels 中定義它。Label 的 Key 遵循固定結構:

traefik.<protocol>.<type>.<name>.<property>

traefik.http.routers.my-app.rule 為例:

  • http: 協定。通常是 http (HTTP/HTTPS) 或 tcp
  • routers: 資源類型。常見的有 routers (路由), services (服務), middlewares (中介軟體)。
  • my-app: 資源名稱。這是由你自訂的 ID,但在同一類資源中必須是唯一的。
  • rule: 屬性。這裡指我們要設定路由規則。

了解了這些語法後,我們來看最常用的幾種 Matcher。

Host 匹配 (Host)

除了我們看過的 Host(`example.com`),也支援萬用字元:

labels:
  - 'traefik.http.routers.app.rule=Host(`*.example.com`)'

這會匹配 api.example.com, blog.example.com 等所有子網域。

路徑前綴匹配 (PathPrefix)

如果你希望根據網址的路徑來分流,例如 /api 給後端 A,/static 給後端 B:

# 服務 A
labels:
  - "traefik.http.routers.service-a.rule=Host(`example.com`) && PathPrefix(`/api`)"

# 服務 B
labels:
  - "traefik.http.routers.service-b.rule=Host(`example.com`) && PathPrefix(`/static`)"

注意:&& 代表 AND 邏輯運算。請求必須同時符合網域和路徑規則。

表頭匹配 (Headers)

你甚至可以根據 HTTP Header 來路由。這在做 API 版本控制或 A/B 測試時很有用。

labels:
  - 'traefik.http.routers.app-v2.rule=Host(`api.example.com`) && Headers(`X-Api-Version`, `2`)'

這條規則只會匹配 X-Api-Version: 2 的請求。

負載平衡 (Load Balancing)

當你的後端服務需要擴展時,Traefik 會自動處理負載平衡。

自動發現副本 (Replicas)

如果你使用 Docker Compose 的 scale 指令擴展服務:

docker compose up -d --scale whoami=3

Traefik 會自動偵測到 whoami 服務現在有 3 個容器實例 (Container Instances),並自動將它們加入到 Load Balancer 的後端池中。

預設情況下,Traefik 使用 Weighted Round Robin (加權輪詢) 演算法。這意味著請求會依序分發給每個容器: 1 -> Container A 2 -> Container B 3 -> Container C 4 -> Container A ...

黏著會話 (Sticky Sessions)

有些應用程式 (例如舊版的 Java Web App 或依賴 PHP Session 的應用) 需要確保同一個使用者的請求總是連到同一個後端容器。這時你需要啟用 Sticky Session。

Traefik 使用 Cookie 來達成這個功能:

labels:
  # 啟用 Sticky Session
  - 'traefik.http.services.app.loadbalancer.sticky=true'
  # 設定 Cookie 名稱
  - 'traefik.http.services.app.loadbalancer.sticky.cookie.name=sticky_cookie'
  # 設定 Cookie 安全屬性 (建議開啟 Secure 和 HTTPOnly)
  - 'traefik.http.services.app.loadbalancer.sticky.cookie.secure=true'
  - 'traefik.http.services.app.loadbalancer.sticky.cookie.httpOnly=true'

當啟用後,Traefik 會在第一個回應中寫入一個 Cookie,記錄這名使用者被分配到的容器 IP。後續帶有這個 Cookie 的請求都會被導向同一個容器。

權重 (Weights)

在 Docker Label 中設定權重比較少見,因為 Docker Compose 的副本通常是同質的。但在其他 Provider (如 File Provider) 中,你可以為不同的 Server 設定不同的權重,讓效能較好的伺服器處理更多請求。

# 在 File Provider 中設定權重範例 (非 Docker Label)
http:
  services:
    my-app:
      loadBalancer:
        servers:
          - url: 'http://private-ip-server-1:80'
            weight: 3 # 處理 3/4 的流量
          - url: 'http://private-ip-server-2:80'
            weight: 1 # 處理 1/4 的流量

掌握了這些路由技巧,你就能靈活地設計應用程式的流量入口。