"帮忙重新部署一下,我更新了 CloudWatch Agent 版本。"
这句话我上个月说了 47 遍。不是夸张,是真数过。因为每次说完,对面的应用开发同学要么沉默三秒,要么直接回一句"排到下周吧"。
做平台工程的,谁还没经历过这种窝囊事。
我相信很多人和我一样:技术能力不差,但被组织流程卡死。明明是一个版本号的改动,硬是被协作成本放大成了一个跨团队项目。
一个 Agent 更新引发的血案
先交代下背景。我们的 Amazon ECS 集群跑着 200 多个服务。监控方案是经典的 sidecar 模式------Amazon CloudWatch Agent 塞在每个 task definition 里,跟着应用容器一起跑。
这个方案运行了一年多,看起来很美。直到有一天,亚马逊云科技发布了 CloudWatch Agent 新版本,修复了一个内存泄漏的 bug。
我想的是:太好了,升级一下就行。
现实是:
- 改 task definition → 但这玩意属于应用团队
- 提 PR → 等 review → 等合并
- 走各团队的发布流程 → 有的周发一次,有的两周一次
- 某些团队正在冻结期 → "下个季度再说"
折腾了三周,才把 60% 的服务更新了。剩下 40% 至今还跑着旧版本。
这不是技术问题,是组织协作问题。 但凶架构设计上能解决,何必去打 200 场口水仗。
亚马逊云科技终于出手了
2025 年 4 月,亚马逊云科技给 Amazon ECS 托管实例加上了 Managed Daemons(托管守护进程) 功能。
核心理念只有一个:守护进程和应用任务的生命周期彻底解耦。
什么意思?
- 平台团队有独立的 daemon task definition
- 更新 Agent 版本不需要碰应用的 task definition
- 部署、回滚、监控,全是平台团队自己的事
- 应用团队甚至可以完全不知道你更新了 Agent
就像小区物业换了个新的门禁系统,业主不需要重新装修自己家。
架构设计:四个关键点
1. 独立的 daemon task definition
这是和普通 task definition 完全分开的资源类型。有自己的参数体系和验证方案。
以前 Agent 和应用容器挤在一个 task definition 里,改 Agent 就等于改应用。现在彻底分家了。
2. daemon_bridge 网络模式
新引入的网络模式。守护进程可以和应用任务通信(采集指标、日志),但网络配置互相隔离。
采集该采集的,隔离该隔离的。边界清晰。
3. 主机级访问权限
监控 Agent 需要什么?需要看 /proc、看 /sys、访问系统调用。Managed Daemons 支持:
- 特权容器模式
- 添加 Linux capabilities
- 挂载主机文件系统路径
这些能力对安全代理和监控代理来说不可或缺。
4. 启动顺序保证
Amazon ECS 保证守护进程先于应用任务启动。应用开始接收流量之前,监控已经就绪。
不会再出现"应用跑了好几分钟才有监控数据"的窗口期。
手把手:部署你的第一个托管守护进程
前置条件
- 一个 Amazon ECS 集群
- 至少一个 Amazon ECS 托管实例容量提供商
- IAM 角色准备好(任务执行角色 + 任务角色)
创建 daemon task definition
以 Amazon CloudWatch Agent 为例,JSON 长这样:
json
{
"family": "cloudwatch-agent-daemon",
"containerDefinitions": [
{
"name": "cloudwatch-agent",
"image": "public.ecr.aws/cloudwatch-agent/cloudwatch-agent:latest",
"cpu": 1024,
"memory": 512,
"essential": true,
"privileged": true,
"mountPoints": [
{
"sourceVolume": "proc",
"containerPath": "/host/proc",
"readOnly": true
},
{
"sourceVolume": "sys",
"containerPath": "/host/sys",
"readOnly": true
}
]
}
],
"volumes": [
{ "name": "proc", "host": { "sourcePath": "/proc" } },
{ "name": "sys", "host": { "sourcePath": "/sys" } }
],
"executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::123456789012:role/ecsCloudWatchAgentRole"
}
重点说一下:
privileged: true:让 Agent 拿到主机级访问权限- 挂载
/proc和/sys:系统指标采集的标配 essential: true:守护进程挂了就触发实例替换
创建 daemon 并绑定容量提供商
准备一个配置文件 create-daemon.json:
json
{
"clusterArn": "arn:aws:ecs:us-east-1:123456789012:cluster/my-prod-cluster",
"daemonName": "cloudwatch-monitoring",
"daemonTaskDefinitionArn": "arn:aws:ecs:us-east-1:123456789012:daemon-task-definition/cloudwatch-agent-daemon:1",
"capacityProviderArns": [
"arn:aws:ecs:us-east-1:123456789012:capacity-provider/my-managed-cp"
]
}
一条命令创建:
bash
aws ecs create-daemon --cli-input-json file://create-daemon.json
完事。Amazon ECS 会自动在每台托管实例上拉起一个守护进程任务。
验证
bash
aws ecs list-daemons \
--cluster-arn arn:aws:ecs:us-east-1:123456789012:cluster/my-prod-cluster
aws ecs describe-daemons \
--daemon-arn arn:aws:ecs:us-east-1:123456789012:daemon/my-prod-cluster/cloudwatch-monitoring
状态是 Active 就 OK。
更新策略:这才是真正的杀手锏
Agent 有新版本了?一条命令全集群滚动更新:
bash
aws ecs update-daemon \
--daemon-arn arn:aws:ecs:us-east-1:123456789012:daemon/my-prod-cluster/cloudwatch-monitoring \
--daemon-task-definition-arn arn:aws:ecs:us-east-1:123456789012:daemon-task-definition/cloudwatch-agent-daemon:2
更新流程是这样的:
- 按 drain 百分比分批处理(默认 25%)
- 新实例启动新版本守护进程
- 应用任务迁移到新实例
- 旧实例关停
关键:先启动后停止。 更新过程中监控不会中断。日志不会丢。
整个滚动更新的具体流程值得展开说一下:
- 你触发更新后,Amazon ECS 按 drain 百分比选中一批实例
- 选中的实例进入排空状态,上面的应用任务开始迁移
- 新实例启动,先拉起新版本的守护进程
- 守护进程就绪后,应用任务才被调度到新实例上
- 旧实例上所有任务迁走后,旧实例被终止
- 一批完成后,处理下一批
这个过程中有两个细节我觉得设计得很好:
- 守护进程优先:无论什么时候,应用任务看到的实例上一定有健康的守护进程
- 可控节奏:drain 百分比让你决定一次处理多少实例。生产环境可以从 10% 开始,确认没问题再加速
万一新版本有问题呢?内置断路器。可以配置 bake time 和 Amazon CloudWatch 告警,更新完等一段时间,告警触发就自动回滚。
我之前踩过的那个 Agent 内存泄漏坑,要是有自动回滚,就不用半夜爬起来手动处理了。
资源对比:sidecar 模式 vs Managed Daemons
假设一台实例跑 8 个应用任务,Agent 需要 512 MB 内存:
| 指标 | sidecar 模式 | Managed Daemons |
|---|---|---|
| Agent 副本数 | 8 个 | 1 个 |
| 内存占用 | 4096 MB | 512 MB |
| 更新操作 | 改 8 个 task definition | 1 条命令 |
| 需要协调的团队 | N 个 | 0 个 |
内存省了 87.5%。但比起资源节省,协调成本降为零才是真正让人感动的。
踩坑备忘录
折腾两天总结的经验:
坑 1:UpdateDaemon 不保留配置
这个坑最隐蔽。每次 UpdateDaemon 必须传完整配置。比如上次启用了 ECS Exec,更新时不传这个参数,会被默认关掉。
坑 2:容量提供商类型搞错
capacityProviderArns 必须是 Amazon ECS 托管实例类型的容量提供商。写成 Fargate 类型会报错,错误信息还不太明显。
坑 3:角色权限遗漏
如果用公共 Amazon ECR 镜像,执行角色需要 ecr-public:GetAuthorizationToken 权限。这个权限容易漏掉。
坑 4:守护进程挂了会触发实例替换
这个是设计行为,不是 bug。守护进程被视为实例健康的判断依据。Agent 挂了 → 实例被判定不健康 → 自动排空替换。初次遇到可能会吓一跳,但其实是在保护你。
实际迁移路径:从 sidecar 到 Managed Daemon
如果你现在已经用 sidecar 跑了一堆 Agent,迁移其实不复杂。我的做法是分四步走:
第一步:盘点 Agent 清单。 把所有䷥ sidecar 方式部署的 Agent 列出来,记下版本号和更新频率。在我们团队,Amazon CloudWatch Agent 月更一次,日志采集的 Fluent Bit 两周一次。更新频率越高的优先迁移。
第二步:测试环境验证。 选一个非关键 Agent,在测试集群部署 Managed Daemon 版本。重点验证 daemon_bridge 网络模式下数据采集是否正常。我第一次测试时发现日志采集的网络路径变了,需要调整 Agent 配置中的 endpoint 地址。
第三步:双跑过渡。 生产环境先保留原有 sidecar,同时部署 Managed Daemon。两边同时采集数据,对比一段时间确认 Daemon 版本数据完整。我跑了三天双保险才放心。
第四步:移除 sidecar。 确认无误后,从应用 task definition 里移除 sidecar 容器。这一步需要应用团队配合,但好在是最后一次。以后就不用再打扰他们了。
整个迁移周期,我用了两周。主要时间花在第三步的验证上。技术操作本身很快。
谁应该关注这个功能
- 管着 10 个以上 Amazon ECS 服务的平台工程师
- 受够了协调应用团队更新 Agent 的运维
- 想用 sidecar 模式之外的方案跑日志/监控/安全代理
- 对资源利用率有追求的团队
写在最后
Amazon ECS Managed Daemons 解决的不只是技术问题,更是组织协作问题。基础设施组件的更新不应该被应用团队的发布周期绑架。
该功能已在所有亚马逊云科技区域可用。不额外收费,只攴守护进程消耗的标准计算资源费用。
现在就可以试。真的。
参考资料:
- 亚马逊云科技官博公告:aws.amazon.com/cn/blogs/ch...
- Amazon ECS Managed Daemons 文档:docs.aws.amazon.com/AmazonECS/l...
- 创建和管理守护进程指南:docs.aws.amazon.com/AmazonECS/l...