使用 Traefik 進行 HTTP 路由

簡介

在本地開發過程中,需要執行多個 HTTP 服務是很常見的。您可能同時擁有一個 API 和一個前端應用,一個用於模擬資料端點的 WireMock 服務,或者一個數據庫視覺化工具(如 phpMyAdmin 或 pgAdmin)。在許多開發設定中,這些服務暴露在不同的埠上,這不僅要求您記住哪個服務在哪個埠,還可能引入其他問題(例如 CORS)。

反向代理可以極大地簡化這種設定,它作為唯一暴露的服務,然後根據請求 URL(透過路徑或主機名)將請求路由到適當的服務。Traefik 是一個現代化的雲原生反向代理和負載均衡器,它使開發和部署多服務應用程式變得更加容易。本指南將向您展示如何將 Traefik 與 Docker 結合使用,以增強您的開發環境。

在本指南中,您將學習如何

  1. 使用 Docker 啟動 Traefik
  2. 配置路由規則以在兩個容器之間分配流量
  3. 在容器化開發環境中使用 Traefik
  4. 使用 Traefik 將請求傳送到非容器化工作負載

先決條件

要跟隨本操作指南,需要滿足以下先決條件

在 Docker 中使用 Traefik

Traefik 的一個獨特功能是它可以透過多種方式進行配置。當使用 Docker provider 時,Traefik 使用標籤從其他正在執行的容器中獲取其配置。Traefik 會監視引擎事件(用於容器啟動和停止),提取標籤,並更新其配置。

雖然有許多 Traefik 監控的標籤,但最常見的兩個是:

讓我們快速演示一下如何啟動 Traefik,然後配置兩個額外的容器,以便使用不同的主機名進行訪問。

  1. 為了讓兩個容器能夠相互通訊,它們需要位於同一個網路上。使用 docker network create 命令建立一個名為 traefik-demo 的網路。

    $ docker network create traefik-demo
    
  2. 使用以下命令啟動一個 Traefik 容器。該命令將 Traefik 暴露在 80 埠,掛載 Docker 套接字(用於監控容器以更新配置),並傳遞 --providers.docker 引數以配置 Traefik 使用 Docker provider。

    $ docker run -d --network=traefik-demo -p 80:80 -v /var/run/docker.sock:/var/run/docker.sock traefik:v3.1.2 --providers.docker
    
  3. 現在,啟動一個簡單的 Nginx 容器,並定義 Traefik 正在監視的標籤以配置 HTTP 路由。請注意,Nginx 容器沒有暴露任何埠。

    $ docker run -d --network=traefik-demo --label 'traefik.http.routers.nginx.rule=Host(`nginx.localhost`)' nginx
    

    容器啟動後,在瀏覽器中開啟 http://nginx.localhost 以檢視該應用(所有基於 Chromium 的瀏覽器都會在本地路由 *.localhost 請求,無需額外設定)。

  4. 啟動第二個將使用不同主機名的應用程式。

    $ docker run -d --network=traefik-demo --label 'traefik.http.routers.welcome.rule=Host(`welcome.localhost`)' docker/welcome-to-docker
    

    容器啟動後,在瀏覽器中開啟 http://welcome.localhost。您應該會看到一個“Welcome to Docker”網站。

在開發中使用 Traefik

現在您已經體驗了 Traefik,是時候嘗試在開發環境中使用它了。在這個例子中,您將使用一個具有前後端分離的示例應用程式。該應用棧具有以下配置:

  1. 所有到 /api 的請求都轉到 API 服務
  2. 所有其他到 localhost 的請求都轉到前端客戶端
  3. 由於該應用使用 MySQL,db.localhost 應提供 phpMyAdmin,以便在開發過程中輕鬆訪問資料庫
Architecture diagram showing Traefik routing requests to other containers based on the path of the request

