使用Terraform在Azure Container Apps中部署docker-compose
用Terraform将Traefik+Docker Compose部署到Azure Container Apps
关键架构调整说明
Azure Container Apps是托管式容器服务,不提供/var/run/docker.sock访问,所以你原Docker Compose中Traefik的--providers.docker配置无法工作——Traefik没法通过Docker socket发现form-service的实例。因此需要先调整架构:
方案1:使用Container Apps内置Ingress(推荐)
直接利用Azure Container Apps自带的Ingress功能替代Traefik的反向代理作用,简化架构并利用托管服务的特性。
方案2:保留Traefik(需调整配置)
如果必须保留Traefik,需要放弃Docker provider,改用静态文件配置或其他服务发现方式,会增加复杂度。
方案1:Terraform配置示例(内置Ingress)
以下是将你的服务部署到Azure Container Apps的Terraform代码,使用azurerm provider:
1. 基础资源定义
terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = "~> 3.0" } } } provider "azurerm" { features {} } # 创建资源组 resource "azurerm_resource_group" "example" { name = "container-apps-rg" location = "East US" } # 创建Container Apps环境 resource "azurerm_container_app_environment" "example" { name = "container-apps-env" resource_group_name = azurerm_resource_group.example.name location = azurerm_resource_group.example.location } # 关联Azure Container Registry(替换为你的ACR信息) data "azurerm_container_registry" "acr" { name = "XX" resource_group_name = azurerm_resource_group.example.name } resource "azurerm_container_app_registry_credential" "acr" { container_app_environment_id = azurerm_container_app_environment.example.id server = data.azurerm_container_registry.acr.login_server username = data.azurerm_container_registry.acr.admin_username password = data.azurerm_container_registry.acr.admin_password }
2. 部署form-service容器应用
resource "azurerm_container_app" "form_service" { name = "form-service" resource_group_name = azurerm_resource_group.example.name location = azurerm_resource_group.example.location environment_id = azurerm_container_app_environment.example.id template { container { name = "form-service" image = "${data.azurerm_container_registry.acr.login_server}/form-service:latest" cpu = 0.5 memory = "1Gi" env { name = "LOG_LEVEL" value = "trace" } ports { port = 3031 protocol = "TCP" } ports { port = 3032 protocol = "TCP" } } } ingress { external_enabled = true target_port = 3031 allow_insecure_connections = true } }
3. 配置自定义路由规则(对应原Traefik路由)
通过azurerm_container_app_ingress_route配置路径路由:
# 配置/app1路由 resource "azurerm_container_app_ingress_route" "app1" { container_app_id = azurerm_container_app.form_service.id name = "app1-route" match_path = "/app1/*" priority = 100 service { port = 3031 } } # 配置/app2路由 resource "azurerm_container_app_ingress_route" "app2" { container_app_id = azurerm_container_app.form_service.id name = "app2-route" match_path = "/app2/*" priority = 90 service { port = 3032 } }
方案2:保留Traefik的Terraform配置(可选)
如果必须保留Traefik,需修改配置放弃Docker provider,改用静态文件配置:
1. 准备Traefik配置文件
在Terraform代码目录下创建两个配置文件:
traefik.yml(静态配置):
api: insecure: true entryPoints: web: address: ":80" providers: file: filename: /etc/traefik/dynamic_config.yml
dynamic_config.yml(动态路由配置,替换为你的Container Apps域名):
http: routers: app1: rule: "Host(`<your-container-app-domain>`) && PathPrefix(`/app1`)" entryPoints: [web] service: app1 app2: rule: "Host(`<your-container-app-domain>`) && PathPrefix(`/app2`)" entryPoints: [web] service: app2 services: app1: loadBalancer: servers: [{url: "http://form-service:3031"}] app2: loadBalancer: servers: [{url: "http://form-service:3032"}]
2. Terraform部署配置
# 部署Traefik容器应用 resource "azurerm_container_app" "traefik" { name = "traefik" resource_group_name = azurerm_resource_group.example.name location = azurerm_resource_group.example.location environment_id = azurerm_container_app_environment.example.id template { container { name = "traefik" image = "traefik:v2.10" cpu = 0.5 memory = "1Gi" volume_mounts { volume_name = "traefik-config" mount_path = "/etc/traefik" } } volume { name = "traefik-config" secret { secret_name = "traefik-config-secret" } } } ingress { external_enabled = true target_port = 80 allow_insecure_connections = true } } # 存储Traefik配置的Secret resource "azurerm_container_app_secret" "traefik_config" { container_app_id = azurerm_container_app.traefik.id name = "traefik-config-secret" data = { "traefik.yml" = filebase64("${path.module}/traefik.yml") "dynamic_config.yml" = filebase64("${path.module}/dynamic_config.yml") } } # 部署form-service(仅内部访问) resource "azurerm_container_app" "form_service" { name = "form-service" resource_group_name = azurerm_resource_group.example.name location = azurerm_resource_group.example.location environment_id = azurerm_container_app_environment.example.id template { container { name = "form-service" image = "${data.azurerm_container_registry.acr.login_server}/form-service:latest" cpu = 0.5 memory = "1Gi" env { name = "LOG_LEVEL" value = "trace" } ports { port = 3031 protocol = "TCP" } ports { port = 3032 protocol = "TCP" } } } ingress { external_enabled = false } }
注意事项
- Container Apps环境内的容器可通过服务名直接通信,无需额外网络配置。
- 若使用ACR镜像,需确保Container Apps环境已配置ACR访问凭据(代码中已包含)。
- 自定义域名需在DNS服务商处配置CNAME指向Container Apps的默认域名。
内容的提问来源于stack exchange,提问作者Karim Chaari
相关产品推荐
相关产品推荐

