深入 Amazon ECS Managed Daemons:daemon_bridge 网络模式、滚动部署与生命周期解耦的实现细节

上周在排查一个监控数据缺失的问题时,我发现根因居然是——某个应用团队两个月前重新部署时,不小心把 task definition 里的 Amazon CloudWatch Agent sidecar 删掉了。

两个月。整整两个月没人发现。

这件事让我下定决心,把所有 sidecar 模式部署的 Agent 迁移到 Amazon ECS 新推出的 Managed Daemons 上。折腾了一周,踩了不少坑,这篇文章把技术细节完整记录下来。

背景:sidecar 模式的系统性缺陷

在讨论 Managed Daemons 之前,有必要梳理清楚 sidecar 模式到底哪里不行。不是说它不能用,而是在规模化运维场景下,它有几个根本性的结构缺陷。

缺陷一:生命周期耦合

Agent 和应用容器共享同一个 task definition。这意味着 Agent 的任何变更(版本升级、配置修改、资源调整)都必须通过修改 task definition 来实现。而修改 task definition 就等于触发应用重新部署。

在微服务架构下,一个集群可能有上百个 task definition。每个属于不同的应用团队。平台工程师要更新 Agent,需要逐个协调。

缺陷二:资源定义混杂

sidecar Agent 的 CPU 和内存配额在应用的 task definition 里定义。应用团队在做容量规划时,要把 Agent 的开销算进去。这增加了认知负担,也容易出错。

缺陷三:无启动顺序保证

Amazon ECS 不保证同一 task 里多个容器的启动顺序(依赖关系可以配但不够可靠)。Agent 可能在应用容器之后启动,导致前几秒的日志和指标丢失。

缺陷四:规模化成本

每个应用任务一个 Agent 副本。一台实例跑 10 个任务,就有 10 个 Agent 进程。资源浪费不说,Agent 之间还可能互相争抢。

Managed Daemons 架构解析

2025 年 4 月,亚马逊云科技发布了面向 Amazon ECS 托管实例的 Managed Daemons 功能。下面从几个核心维度拆解。

daemon task definition:独立的资源类型

Managed Daemons 引入了全新的 daemon task definition 概念。它和普通 task definition 是完全独立的资源类型,有自己的参数体系和验证逻辑。

关键区别:

  • 注册入口不同(控制台有单独的"进程守护程序定义"菜单)
  • API 调用不同(register-daemon-task-definition 而非 register-task-definition
  • 部署方式不同(关联到容量提供商,而非通过 service 调度)

这种设计从根本上切断了守护进程和应用任务的耦合。

daemon_bridge 网络模式

这是 Managed Daemons 引入的新网络模式。让我详细说说它解决了什么问题。

在 sidecar 模式下,Agent 和应用共享同一个网络命名空间。Agent 可以通过 localhost 采集应用指标,但同时也意味着:

  • Agent 的端口可能和应用端口冲突
  • Agent 的网络配置变更会影响应用
  • 安全边界模糊

daemon_bridge 模式下:

  • 守护进程有独立的网络栈
  • 可以和应用任务通信(用于指标采集、日志推送等)
  • 但网络配置互相隔离,不会互相干扰

这在网络层面实现了"该通的通、该隔的隔"。

主机级访问能力

对于监控和安全代理来说,主机级访问权限是刚需。Managed Daemons 支持三种方式:

特权容器(privileged mode)

{
  "containerDefinitions": [
    {
      "name": "monitoring-agent",
      "privileged": true
    }
  ]
}

给容器完整的主机权限。适用于需要深度系统访问的场景。

Linux capabilities

比特权模式更精细的权限控制。比如只需要网络管理能力:

{
  "linuxParameters": {
    "capabilities": {
      "add": ["NET_ADMIN", "SYS_PTRACE"]
    }
  }
}

建议优先用 capabilities,而不是一上来就开 privileged。权限越小越安全。

主机文件系统挂载

{
  "mountPoints": [
    {
      "sourceVolume": "proc",
      "containerPath": "/host/proc",
      "readOnly": true
    }
  ],
  "volumes": [
    { "name": "proc", "host": { "sourcePath": "/proc" } }
  ]
}

监控 Agent 采集 CPU、内存、网络、磁盘 I/O 指标都需要读取 /proc/sys

生命周期保证

Managed Daemons 在生命周期管理上提供了三个关键保证:

  1. 先启动保证:守护进程先于应用任务启动。应用开始处理请求前,监控已经就绪。
  2. 最后耗尽:实例退出时,守护进程最后停止。确保应用停止过程中的日志和指标仍然被采集。
  3. 唯一性:每台实例只运行一个守护进程副本。不会出现多副本竞争的问题。

实操:部署 Amazon CloudWatch Agent 作为 Managed Daemon

完整的 daemon task definition

{
  "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
        },
        {
          "sourceVolume": "rootfs",
          "containerPath": "/rootfs",
          "readOnly": true
        }
      ],
      "environment": [
        {
          "name": "HOST_PROC",
          "value": "/host/proc"
        },
        {
          "name": "HOST_SYS",
          "value": "/host/sys"
        }
      ]
    }
  ],
  "volumes": [
    { "name": "proc", "host": { "sourcePath": "/proc" } },
    { "name": "sys", "host": { "sourcePath": "/sys" } },
    { "name": "rootfs", "host": { "sourcePath": "/" } }
  ],
  "executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::123456789012:role/ecsCloudWatchAgentRole"
}