該應用程式可以在 GitHub 上訪問,地址為 dockersamples/easy-http-routing-with-traefik

  1. compose.yaml 檔案中,Traefik 使用以下配置:

    services:
      proxy:
        image: traefik:v3.1.2
        command: --providers.docker
        ports:
          - 80:80
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock

    請注意,這與之前使用的配置基本相同,但現在是 Compose 語法。

  2. 客戶端服務具有以下配置,它將啟動容器併為其提供在 localhost 接收請求的標籤。

    services:
      # …
      client:
        image: nginx:alpine
        volumes:
          - "./client:/usr/share/nginx/html"
        labels:
          traefik.http.routers.client.rule: "Host(`localhost`)"
  3. api 服務具有類似的配置,但您會注意到路由規則有兩個條件——主機必須是“localhost”,並且 URL 路徑必須以“/api”為字首。由於此規則更具體,Traefik 將會比客戶端規則優先評估它。

    services:
      # …
      api:
        build: ./dev/api
        volumes:
          - "./api:/var/www/html/api"
        labels:
          traefik.http.routers.api.rule: "Host(`localhost`) && PathPrefix(`/api`)"
  4. 最後,phpmyadmin 服務被配置為接收對主機名“db.localhost”的請求。該服務還定義了環境變數以自動登入,這使得進入應用程式變得更容易一些。

    services:
      # …
      phpmyadmin:
        image: phpmyadmin:5.2.1
        labels:
          traefik.http.routers.db.rule: "Host(`db.localhost`)"
        environment:
          PMA_USER: root
          PMA_PASSWORD: password
  5. 在啟動堆疊之前,如果 Nginx 容器仍在執行,請停止它。

就是這樣。現在,您只需要用 docker compose up 啟動 Compose 堆疊,所有的服務和應用程式就都準備好進行開發了。

將流量傳送到非容器化工作負載

在某些情況下,您可能希望將請求轉發到未在容器中執行的應用程式。在下面的架構圖中,使用了與之前相同的應用程式,但 API 和 React 應用現在在本機上執行。

An architecture diagram showing several components and the routing between them. Traefik is able to send requests to both non-containerized and containerized workloads

要實現這一點,Traefik 將需要使用另一種方法來配置自身。檔案提供程式允許您在 YAML 文件中定義路由規則。這是一個示例檔案:

http:
  routers:
    native-api:
      rule: "Host(`localhost`) && PathPrefix(`/api`)"
      service: native-api
    native-client:
      rule: "Host(`localhost`)"
      service: native-client

  services:
    native-api:
      loadBalancer:
        servers:
          - url: "http://host.docker.internal:3000/"
    native-client:
      loadBalancer:
        servers:
          - url: "http://host.docker.internal:5173/"

此配置指示對 localhost/api 的請求將被轉發到名為 native-api 的服務,該服務再將請求轉發到 http://host.docker.internal:3000。主機名 host.docker.internal 是 Docker Desktop 提供的一個名稱,用於向主機發送請求。

有了這個檔案,唯一的變化就是 Traefik 的 Compose 配置。具體來說,有兩件事發生了變化:

  1. 配置檔案被掛載到 Traefik 容器中(具體的目標路徑由您決定)
  2. command 已更新,以新增檔案提供程式並指向配置檔案的位置
services:
  proxy:
    image: traefik:v3.1.2
    command: --providers.docker --providers.file.filename=/config/traefik-config.yaml --api.insecure
    ports:
      - 80:80
      - 8080:8080
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./dev/traefik-config.yaml:/config/traefik-config.yaml

啟動示例應用

要執行將請求從 Traefik 轉發到原生執行的應用的示例應用,請使用以下步驟:

  1. 如果您的 Compose 堆疊仍在執行,請使用以下命令停止它:

    $ docker compose down
    
  2. 使用提供的 compose-native.yaml 檔案啟動應用程式。

    $ docker compose -f compose-native.yaml up
    

    開啟 https:// 將返回 502 Bad Gateway,因為其他應用程式尚未執行。

  3. 透過執行以下步驟啟動 API:

    cd api
    yarn install
    yarn dev
    
  4. 在一個新的終端中執行以下步驟來啟動前端(從專案根目錄開始):

    cd client
    yarn install
    yarn dev
    
  5. https:// 開啟應用。您應該會看到一個從 https:///api/messages 獲取訊息的應用。您也可以開啟 http://db.localhost 直接從 Mongo 資料庫檢視或調整可用的訊息。Traefik 將確保請求被正確路由到正確的容器或應用程式。

  6. 完成後,執行 docker compose down 停止容器,並透過按 ctrl+c 停止 Yarn 應用。

回顧

執行多個服務不必需要複雜的埠配置和良好的記憶力。藉助像 Traefik 這樣的工具,可以輕鬆啟動所需的服務並輕鬆訪問它們——無論是應用本身(例如前端和後端)還是用於額外的開發工具(例如 phpMyAdmin)。