4. 使用IaC创建生产级AKS集群

用门户点选创建 AKS 只能算“跑通”,生产级集群必须做到可重复、可审计、可回滚。IaC 的价值不在于把点击换成代码,而在于把集群的每一个决策——网络模型、节点池规格、可用区分布、标签体系、状态存放位置——都固化成可评审的声明。本节以 Terraform 为主线,Bicep 作为对照,讲清模块化设计、节点池与可用区、标签与资源组策略、状态管理与 CI 集成这五件事如何落到一份能真正上生产的代码里。

一、模块化设计:把“集群”拆成可复用的三层

生产级 AKS 的 Terraform 代码不应是一个几百行的 main.tf,而应按职责分层。推荐三层结构:基础层负责资源组、Log Analytics 工作区、容器注册表(ACR)、虚拟网络与子网;集群层负责 AKS 本身与系统节点池;工作负载层负责业务节点池、ACR 拉取角色分配、诊断设置。三层之间通过模块输出(output)传递 ID,而不是硬编码名称。

模块的输入变量要收敛到真正需要按环境变化的参数,例如 environment、location、kubernetes_version、node_pools、tags。凡是“所有环境都一样”的值应写成模块内默认值,避免变量爆炸。下面是一个集群层模块调用的骨架示例:

module "aks" {
  source              = "./modules/aks-cluster"
  cluster_name        = "aks-prod-eastus"
  resource_group_name = module.base.rg_name
  location            = "eastus"
  kubernetes_version  = "1.29"
  subnet_id           = module.base.aks_subnet_id
  log_analytics_id    = module.base.log_analytics_id
  system_node_pool = {
    vm_size   = "Standard_D4s_v5"
    min_count = 3
    max_count = 6
    zones     = ["1", "2", "3"]
  }
  tags = local.common_tags
}

Bicep 的模块化思路一致,用 module 引用 br/public:avm/res/container-service/managed-cluster 这类已验证模块,参数以对象形式传入。两者取舍:Terraform 生态成熟、状态与 plan 机制清晰,适合多云与复杂依赖;Bicep 与 ARM 原生贴合、无需管理状态文件,适合纯 Azure 团队。生产上不建议同一集群混用两套 IaC 管理,否则会出现“谁覆盖谁”的漂移。

二、节点池配置:系统池与业务池必须分开

AKS 默认只给一个节点池,生产环境必须显式拆分。系统节点池只跑 CoreDNS、metrics-server、CSI 驱动等关键组件,业务节点池按负载特征划分。关键参数包括:vm_size、min_count/max_count、os_disk_size_gb、max_pods、node_labels、node_taints、mode。

一个常见的生产配置是:系统池 3 节点、Standard_D4s_v5、开启自动伸缩 3–6;通用业务池 3–20 节点、Standard_D8s_v5;内存密集型池用 Standard_E8s_v5 并打上 workload=memory 标签与 sku=memory:NoSchedule 污点,让业务通过 nodeSelector 与 toleration 精确调度。系统池必须设置 only_critical_addons_enabled = true(或等价参数),防止业务 Pod 抢占系统资源。

节点池的 max_pods 与子网容量强相关。Azure CNI 下每个 Pod 占一个 IP,若子网为 /24(约 251 个可用 IP),单池 20 节点 × 30 Pod 就会耗尽。规划时按“节点数 × max_pods + 预留”反推子网大小,或改用 Azure CNI Overlay 降低 IP 消耗。

三、可用区与多区域部署

可用区决定单区域内的高可用,多区域决定区域级容灾,两者不是一回事。生产集群的系统节点池应跨三个可用区(zones = ["1","2","3"]),业务池至少跨两个。注意:可用区在集群创建时确定,节点池创建后无法追加可用区,只能新建节点池迁移。

多区域部署有三种典型形态:主动-主动(两个区域各一套 AKS,前置 Front Door 或 Traffic Manager 分流,数据层做异地复制);主动-被动(主区域承载流量,备区域集群常驻但缩容到最小,故障时扩容接管);成对区域(利用 Azure 区域对,如 East US / West US,享受平台级更新顺序与复制优先)。选择依据是 RTO/RPO 与成本:主动-主动成本约两倍但 RTO 接近分钟级,主动-被动成本可控但切换需验证扩容速度与镜像预热。

生产级 AKS 多区域与可用区拓扑 主区域 East US AKS 控制面(托管) K8s 1.29 · 私有 API Server 系统池 Zone 1 D4s_v5 ×1 系统池 Zone 2 D4s_v5 ×1 系统池 Zone 3 D4s_v5 ×1 业务池 general · D8s_v5 · 3–20 · Zone 1/2/3 label: workload=general 业务池 memory · E8s_v5 · 2–10 · Zone 1/2 taint: sku=memory:NoSchedule 备区域 West US AKS 控制面(托管) 同版本 · 同网络模型 系统池 · Zone 1/2/3 常态 min=1,故障时扩至 3 镜像预热 + 配置同步 业务池 general · 缩容至 min 故障时按 HPA 扩容接管 ACR 异地复制 · 双区域拉取 Geo-replication: eastus / westus 配对

四、标签与资源组策略

标签是成本分摊、自动化运维和合规审计的基础。生产集群至少应统一以下标签:environment(prod/staging)、owner、cost-center、managed-by(terraform)、data-classification。在 Terraform 中用 locals 定义 common_tags 并合并到每个资源,避免逐个手写。

资源组策略上,推荐“按生命周期分组”而非“按类型分组”:AKS 集群、节点池、ACR、Log Analytics 放在同一资源组,因为它们同生共死;网络与共享服务(如 Hub VNet、防火墙)放在独立资源组,由平台团队管理。这样销毁一套环境时只需删除一个资源组,不会误伤共享网络。同时用 Azure Policy 强制要求资源组必须带 environment 标签,未打标签的部署直接拒绝。

五、状态管理与 CI 集成

Terraform 状态文件是生产 IaC 的生命线。必须使用远程后端,推荐 Azure Storage Account + Blob 容器,并开启版本控制与软删除。状态文件按环境隔离:prod.tfstate、staging.tfstate 分不同 key,避免一次 plan 影响所有环境。后端配置示例:

terraform {
  backend "azurerm" {
    resource_group_name  = "rg-tfstate"
    storage_account_name = "sttfstateprod001"
    container_name       = "tfstate"
    key                  = "aks/prod.tfstate"
  }
}

CI 集成推荐“PR 阶段 plan、合并后 apply”的两段式流水线。PR 触发 terraform fmt -check、terraform validate、terraform plan,plan 结果作为评论贴回 PR 供评审;合并到 main 后触发 terraform apply -auto-approve。凭据使用 OIDC 联邦身份而非长期密钥,GitHub Actions 或 Azure DevOps 通过 azure/login 获取短期令牌。apply 后追加一步 az aks get-credentials 与 kubectl get nodes,确认集群真实可达,而不是只看 Terraform 返回成功。

状态文件包含敏感信息(如连接字符串、密钥),Storage Account 必须关闭公共访问、启用私有终结点,并限制只有 CI 服务主体与平台团队可读写。切勿把 tfstate 提交到 Git 仓库。

最后,IaC 不是一次性交付。集群创建后要定期执行 terraform plan 检测漂移——如果有人通过门户改了节点池,plan 会显示差异。把漂移检测纳入每日定时流水线,发现差异即告警,才能保证“代码即真相”长期成立。