使用 Terraform 在阿里云 ESS 中自动加入 Docker 节点
解读
国内云原生面试中,这道题考察的是“把 Docker 节点当成弹性资源”的闭环能力:
- 用 Terraform 声明式管理 IaaS 资源(ESS、ECS、VPC、SLB、RAM、AutoScaling 生命周期钩子);
- 让新弹出的 ECS 实例自动完成 Docker 安装、守护进程加固、Swarm 或 Agent 注册,并无损地接入线上集群;
- 缩容时优雅下线容器、排空节点、从集群摘除,再释放实例,保证业务无损与成本最优。
面试官最在意的是“ESS 生命周期钩子 + Terraform 本地执行器 + Docker 节点初始化”这一条链路的可靠性、幂等性与可观测性”,而不是单纯把 ECS 装个 Docker。
知识点
- Terraform 阿里云 Provider 资源:
alicloud_ess_scaling_group、alicloud_ess_scaling_configuration、alicloud_ess_lifecycle_hook、alicloud_ess_notification、alicloud_ram_role、alicloud_oss_bucket_object。 - ESS 生命周期钩子:
SCALE_OUT与SCALE_IN事件,MNS 主题 + RocketMQ 或 FunctionCompute 作为通知通道。 - Terraform local-exec / null_resource:在收到钩子消息时,本地或跳板机执行 ansible/ansible-playbook,完成 Docker 节点初始化。
- Docker 20.10+ 国内源优化:
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo,systemd 级 cgroup 驱动统一为 systemd。 - Swarm join-token 或 ACK/ASK 节点接入:通过OSS 临时凭证拉取
swarm-worker-token或kubelet-bootstrap-kubeconfig,防止敏感信息写入镜像或 user-data。 - 节点无损缩容:
docker node update --availability drain+kubectl drain前置脚本,等待 Pod 重调度完成后,再向 ESS 发送CONTINUE信号。 - Terraform 状态锁:使用阿里云 OSS Backend + DynamoDB 模拟锁(或 Terraform Cloud),保证多工程师并发安全。
- 成本治理:
alicloud_ess_scaling_rule绑定按量+Spot 混合策略,Terraform 输出节点标签,供 Prometheus 做实时成本核算。
答案
- 目录结构
terraform-ess-docker/
├── main.tf
├── variables.tf
├── templates/
│ └── worker_init.sh
├── ansible/
│ └── docker-worker.yml
└── backend.tf
- 核心 Terraform 片段(main.tf 节选)
# 1) ESS 启动模板指定实例规格、镜像、UserData 只做“最小 OS+云助手”
resource "alicloud_ess_scaling_configuration" "docker_worker" {
scaling_group_id = alicloud_ess_scaling_group.docker.id
image_id = data.alicloud_images.centos.images[0].id
instance_type = var.instance_type
security_group_id = alicloud_security_group.docker.id
user_data = base64encode(templatefile("${path.module}/templates/worker_init.sh", {}))
key_name = alicloud_key_pair.docker.key_name
active = true
force_delete = true
spot_strategy = "SpotWithPriceLimit"
spot_price_limit = var.spot_price_limit
}
# 2) 生命周期钩子:SCALE_OUT 事件发送到 MNS 主题
resource "alicloud_ess_lifecycle_hook" "scale_out" {
scaling_group_id = alicloud_ess_scaling_group.docker.id
name = "docker-node-join"
lifecycle_transition = "SCALE_OUT"
notification_metadata = jsonencode({
terraform_workspace = terraform.workspace
hook_type = "docker_swarm_join"
})
notification_arn = "acs:mns:cn-shanghai:${data.alicloud_account.current.id}:topic/ess-lifecycle"
heartbeat_timeout = 600
default_result = "CONTINUE"
}
# 3) 本地接收 MNS 消息并触发 Ansible
resource "null_resource" "docker_join" {
triggers = {
always_run = timestamp()
}
provisioner "local-exec" {
command = <<EOF
aliyun-mns-cli receive --topic ess-lifecycle --wait > /tmp/msg.json
instance_id=$(jq -r '.Message.instanceId' /tmp/msg.json)
private_ip=$(jq -r '.Message.privateIp' /tmp/msg.json)
ansible-playbook ansible/docker-worker.yml -e instance_id=$instance_id -e private_ip=$private_ip
aliyun-mns-cli ack --receiptHandle $(jq -r '.ReceiptHandle' /tmp/msg.json)
EOF
working_dir = path.module
}
}
- Ansible Playbook 关键任务(ansible/docker-worker.yml 节选)
- hosts: new_instances
gather_facts: no
vars:
swarm_manager_ip: "{{ hostvars['manager']['private_ip'] }}"
tasks:
- name: 安装 Docker CE(国内源)
yum:
name:
- docker-ce-20.10.24
- docker-ce-cli-20.10.24
- containerd.io
enablerepo: mirrors.aliyun.com_docker-ce
state: present
- name: 配置 systemd 驱动
copy:
dest: /etc/docker/daemon.json
content: |
{
"exec-opts": ["native.cgroupdriver=systemd"],
"registry-mirrors": ["https://registry.cn-hangzhou.aliyuncs.com"],
"log-driver": "json-file",
"log-opts": { "max-size": "50m", "max-file": "3" }
}
notify: restart docker
- name: 获取 swarm join-token
uri:
url: "http://oss-{{ region }}.aliyuncs.com/swarm-worker-token"
method: GET
headers:
x-oss-security-token: "{{ sts_token }}"
register: token
no_log: true
- name: 加入 swarm 集群
command: "docker swarm join --token {{ token.content }} {{ swarm_manager_ip }}:2377"
- 缩容反向流程
- 生命周期钩子
SCALE_IN触发 FunctionCompute,函数内调用 Terraform Taint + Ansible 对目标节点执行
docker node update --availability drain && docker node rm --force
完成后回调 ESSCompleteLifecycleAction。 - Terraform 侧使用
null_resource监听缩容事件,自动从 state 中移除对应资源,保证状态一致。
- 验证
terraform apply后手动调大min_size,3 分钟内新节点在 Swarm 显示 Ready;- 调小
min_size,Pod 重调度完成时间 < 30 s,节点安全释放; terraform state list | grep docker无残留地址,OSS backend 状态文件 MD5 一致。
拓展思考
- 多地域容灾:把 ESS 与 Swarm Manager 做跨地域对等连接,Terraform 用
workspace区分cn-shanghai与cn-beijing,通过 DNS 权重实现就近接入。 - 混合云场景:线下 IDC 裸金属通过 AliCloud Express Connect 加入同一个 Swarm 覆盖网络,Terraform 用 external data source 动态读取线下节点信息,统一纳管。
- 安全加固:
– 用 RAM 角色+STS 临时凭证替代 AccessKey,Terraform 端开启assume_role;
– Docker Daemon 仅监听 Unix Socket,通过 AliCloud Bastionhost + ansible_ssh_common_args 跳板运维;
– 镜像签名采用 Harbor+Notary,Terraform 在user_data里注入docker trust公钥,拒绝未签名镜像启动。 - GitOps 闭环:PR Merge 触发 GitHub Action → Terraform Cloud Run → ESS 弹性伸缩,Prometheus 监控节点 CPU 利用率 < 20% 持续 5 min 自动缩容,实现无人值守。