Terraform 管理 ACK 集群
解读
在国内云原生面试中,阿里云容器服务 ACK(Alibaba Cloud Kubernetes)是出镜率最高的托管 K8s 产品。面试官问“如何用 Terraform 管理 ACK 集群”,并不是想听你背文档,而是考察三条线:
- 是否把 IaC 理念真正落地到阿里云平台,而不是“控制台点一点”;
- 是否能把 PHP 业务场景(高并发、弹性伸缩、低成本)映射到 ACK 的节点池、弹性伸缩组、Spot 实例等配置;
- 是否具备“可演进”的架构思维:灰度、多环境、多地域、GitOps、备份、费用审计。
一句话:让你证明“我能用代码把 ACK 从 0 到 1 再到 100 地管起来,且让 PHP 团队无感接入”。
知识点
- 阿里云 Provider 链路与认证
- 环境变量 ALICLOUD_ACCESS_KEY / ALICLOUD_SECRET_KEY 与 STS 临时凭证区别
- Provider block 版本锁定策略(>=1.200.0 才支持 ACK Pro 托管版)
- ACK 资源拓扑
- alicloud_cs_managed_kubernetes:托管版集群
- alicloud_cs_kubernetes_node_pool:节点池(支持 Spot、ESS、Taints、Labels)
- alicloud_cs_autoscaling_config:节点池级 HPA 阈值
- alicloud_cs_autoscaling_config 与 cluster-autoscaler 组件的协同
- 网络与安全
- Terway ENI vs Flannel 在 PHP 长连接场景下的性能差异
- 白名单式安全组:只暴露 80/443/9000(PHP-FPM Status)
- 使用 alicloud_ram_policy 给 CI 角色最小权限:cs:Describe*,cs:ScaleCluster
- 状态与流水线
- 远端状态锁:OSS Backend + DynamoDB 模拟(国内用 Tablestore 锁)
- GitLab-CI / GitHub Action 调用 terraform plan/apply 的 Role 切换
- PHP 业务耦合
- 通过 ConfigMap 注入 PHP_INI_SCAN_DIR,实现不同版本 ini 热更新
- 节点池 Label:workload=php-api,配合 nodeSelector 把 IO 密集 Pod 绑定到 ESS 按量节点,把计算密集 Pod 绑定到 Spot 节点
- 成本治理
- 使用 alicloud_cs_kubernetes_node_pool 的 instance_types 多规格兜底,结合 preemptible_percentage 控制 Spot 比例
- 通过 alicloud_bss_openapi 拉取账单,按 namespace 打标签做成本分摊
- 灾备与可观测
- 使用 alicloud_cs_kubernetes_addon 一键安装 alicloud-monitor-controller、logtail、arms-prometheus
- 跨地域备份:通过 alicloud_cs_kubernetes_backup 做 etcd 快照并转储到 OSS 跨区域复制
- 合规
- 等保 2.0 要求:Master 节点强制加密盘,通过 encrypted_enable = true 开启
答案
下面给出一套可直接落地到生产环境的“最小可扩展” Terraform 结构,并穿插解释面试时要口头强调的细节。目录结构:
.
├── environments
│ ├── dev.tfvars
│ └── prod.tfvars
├── modules
│ ├── ack
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── network
│ └── addons
├── main.tf
├── backend.tf
└── ci.tf
- 主入口 main.tf(面试时口头说明“模块化解耦,可支持多地域多 AZ”)
provider "alicloud" {
region = var.region
# 生产环境用 assume_role 方式,避免 AK 泄露
assume_role = {
role_arn = "acs:ram::${var.account_id}:role/terraform-ci"
}
}
module "vpc" {
source = "./modules/network"
region = var.region
vpc_cidr = var.vpc_cidr
cluster_name = var.cluster_name
}
module "ack" {
source = "./modules/ack"
cluster_name = var.cluster_name
vpc_id = module.vpc.vpc_id
vswitch_ids = module.vpc.vswitch_ids
service_cidr = var.service_cidr
pod_cidr = var.pod_cidr
php_node_pool_size = var.php_node_pool_size
spot_percentage = var.spot_percentage
key_name = var.key_name
encrypted_enable = true # 等保合规
}
- modules/ack/main.tf(只展示核心资源,面试时要能讲出“为什么用节点池而不是手动加节点”)
resource "alicloud_cs_managed_kubernetes" "pro" {
name = var.cluster_name
cluster_spec = "ack.pro.small"
version = "1.26.3-aliyun.1"
vpc_id = var.vpc_id
vswitch_ids = var.vswitch_ids
service_discovery_types = ["CoreDNS"]
load_balancer_spec = "slb.s2.small"
# 托管版默认不暴露 API Server EIP,安全
is_enterprise_security_group = true
encrypted_enable = var.encrypted_enable
# 关键:Pod 与 Service CIDR 提前规划,避免和 PHP 内部微服务冲突
pod_cidr = var.pod_cidr
service_cidr = var.service_cidr
# 开启日志转存,方便 PHP 业务排障
cluster_log_ttl = 7
}
resource "alicloud_cs_kubernetes_node_pool" "php" {
cluster_id = alicloud_cs_managed_kubernetes.pro.id
name = "php-api"
vswitch_ids = var.vswitch_ids
instance_types = ["ecs.c6e.large", "ecs.c6.large"] # 多规格兜底
system_disk_category = "cloud_essd"
system_disk_size = 100
key_name = var.key_name
# 成本优化:Spot 实例
spot_strategy = "SpotWithPriceLimit"
spot_price_limit = 0.25
# 节点池级自动伸缩
scaling_config {
min_size = 2
max_size = var.php_node_pool_size
}
# 给 PHP-FPM 预留 CPU 绑定
taints {
key = "workload"
value = "php"
effect = "NoSchedule"
}
labels = {
workload = "php-api"
env = terraform.workspace
}
management {
auto_repair = true
auto_upgrade = true
surge = 1
max_unavailable = 0
}
}
- backend.tf(面试亮点:状态锁 + 多环境)
terraform {
backend "oss" {
bucket = "tf-state-ack"
prefix = "php/ack/${terraform.workspace}"
region = "cn-shanghai"
# 用 Tablestore 做行锁,避免并发 apply
tablestore_endpoint = "https://tf-lock.cn-shanghai.ots.aliyuncs.com"
tablestore_table = "tf_lock"
}
}
- ci.tf(GitLab-CI 调用示例,面试时强调“无 AK 落地”)
data "alicloud_ram_policy" "terraform_ci" {
name = "TerraformCIPolicy"
# 最小权限:仅 ACK 只读 + 节点池伸缩
}
resource "alicloud_ram_role" "ci" {
name = "terraform-ci"
document = jsonencode({
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = ["ci.aliyuncs.com"] }
}]
})
}
- 变量与输出(面试时主动说“我把 kubeconfig 加密输出到 SSM,PHP CD 系统通过 SDK 拉取”)
output "kubeconfig" {
value = alicloud_cs_managed_kubernetes.pro.kube_config
sensitive = true
}
落地步骤(面试时按 30 秒版本口述)
- 准备:在 RAM 创建 terraform-ci 角色并授予最小权限,OSS 新建 tf-state-ack 桶并开启版本控制;
- 本地 terraform workspace new prod,terraform plan -var-file=environments/prod.tfvars;
- MR 合并后,GitLab-Runner 使用 OIDC 扮演 terraform-ci 角色,自动 apply;
- 集群就绪后,通过 alicloud_cs_kubernetes_addon 安装 arms-prometheus,PHP 应用使用社区版 fpm-exporter 暴露指标;
- 节点池根据 PHP 业务高峰自动伸缩,Spot 实例被回收时,Pod 优雅终止时间设为 30s,保证正在处理的请求不丢失;
- 每周通过 Terraform 的 -target=alicloud_cs_kubernetes_backup 做 etcd 快照并转储到 OSS,保留 30 天,满足等保审计。
拓展思考
- 多集群联邦:如果 PHP 业务要做单元化部署,上海+深圳双活,可用 Terraform 的 for_each 批量创建 ack 资源,并通过 ACK One 联邦统一管理;
- GitOps 深化:用 Terraform 只负责“基础设施层”,应用层用 ArgoCD + ApplicationSet,PHP 镜像版本变动不再走 Terraform,而是走 Git 标签;
- 费用闭环:通过 alicloud_bss_openapi 把每日账单写入自建 PHP 财务系统,按 namespace+label 做成本分摊,实现“业务团队自费扩容”;
- 安全左移:在 Terraform PR 阶段使用 Checkov 扫描,禁止公网 SLB、禁止开放 0.0.0.0/0 入站,扫描结果以 JSON 回传 GitLab,MR 合并前必须清零高危;
- 灾备演练:通过 Terraform 的 -replace 参数模拟整个可用区节点池故障,验证 PHP 会话中心(Redis Cluster)跨 AZ 高可用,RTO<5min;
- 混合云:线下 IDC 已有 PHP 老集群,可通过 Terraform 创建 ACK 注册集群,把线下 Kubelet 注册到云端,实现统一运维视角;
- Serverless 趋势:PHP 业务若改造成 Knative 模式,可用 Terraform 创建 ACK Serverless 集群,并配置 alicloud_cs_serverless_kubernetes,按需付费,彻底去掉节点池维护成本。