如何在同一個 Azure Container Apps Environment(CAE)中,
透過 Application Gateway(AGW)將不同網域路由到不同的 Container App,
達到單一基礎設施服務多個應用程式的效果。
架構圖
┌─────────────────────────────────────────────┐
外部使用者 │ Virtual Network (vnet-ag-example-001) │
│ │ ┌──────────────────────────────────────┐ │
│ HTTPS │ │ Application Gateway (agw-example) │ │
▼ │ │ │ │
[app-a.example.com]─────┼──► Listener: app-a.example.com │ │
[app-b.example.com]─────┼──► Listener: app-b.example.com │ │
│ └───────────┬──────────────┬───────────┘ │
└─────────────┼──────────────┼───────────────┘
│ VNet Peering │
┌─────────────┼──────────────┼───────────────┐
│ Virtual Network (vnet-example-001) │
│ │ │ │
│ ┌──────────▼──────────────▼───────────┐ │
│ │ Container Apps Environment (CAE) │ │
│ │ (Internal, Consumption) │ │
│ │ │ │
│ │ ca-app-a (port 80, nginx) │ │
│ │ ca-app-b (port 8080, api) │ │
│ └─────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────┘
前置條件
| 資源 | 說明 |
|---|---|
| Container Apps Environment | 建立為 Internal(Workload Profile: Consumption) |
| VNet for CAE | 例如 vnet-example-001,CAE 使用其中一個子網路 |
| VNet for AGW | 例如 vnet-ag-example-001,AGW 使用其中一個子網路 |
| VNet Peering | 上述兩個 VNet 互相 Peered |
| Private DNS Zone | 由 CAE 自動建立,需手動連結至 AGW 所在的 VNet |
| SSL 憑證 | 每個自訂網域需有對應的憑證(PFX 格式上傳至 AGW) |
一、Private DNS Zone 設定
CAE 建立後,Azure 會自動在同一資源群組建立一個 Private DNS Zone,
格式為:<隨機字串>.<region>.azurecontainerapps.io
例如:abcd1234efgh.japaneast.azurecontainerapps.io
此 DNS Zone 需與 AGW 所在的 VNet 連結,AGW 才能解析 CA 的 FQDN。
az network private-dns link vnet create \
--resource-group <cae-resource-group> \
--zone-name "abcd1234efgh.japaneast.azurecontainerapps.io" \
--name "link-to-agw-vnet" \
--virtual-network <agw-vnet-id> \
--registration-enabled false
Wildcard DNS 記錄
Private DNS Zone 中有一筆 Wildcard A 記錄:
| 名稱 | 類型 | 值 |
|---|---|---|
* | A | <CAE 內部 Load Balancer IP> |
此 Wildcard 只能匹配單層子網域(如 ca-app-a.abcd1234efgh...),
無法匹配 ca-app-a.internal.abcd1234efgh...(雙層)。
⚠️ 注意:若使用
--ingress internal建立 CA,其 FQDN 會包含.internal.,
不會被 Wildcard 記錄匹配,AGW 將無法解析。
請一律使用--ingress external(在 Internal CAE 內,external 仍為 VNet 私有,並非真正對外)。
二、建立 Container App
每個應用程式建立時指定 --ingress external,對應的 FQDN 格式為:<app-name>.<env-domain>
# 建立 App A(前端,nginx on port 80)
az containerapp create \
--name ca-app-a \
--resource-group <resource-group> \
--environment <cae-name> \
--image <registry>.azurecr.io/app-a:latest \
--target-port 80 \
--ingress external \
--registry-server <registry>.azurecr.io
# 建立 App B(後端 API,on port 8080)
az containerapp create \
--name ca-app-b \
--resource-group <resource-group> \
--environment <cae-name> \
--image <registry>.azurecr.io/app-b:latest \
--target-port 8080 \
--ingress external \
--registry-server <registry>.azurecr.io
建立後,各 CA 的 FQDN 例如:
ca-app-a.abcd1234efgh.japaneast.azurecontainerapps.ioca-app-b.abcd1234efgh.japaneast.azurecontainerapps.io
允許 HTTP(不強制重新導向 HTTPS)
ACA 預設會將 HTTP 請求 301 重新導向至 HTTPS。
若 AGW 以 HTTP 協定連至 CA(通常如此,因為 TLS 已在 AGW 終止),
需對每個 CA 啟用 --allow-insecure:
az containerapp ingress update \
--name ca-app-a \
--resource-group <resource-group> \
--allow-insecure
三、Application Gateway 設定
同一個 AGW 可以透過多組 Listener + Rule + Backend Pool 路由不同網域。
3-1 Backend Pool
每個應用程式建立一個 Backend Pool,目標為 CA 的 FQDN:
| Pool 名稱 | FQDN |
|---|---|
pool-app-a | ca-app-a.abcd1234efgh.japaneast.azurecontainerapps.io |
pool-app-b | ca-app-b.abcd1234efgh.japaneast.azurecontainerapps.io |
az network application-gateway address-pool create \
--gateway-name <agw-name> \
--resource-group <agw-resource-group> \
--name pool-app-a \
--servers ca-app-a.abcd1234efgh.japaneast.azurecontainerapps.io
3-2 Backend HTTP Settings
指定協定為 HTTP(因為 TLS 在 AGW 終止)、Port 80,
並固定 Host Header 為 CA 的 FQDN,使 ACA Ingress 能正確路由:
az network application-gateway http-settings create \
--gateway-name <agw-name> \
--resource-group <agw-resource-group> \
--name set-app-a \
--protocol Http \
--port 80 \
--host-name "ca-app-a.abcd1234efgh.japaneast.azurecontainerapps.io"
--host-name設定會讓 AGW 傳送正確的HostHeader 給 ACA,
若不設定,ACA Ingress 收到的 Host 可能是原始網域(如app-a.example.com),
導致路由失敗(HTTP 404)。
3-3 HTTPS Listener
每個自訂網域需要一個 Listener:
# 先上傳 SSL 憑證
az network application-gateway ssl-cert create \
--gateway-name <agw-name> \
--resource-group <agw-resource-group> \
--name cert-app-a \
--cert-file app-a.pfx \
--cert-password <password>
# 建立 Listener
az network application-gateway http-listener create \
--gateway-name <agw-name> \
--resource-group <agw-resource-group> \
--name lsn-app-a-https \
--frontend-port <https-port-name> \
--frontend-ip <frontend-ip-config-name> \
--host-name "app-a.example.com" \
--ssl-cert cert-app-a
3-4 Routing Rule
將 Listener 連接至 Backend Pool + HTTP Settings:
az network application-gateway rule create \
--gateway-name <agw-name> \
--resource-group <agw-resource-group> \
--name rule-app-a \
--http-listener lsn-app-a-https \
--address-pool pool-app-a \
--http-settings set-app-a \
--rule-type Basic \
--priority 200
重複 3-1 至 3-4 步驟,為 app-b.example.com 建立對應的資源。
四、新增應用程式到現有環境
只需重複以下步驟,無需修改 DNS Zone 或 CAE 設定:
- 建立 Container App(
--ingress external) - 啟用
--allow-insecure - AGW 新增 Backend Pool(目標為新 CA 的 FQDN)
- AGW 新增 HTTP Settings(Host Header 設為新 CA 的 FQDN)
- AGW 新增 Listener(綁定新網域的 SSL 憑證)
- AGW 新增 Routing Rule(連接上述三者)
五、CA 間互相呼叫
在同一個 Internal CAE 中,Container App 可透過 FQDN 直接呼叫彼此,
不需經過 AGW:
http://ca-app-b.abcd1234efgh.japaneast.azurecontainerapps.io/api/endpoint
例如前端 nginx 的 proxy_pass 設定:
location /api/ {
proxy_pass http://ca-app-b.abcd1234efgh.japaneast.azurecontainerapps.io/api/;
}
建議將後端 FQDN 透過環境變數注入(例如 BACKEND_URL),
搭配 nginx 的 envsubst 機制於容器啟動時替換:
# nginx.conf.template
location /api/ {
proxy_pass ${BACKEND_URL}/api/;
}
# Dockerfile:放在 /etc/nginx/templates/ 會在啟動時自動替換
COPY .deploy/nginx.conf.template /etc/nginx/templates/default.conf.template
六、常見問題
AGW Backend Health 顯示 Unknown 或 502
- DNS 無法解析 CA FQDN:確認 Private DNS Zone 已連結至 AGW 所在的 VNet。
- 使用了
.internal.FQDN:CA 建立時使用了--ingress internal,改為--ingress external。 - HTTP 重新導向:CA 收到 HTTP 請求回傳 301,對該 CA 執行
az containerapp ingress update --allow-insecure。 - Host Header 不符:AGW 的 HTTP Settings 未設定
--host-name,導致 ACA Ingress 無法識別目標應用程式。
CA 啟動失敗:open .: no such file or directory
應用程式嘗試讀取相對路徑的檔案(如資料庫 Migration 檔),
但工作目錄與預期不符。
解法:透過環境變數指定絕對路徑,例如:
az containerapp update \
--name ca-app-b \
--resource-group <resource-group> \
--set-env-vars "MIGRATION_SOURCE=file:///app/migrations"
CA 中的環境變數包含 & 字元
在 Windows 環境下使用 az CLI(.cmd 包裝器)時,& 會被 cmd.exe 解析為命令分隔符,
導致參數被截斷。解法:改用 az rest --method PATCH 搭配 JSON 檔案傳遞:
{
"properties": {
"template": {
"containers": [{
"name": "ca-app-b",
"env": [
{ "name": "DATABASE_URL", "value": "sqlserver://host?db=mydb¶m=value" }
]
}]
}
}
}
az rest --method PATCH \
--url "https://management.azure.com/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.App/containerApps/<name>?api-version=2024-03-01" \
--body @patch.json