这个配置比前面的示例更完整,多了:

  • /rootfs 挂载:让 Agent 能读取根文件系统(磁盘指标需要)
  • 环境变量 HOST_PROCHOST_SYS:告诉 Agent 主机 proc/sys 的挂载路径

创建和关联 daemon

# 创建配置
cat <<'EOF' > create-daemon.json
{
  "clusterArn": "arn:aws:ecs:us-east-1:123456789012:cluster/production",
  "daemonName": "cloudwatch-agent",
  "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/prod-managed-cp"
  ]
}
EOF

# 创建 daemon
aws ecs create-daemon --cli-input-json file://create-daemon.json

# 验证状态
aws ecs describe-daemons \
  --daemon-arn arn:aws:ecs:us-east-1:123456789012:daemon/production/cloudwatch-agent

滚动更新机制详解

这是我认为 Managed Daemons 设计得相当精妙的部分。

更新触发

aws ecs update-daemon \
  --daemon-arn arn:aws:ecs:us-east-1:123456789012:daemon/production/cloudwatch-agent \
  --daemon-task-definition-arn arn:aws:ecs:us-east-1:123456789012:daemon-task-definition/cloudwatch-agent-daemon:2

更新流程

  1. Amazon ECS 根据 drain 百分比(默认 25%)选择一批实例
  2. 被选中的实例进入排空状态
  3. 实例上的应用任务被迁移到其他健康实例
  4. 新实例启动,先拉起新版本守护进程
  5. 守护进程就绪后,应用任务被调度到新实例
  6. 旧实例终止
  7. 循环处理下一批

核心原则:先启动后停止。 任何时刻都有守护进程在运行。监控不中断,日志不丢失。

回滚机制

三层保护:

第一层:断路器。 部署过程中如果新版本启动失败达到阈值,自动停止部署。

第二层:Bake time。 更新完所有实例后,等待指定时间观察。这个时间窗口用来发现那些不会立即暴露的问题(比如内存泄漏、性能劣化)。

第三层:Amazon CloudWatch 告警联动。 在 bake time 期间,如果配置的 Amazon CloudWatch 告警触发,自动执行回滚。

这三层加起来,基本覆盖了从"启动就挂"到"跑一阵才出问题"的各种故障场景。

踩坑详录

坑 1:UpdateDaemon API 不保留上次配置

这个设计和大多数 AWS API 的行为不一样。每次 UpdateDaemon 必须传完整配置。

举个例子:你第一次创建时启用了 enableExecuteCommand: true。更新 daemon task definition 版本时如果没带这个参数,ECS Exec 功能会被关掉。

这个行为在官方文档的 Important 框里有说明,但说实话我第一次就没注意。

坑 2:守护进程崩溃循环会导致实例反复替换

守护进程停止 → Amazon ECS 判定实例不健康 → 排空替换 → 新实例启动 → 守护进程又崩 → 再次替换……

如果 Agent 配置有错误导致反复崩溃,这个循环很恐怖。建议上线前一定在测试环境跑稳了再推生产。

坑 3:公共 ECR 镜像的权限

public.ecr.aws 的镜像时,任务执行角色需要 ecr-public:GetAuthorizationTokenecr-public:BatchGetImage 权限。这两个权限比较容易漏。

总结

Amazon ECS Managed Daemons 从架构层面解决了 sidecar 模式在规模化运维场景下的系统性缺陷。独立的 daemon task definition、daemon_bridge 网络模式、生命周期保证、滚动部署与自动回滚——每个设计决策都指向同一个目标:让平台工程师独立管控基础设施组件。

目前已在所有亚马逊云科技区域可用,不收额外费用。

参考资料:

posted @ 2026-04-07 19:55  亚马逊云开发者  阅读(2)  评论(0)    收藏  举报