Docker: HTTPS 路由

如何在 Docker 容器中管理 HTTP 路由和 TLS 加密(HTTPS)

👋 歡迎來到 Stackhero 文件!

Stackhero 提供一個即用型的 Docker cloud CaaS (Containers as a Service) 解決方案,帶來多種優勢,包括:

  • 只需 docker-compose up 即可輕鬆將您的容器部署到生產環境
  • 使用 HTTPS 保護的可自訂域名(例如,https://api.your-company.comhttps://www.your-company.comhttps://backoffice.your-company.com)。
  • 專用私有 VM提供的最佳性能和強大的安全性
  • 只需點擊即可輕鬆更新

節省時間簡化您的生活:只需 5 分鐘即可嘗試 Stackhero 的 Docker CaaS cloud hosting 解決方案,並將您的容器部署到生產環境!

每個 Stackhero for Docker 實例都包含一個專用且預配置的 Traefik 伺服器,該伺服器管理 HTTP 流量並自動使用 TLS 證書加密。這種簡化的設置使路由變得簡單高效。例如:

  • https://www.my-company.com(有或沒有 www 前綴) 的流量發送到您的 frontend 容器。
  • https://www.my-company.com/documentations 路由到您的 documentations 容器。
  • https://api.my-project.io 直接到您的 api 容器。

您可以管理無限數量的域名,並自動創建和更新 TLS 證書。最棒的是,您只需三行即可配置所有這些!

在以下所有配置示例中,您需要將 <XXXXXX>.stackhero-network.com 替換為您的 Stackhero for Docker 實例域名。

以下是 docker-compose.yml 文件的基本示例:

services:
  test:
    image: nginx
    labels:
      - "traefik.enable=true" # 啟用 Traefik 將流量路由到此容器
      - "traefik.http.routers.test.rule=Host(`<XXXXXX>.stackhero-network.com`)" # 定義主機
      - "traefik.http.routers.test.tls.certresolver=letsencrypt" # 使用 'letsencrypt' 作為 TLS 證書解析器

在此示例中,名為 test 的容器使用 Nginx 映像運行。關鍵配置在 labels 部分提供。您只需將這些行複製到您的 docker-compose.yml 文件中,並將 <XXXXXX>.stackhero-network.com 替換為您的服務域名。

您可以使用以下命令部署容器:

docker context use <XXXXXX>.stackhero-network.com
docker-compose up

容器啟動後,訪問 https://<XXXXXX>.stackhero-network.com/ 查看 "Welcome to nginx!" 頁面。

Nginx 歡迎頁面Nginx 歡迎頁面

如果您沒有看到 Nginx 歡迎頁面,請檢查 Traefik 儀表板以查找可能的錯誤!

通過此配置,發送到 <XXXXXX>.stackhero-network.com 的 HTTP 請求將被路由到您的 test 容器,並且 Traefik 自動創建和管理 HTTPS 的 TLS 證書。

在前面的示例中,我們使用了默認的 <XXXXXX>.stackhero-network.com 域名。實際上,您可能會使用自己的公司或項目域名,例如 www.my-company.comapi.my-project.io。以下部分說明如何配置自定義域名。

在此示例中,您將配置域名 api.my-project.io。將 my-project.io 替換為您擁有的域名,並將 api 替換為您想要的子域名。

首先,更新您的域名 DNS 設置,使 api.my-project.io 指向您的 <XXXXXX>.stackhero-network.com 域名。

  1. 登錄到您的域名提供商並訪問您的 DNS 配置。
  2. 創建一個名為 api(或您選擇的其他子域名)的新條目,將其類型設置為 CNAME,並將其目標設置為 <XXXXXX>.stackhero-network.com

Cloudflare DNS 介面上的 DNS 配置示例Cloudflare DNS 介面上的 DNS 配置示例

DNS 配置完成後,您可以通過運行以下命令進行驗證:

host api.my-project.io

您應該會看到類似的響應:

api.my-project.io is an alias for <XXXXXX>.stackhero-network.com

DNS 傳播可能需要最多 24 小時,具體取決於您的提供商。 如果 host 命令未返回預期的回覆,請稍等片刻再重試。

接下來,使用以下配置更新您的 docker-compose.yml 文件:

services:
  api:
    image: traefik/whoami
    hostname: api
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.api.rule=Host(`api.my-project.io`)" # 在此處添加您的域名
      - "traefik.http.services.api.loadbalancer.server.port=<PORT>" # 將 "<PORT>" 替換為您的 API 所監聽的端口
      - "traefik.http.routers.api.tls.certresolver=letsencrypt"

請不要忘記在 Traefik 標籤 Host 中將 api.my-project.io 替換為您的實際域名。

使用以下命令部署您的容器:

docker-compose up -d

然後訪問 https://api.my-project.io。您應該會看到一個顯示您的容器主機名 api 的文本頁面。

證明 Traefik 正在為我們的新域名處理 HTTP 流量並進行 TLS 加密證明 Traefik 正在為我們的新域名處理 HTTP 流量並進行 TLS 加密

在首次創建容器時,TLS 證書可能需要幾秒鐘才能生成。 如果遇到 TLS 錯誤,請稍等幾秒鐘並刷新頁面,以便證書有時間生成。

恭喜,您現在已配置了您的第一個自定義域名!

在定義網站 URL(例如 my-company.com)時,最好也設置一個 "www" 子域名。這確保通過 www.my-company.com 連接的用戶被重定向到您的主站點,並有助於避免重複內容問題。

在下面的示例中,my-company.comwww.my-company.com 都被處理。訪問 www.my-company.com 的用戶將被重定向到 my-company.com

services:
  frontend:
    image: traefik/whoami
    hostname: frontend
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.frontend.rule=Host(`my-company.com`) || Host(`www.my-company.com`)" # 在此處添加兩個域名
      - "traefik.http.services.frontend.loadbalancer.server.port=<PORT>" # 將 "<PORT>" 替換為您的前端所監聽的端口
      - "traefik.http.routers.frontend.tls.certresolver=letsencrypt"

      # 將 'www.my-company.com' 重定向到 'my-company.com':
      - 'traefik.http.routers.frontend.middlewares=redirect-www'
      - "traefik.http.middlewares.redirect-www.redirectregex.regex=^https://www.my-company.com/(.*)"
      - "traefik.http.middlewares.redirect-www.redirectregex.replacement=https://my-company.com/$${1}"
      - "traefik.http.middlewares.redirect-www.redirectregex.permanent=true"

假設您有一個專門用於文檔網站的容器。您可能希望將 https://my-company.com/docs 路由到此容器,同時將其他請求(例如 https://my-company.com/)發送到您的前端容器。下面的示例顯示了如何完成此操作:

services:
  documentations:
    image: traefik/whoami
    hostname: documentations
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.documentations.rule=Host(`my-company.com`) && PathPrefix(`/docs`)" # 在此處定義路徑前綴
      - "traefik.http.services.documentations.loadbalancer.server.port=<PORT>" # 將 "<PORT>" 替換為您的文檔容器所監聽的端口
      - "traefik.http.routers.documentations.tls.certresolver=letsencrypt"

  frontend:
    image: traefik/whoami
    hostname: frontend
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.frontend.rule=Host(`my-company.com`) || Host(`www.my-company.com`)"
      - "traefik.http.services.frontend.loadbalancer.server.port=<PORT>" # 將 "<PORT>" 替換為您的前端所監聽的端口
      - "traefik.http.routers.frontend.tls.certresolver=letsencrypt"

      # 將 'www.my-company.com' 重定向到 'my-company.com':
      - 'traefik.http.routers.frontend.middlewares=redirect-www'
      - "traefik.http.middlewares.redirect-www.redirectregex.regex=^https://www.my-company.com/(.*)"
      - "traefik.http.middlewares.redirect-www.redirectregex.replacement=https://my-company.com/$${1}"
      - "traefik.http.middlewares.redirect-www.redirectregex.permanent=true"

現在,訪問 https://my-company.com/docs(或任何子路徑,例如 https://my-company.com/docs/something)將顯示來自 documentations 容器的內容。其他路徑,例如 https://my-company.com/help,將由 frontend 容器提供服務。

默認情況下,Traefik 連接到容器的第一個暴露端口。在某些情況下,您可能需要指定特定端口。下面的示例演示如何定義自定義端口:

services:
  frontend:
    image: traefik/whoami
    hostname: frontend
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.frontend.rule=Host(`my-company.com`)"
      - "traefik.http.routers.frontend.tls.certresolver=letsencrypt"

      # 將 'https://my-company.com' 的流量路由到端口 80 上的 'frontend' 容器
      - "traefik.http.services.frontend.loadbalancer.server.port=80"

在您的 docker-compose.yml 文件中定義容器時,某些字段對於更好的一致性和管理便利性非常重要。考慮使用以下推薦配置:

services:
  <CONTAINER_NAME>:
    image: traefik/whoami
    hostname: <CONTAINER_NAME>
    container_name: <CONTAINER_NAME>
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.<CONTAINER_NAME>.rule=Host(`my-company.com`)"
      - "traefik.http.routers.<CONTAINER_NAME>.tls.certresolver=letsencrypt"

只需將 <CONTAINER_NAME> 替換為您想要的容器名稱,例如 frontend

在創建子域名時,Let's Encrypt 遵循 RFC 952 和 1123,這些規範僅允許字符集 [a-zA-Z0-9-] 中的字符。

雖然在 DNS 記錄名稱中允許使用下劃線 ('_'),但在主機名中不被接受。因此,Let's Encrypt 拒絕像 "my_subdomain.example.com" 這樣的子域名,並顯示錯誤 "Domain name contains an invalid character"。

要解決此問題,只需從您的子域名中刪除所有下劃線。