AWS云上ECS托管控制器场景服务部署策略实践和原理

Amazon ECS Service 有三种独立的部署控制器类型(deploymentController.type):

  • ECS,即ECS 自身管理部署,支持 ROLLING / BLUE_GREEN / LINEAR / CANARY 四种策略
  • CodeDeplo,即 CodeDeploy 管理蓝绿部署,有独立的 Deployment Group、AppSpec、生命周期钩子和基于 CloudWatch 告警的自动回滚
  • External,即完全由第三方控制,通过 TaskSet API 手动编排部署流程

本文主要讨论ECS控制器下的不同部署策略的问题,ECS 部署控制器有如下4种

策略 流量切换方式 回滚速度 是否需要负载均衡器 资源开销
ROLLING 逐步替换旧任务为新任务 慢(分钟级,需重新部署) 不需要 低,不需要双倍环境
BLUE_GREEN 新环境就绪后一次性切换全部流量 快(秒级,切回旧环境) 需要 ALB/NLB/Service Connect 高,需同时运行两套环境
LINEAR 按固定百分比等量递增切换(如每5分钟切10%) 快(切回旧环境) 需要 ALB/NLB/Service Connect 高,需同时运行两套环境
CANARY 先切一小部分流量(如10%),等待后一次性切剩余全部 快(切回旧环境) 需要 ALB/NLB/Service Connect 高,需同时运行两套环境

各策略适用场景

ROLLING(滚动更新,默认策略)

  • 渐进式更新,无需同时下线整个服务
  • 不想承担双倍环境的资源成本
  • 可接受较长的部署时间
  • 不需要秒级回滚
  • 不需要负载均衡器
  • 有状态应用,难以运行两套并行环境
  • 对成本敏感

BLUE_GREEN(蓝绿部署)

  • 需要在切换生产流量前验证新版本
  • 要求零停机部署
  • 需要秒级回滚能力
  • 使用 ALB/NLB/Service Connect

其中,CodeDeploy 部署控制器与 ECS 原生 BLUE_GREEN 的对比如下

ECS 原生 BLUE_GREEN CodeDeploy 蓝绿
控制器 ECS 自身 AWS CodeDeploy
配置方式 ECS Service 配置中直接设置 需额外配置 CodeDeploy Application、Deployment Group
流量切换 ECS 管理 CodeDeploy 管理,支持 Linear/Canary/AllAtOnce 策略
生命周期钩子 7 个阶段,粒度更细 2 个阶段(BeforeAllowTraffic / AfterAllowTraffic)
回滚机制 hookStatus=FAILED 或超时自动回滚 基于 CloudWatch 告警自动回滚或手动回滚
复杂度 较低 较高,需管理更多组件

LINEAR(线性部署)

  • 希望通过逐步增加流量来验证新版本
  • 需要在部署过程中持续监控指标和性能
  • 希望通过增量暴露生产流量来最小化风险

CANARY(金丝雀部署)

  • 希望先用小部分用户测试新功能
  • 需要用真实生产流量验证性能和功能
  • 希望在发现问题时最小化爆炸半径

测试环境

需要注意的是比较新版的AWSCLI 工具才支持 BLUE_GREEN/LINEAR/CANARY 策略参数,本次版本为v2.34.37。

任务定义

先用httpbin作为测试镜像,通过环境变量来区分不同的部署任务

bash 复制代码
# 基础版本 (v2)
aws ecs register-task-definition \
  --family httpbin-test \
  --requires-compatibilities FARGATE \
  --network-mode awsvpc \
  --cpu 256 --memory 512 \
  --execution-role-arn arn:aws-cn:iam::<accountid>:role/ecsTaskExecutionRole \
  --container-definitions '[{
    "name": "httpbin",
    "image": "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/kennethreitz/httpbin:latest",
    "essential": true,
    "portMappings": [{"containerPort": 80, "protocol": "tcp"}],
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-create-group": "true",
        "awslogs-group": "/ecs/httpbin-test",
        "awslogs-region": "cn-north-1",
        "awslogs-stream-prefix": "ecs"
      }
    }
  }]'

# 更新版本 (v3) - 增加环境变量区分版本
# 同上,增加 "environment": [{"name": "VERSION", "value": "v2-rolling"}]

ROLLING

创建服务

默认 ROLLING 策略

bash 复制代码
aws ecs create-service \
  --cluster workfargate \
  --service-name httpbin-test \
  --task-definition httpbin-test:2 \
  --desired-count 1 \
  --launch-type FARGATE \
  --network-configuration '{
    "awsvpcConfiguration": {
      "subnets": ["subnet-0270xxxxxxxxcdd","subnet-0270xxxxxxxxcdd"],
      "securityGroups": ["sg-096df1xxxxxxxe9"],
      "assignPublicIp": "ENABLED"
    }
  }'
触发滚动更新
bash 复制代码
aws ecs update-service \
  --cluster workfargate \
  --service httpbin-test \
  --task-definition httpbin-test:3 \
  --force-new-deployment
部署日志
复制代码
04:10:41  部署触发
          deployments:
            - PRIMARY: httpbin-test:3 (running=0, IN_PROGRESS)  ← 新版本
            - ACTIVE:  httpbin-test:2 (running=1, IN_PROGRESS)  ← 旧版本仍在运行

04:10:56  (service httpbin-test) has started 1 tasks: (task ce1f239b...)
          → 新任务启动

04:11:56  (service httpbin-test) has stopped 1 running tasks: (task 5cdc1773...)
          → 新任务健康后,停止旧任务
          deployments:
            - PRIMARY: httpbin-test:3 (running=1)  ← 新版本接管
            - DRAINING: httpbin-test:2 (running=0) ← 旧版本排空
关键观察
  • 部署方式: 先启动新任务,新任务健康后,停止旧任务
  • 双版本共存时间: 约 1 分钟(新任务启动到旧任务停止)
  • 总部署时间: 约 2 分钟
  • 不需要 ALB: 直接通过任务 IP 访问验证
  • 流量中断: 无 LB 场景下在旧任务停止和新任务就绪之间可能有短暂中断

BLUE_GREEN

先决条件

蓝绿部署需要以下额外资源:

创建 IAM Role(允许 ECS 管理 ELB)

bash 复制代码
# 创建角色
aws iam create-role \
  --role-name ECSLoadBalancerManagement \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Allow",
      "Principal": {"Service": "ecs.amazonaws.com"},
      "Action": "sts:AssumeRole"
    }]
  }'

# 附加策略
aws iam attach-role-policy \
  --role-name ECSLoadBalancerManagement \
  --policy-arn arn:aws-cn:iam::aws:policy/AmazonECSInfrastructureRolePolicyForLoadBalancers

创建 ALB

bash 复制代码
aws elbv2 create-load-balancer \
  --name httpbin-deploy-test-alb \
  --type application \
  --scheme internet-facing \
  --security-groups sg-096df1xxxxxxxe9 \
  --subnets subnet-0270xxxxxxxxcdd subnet-0270xxxxxxxxcdd

创建两个 Target Group(Blue + Green)

bash 复制代码
# Blue TG(主 Target Group)
aws elbv2 create-target-group \
  --name httpbin-bluegreen-blue \
  --protocol HTTP --port 80 \
  --vpc-id vpc-08xxxxxxxx2ae \
  --target-type ip \
  --health-check-path /get

# Green TG(备用 Target Group)
aws elbv2 create-target-group \
  --name httpbin-bluegreen-green \
  --protocol HTTP --port 80 \
  --vpc-id vpc-08xxxxxxxx2ae \
  --target-type ip \
  --health-check-path /get

创建 Listener(必须同时关联两个 TG,使用 weighted forward)

注意:Listener 的 default action 必须使用 ForwardConfig 同时包含 blue 和 green 两个 TG,否则创建服务时会报错:Both targetGroup and alternateTargetGroup must be associated with the productionListenerRule

bash 复制代码
aws elbv2 create-listener \
  --load-balancer-arn <ALB_ARN> \
  --protocol HTTP --port 80 \
  --default-actions '[{
    "Type": "forward",
    "ForwardConfig": {
      "TargetGroups": [
        {"TargetGroupArn": "<BLUE_TG_ARN>", "Weight": 100},
        {"TargetGroupArn": "<GREEN_TG_ARN>", "Weight": 0}
      ]
    }
  }]'
创建蓝绿服务
bash 复制代码
aws ecs create-service --cli-input-json '{
  "serviceName": "httpbin-bg2",
  "cluster": "workfargate",
  "taskDefinition": "httpbin-test:2",
  "desiredCount": 1,
  "launchType": "FARGATE",
  "networkConfiguration": {
    "awsvpcConfiguration": {
      "subnets": ["subnet-0270xxxxxxxxcdd", "subnet-0270xxxxxxxxcdd"],
      "securityGroups": ["sg-096df1xxxxxxxe9"],
      "assignPublicIp": "ENABLED"
    }
  },
  "deploymentController": {"type": "ECS"},
  "deploymentConfiguration": {
    "strategy": "BLUE_GREEN",
    "maximumPercent": 200,
    "minimumHealthyPercent": 100,
    "bakeTimeInMinutes": 1
  },
  "loadBalancers": [{
    "targetGroupArn": "<BLUE_TG_ARN>",
    "containerName": "httpbin",
    "containerPort": 80,
    "advancedConfiguration": {
      "alternateTargetGroupArn": "<GREEN_TG_ARN>",
      "productionListenerRule": "<LISTENER_DEFAULT_RULE_ARN>",
      "roleArn": "arn:aws-cn:iam::<accountid>:role/ECSLoadBalancerManagement"
    }
  }]
}'

部署配置

触发蓝绿部署
bash 复制代码
aws ecs update-service \
  --cluster workfargate \
  --service httpbin-bg2 \
  --task-definition httpbin-test:3 \
  --force-new-deployment
部署日志
复制代码
04:27:10  初始部署完成 (httpbin-test:2)
          (service httpbin-bg2) has reached a steady state.
          (service httpbin-bg2) deployment completed.

04:28:10  触发蓝绿部署 → httpbin-test:3
          deployments:
            - PRIMARY: httpbin-test:3 (running=0, IN_PROGRESS)  ← 绿色(新版本)
            - ACTIVE:  httpbin-test:2 (running=1, COMPLETED)    ← 蓝色(旧版本)

04:28:33  (service httpbin-bg2) has started 1 tasks: (task 9cf4bc5c...)
          → 绿色任务启动

04:29:02  (service httpbin-bg2) registered 1 targets in (target-group httpbin-bluegreen-blue)
          → 绿色任务注册到 blue TG
          此时两个版本同时运行: running=2
            - PRIMARY: httpbin-test:3 (running=1)  ← 绿色就绪
            - ACTIVE:  httpbin-test:2 (running=1)  ← 蓝色仍在运行

04:30:53  (service httpbin-bg2) has stopped 1 running tasks: (task a5794ceb...)
          → 生产流量已切换,蓝色任务停止

04:31:03  (service httpbin-bg2) deregistered 1 targets in (target-group httpbin-bluegreen-green)
          (service httpbin-bg2, taskSet ecs-svc/...) has begun draining connections on 1 tasks.
          → 旧任务从 green TG 注销,开始排空连接

04:31:54  (service httpbin-bg2) deployment completed.
          (service httpbin-bg2) has reached a steady state.
          deployments:
            - PRIMARY: httpbin-test:3 (running=1, COMPLETED)  ← 只剩新版本
关键观察
  • 部署流程: 启动绿色任务 → 注册到 TG → 健康检查通过 → 切换流量 → bake time → 停止蓝色任务
  • 双版本共存时间: 约 2 分钟(从绿色任务启动到蓝色任务停止)
  • 总部署时间: 约 3.5 分钟(含 1 分钟 bake time)
  • 零停机: 流量在 ALB 层面切换,用户无感知
  • bakeTimeInMinutes: 设为 1 分钟,生产流量切换后等待 1 分钟再终止旧版本
  • TG 交替使用: 初始部署用 green TG,蓝绿更新时新版本注册到 blue TG,下次部署会反过来

LINEAR

与蓝绿部署相同,需要 ALB + 双 TG + Listener(weighted forward)+ IAM Role。

bash 复制代码
# 创建 Blue/Green TG
aws elbv2 create-target-group --name httpbin-linear-blue --protocol HTTP --port 80 \
  --vpc-id vpc-08xxxxxxxx2ae --target-type ip --health-check-path /get
aws elbv2 create-target-group --name httpbin-linear-green --protocol HTTP --port 80 \
  --vpc-id vpc-08xxxxxxxx2ae --target-type ip --health-check-path /get

# 创建 Listener(必须同时包含两个 TG)
aws elbv2 create-listener --load-balancer-arn <ALB_ARN> --protocol HTTP --port 8081 \
  --default-actions '[{
    "Type": "forward",
    "ForwardConfig": {
      "TargetGroups": [
        {"TargetGroupArn": "<BLUE_TG_ARN>", "Weight": 100},
        {"TargetGroupArn": "<GREEN_TG_ARN>", "Weight": 0}
      ]
    }
  }]'
创建线性部署服务
bash 复制代码
aws ecs create-service --cli-input-json '{
  "serviceName": "httpbin-linear",
  "cluster": "workfargate",
  "taskDefinition": "httpbin-test:2",
  "desiredCount": 1,
  "launchType": "FARGATE",
  "networkConfiguration": {
    "awsvpcConfiguration": {
      "subnets": ["subnet-0270xxxxxxxxcdd", "subnet-0270xxxxxxxxcdd"],
      "securityGroups": ["sg-096df1xxxxxxxe9"],
      "assignPublicIp": "ENABLED"
    }
  },
  "deploymentController": {"type": "ECS"},
  "deploymentConfiguration": {
    "strategy": "LINEAR",
    "maximumPercent": 200,
    "minimumHealthyPercent": 100,
    "bakeTimeInMinutes": 0,
    "linearConfiguration": {
      "stepPercent": 50,
      "stepBakeTimeInMinutes": 1
    }
  },
  "loadBalancers": [{
    "targetGroupArn": "<BLUE_TG_ARN>",
    "containerName": "httpbin",
    "containerPort": 80,
    "advancedConfiguration": {
      "alternateTargetGroupArn": "<GREEN_TG_ARN>",
      "productionListenerRule": "<LISTENER_DEFAULT_RULE_ARN>",
      "roleArn": "arn:aws-cn:iam::<accountid>:role/ECSLoadBalancerManagement"
    }
  }]
}'

关键参数:

  • stepPercent: 50 --- 每步切换 50% 流量
  • stepBakeTimeInMinutes: 1 --- 每步之间等待 1 分钟

部署配置

触发线性部署
bash 复制代码
aws ecs update-service --cluster workfargate --service httpbin-linear \
  --task-definition httpbin-test:3 --force-new-deployment
部署日志与 ALB 权重变化
复制代码
时间        green TG 权重    blue TG 权重    说明
─────────────────────────────────────────────────────────────────
05:24:05    100              0               初始状态:旧版本在 green TG
05:24:24    100              0               新任务启动中
05:24:42    100              0               新任务注册到 blue TG
05:25:01    100              0               新任务 running=1,双版本共存
05:25:20    500              500             ★ 第1步:流量 50/50 切换!
05:25:39    500              500             bake time 等待中...
05:25:57    500              500             bake time 等待中...
05:26:16    500              500             bake time 1 分钟到期
05:26:35    0                100             ★ 第2步:流量 100% 切到新版本!
05:26:54    0                100             旧任务停止
05:27:12    0                100             旧任务排空连接 (DRAINING)
05:28:09    0                100             部署完成 (COMPLETED)
底层机制分析

ECS 通过修改 ALB Listener Rule 的 Target Group 权重来实现流量切换:

  1. 初始状态:green TG weight=100, blue TG weight=0(旧版本 100% 流量)
  2. 新任务启动并注册到 blue TG,健康检查通过
  3. 第 1 步:ECS 修改权重为 green=500, blue=500(各 50%,对应 stepPercent=50)
  4. 等待 stepBakeTimeInMinutes=1 分钟
  5. 第 2 步:ECS 修改权重为 green=0, blue=100(新版本 100%)
  6. 旧任务从 green TG 注销,排空连接后停止

注意:权重使用 500:500 而非 50:50,这是 ALB 的权重表示方式,效果相同(按比例分配)。

关键观察
  • 流量切换是渐进的: 50% → 100%,每步之间有 bake time
  • 总部署时间: 约 4 分钟(含 1 分钟 bake time)
  • 如果 stepPercent=25: 流量会按 25% → 50% → 75% → 100% 四步切换
  • 回滚: 在任何步骤中如果检测到问题,可以立即将权重切回旧版本

CANARY

前置条件

与蓝绿/线性部署相同,需要 ALB + 双 TG + Listener + IAM Role。

bash 复制代码
# 创建 Blue/Green TG
aws elbv2 create-target-group --name httpbin-canary-blue --protocol HTTP --port 80 \
  --vpc-id vpc-08xxxxxxxx2ae --target-type ip --health-check-path /get
aws elbv2 create-target-group --name httpbin-canary-green --protocol HTTP --port 80 \
  --vpc-id vpc-08xxxxxxxx2ae --target-type ip --health-check-path /get

# 创建 Listener
aws elbv2 create-listener --load-balancer-arn <ALB_ARN> --protocol HTTP --port 8082 \
  --default-actions '[{
    "Type": "forward",
    "ForwardConfig": {
      "TargetGroups": [
        {"TargetGroupArn": "<BLUE_TG_ARN>", "Weight": 100},
        {"TargetGroupArn": "<GREEN_TG_ARN>", "Weight": 0}
      ]
    }
  }]'
创建金丝雀部署服务
bash 复制代码
aws ecs create-service --cli-input-json '{
  "serviceName": "httpbin-canary",
  "cluster": "workfargate",
  "taskDefinition": "httpbin-test:2",
  "desiredCount": 1,
  "launchType": "FARGATE",
  "networkConfiguration": {
    "awsvpcConfiguration": {
      "subnets": ["subnet-0270xxxxxxxxcdd", "subnet-0270xxxxxxxxcdd"],
      "securityGroups": ["sg-096df1xxxxxxxe9"],
      "assignPublicIp": "ENABLED"
    }
  },
  "deploymentController": {"type": "ECS"},
  "deploymentConfiguration": {
    "strategy": "CANARY",
    "maximumPercent": 200,
    "minimumHealthyPercent": 100,
    "bakeTimeInMinutes": 0,
    "canaryConfiguration": {
      "canaryPercent": 20,
      "canaryBakeTimeInMinutes": 1
    }
  },
  "loadBalancers": [{
    "targetGroupArn": "<BLUE_TG_ARN>",
    "containerName": "httpbin",
    "containerPort": 80,
    "advancedConfiguration": {
      "alternateTargetGroupArn": "<GREEN_TG_ARN>",
      "productionListenerRule": "<LISTENER_DEFAULT_RULE_ARN>",
      "roleArn": "arn:aws-cn:iam::<accountid>:role/ECSLoadBalancerManagement"
    }
  }]
}'

关键参数:

  • canaryPercent: 20 --- 金丝雀阶段切 20% 流量到新版本
  • canaryBakeTimeInMinutes: 1 --- 金丝雀阶段等待 1 分钟后再全量切换

部署配置

负载均衡器配置

触发金丝雀部署
bash 复制代码
aws ecs update-service --cluster workfargate --service httpbin-canary \
  --task-definition httpbin-test:3 --force-new-deployment
部署日志与 ALB 权重变化
复制代码
时间        blue TG 权重     green TG 权重    说明
─────────────────────────────────────────────────────────────────
05:30:57    0                100              初始状态:旧版本在 green TG
05:31:17    0                100              新任务启动,注册到 blue TG
05:31:37    0                100              新任务 running=1,双版本共存
05:31:57    200              800              ★ 金丝雀阶段:20% 流量切到新版本!
05:32:17    200              800              canaryBakeTime 等待中...
05:32:36    200              800              canaryBakeTime 等待中...
05:32:56    200              800              canaryBakeTime 等待中...
05:33:16    100              0               ★ 全量切换:100% 流量切到新版本!
05:33:36    100              0               旧任务停止中
05:33:56    100              0               旧任务排空连接 (DRAINING)
05:34:56    100              0               部署完成 (COMPLETED)
底层机制分析

CANARY 与 LINEAR 的核心区别在于流量切换模式:

  1. 初始状态:green TG weight=100, blue TG weight=0
  2. 新任务启动并注册到 blue TG,健康检查通过
  3. 金丝雀阶段:ECS 修改权重为 blue=200, green=800(即 20%:80%,对应 canaryPercent=20)
  4. 等待 canaryBakeTimeInMinutes=1 分钟(此时可监控指标、验证新版本)
  5. 全量切换:ECS 修改权重为 blue=100, green=0(新版本 100%)
  6. 旧任务从 green TG 注销,排空连接后停止
关键观察
  • 两步切换: 先切小流量试探(20%),没问题后直接全量(100%)
  • 总部署时间: 约 4 分钟(含 1 分钟 canary bake time)
  • 与 LINEAR 对比: LINEAR 是均匀递增(50→100),CANARY 是先试探再全量(20→100)
  • 适合场景: 想用少量真实流量验证新版本,但不需要像 LINEAR 那样多步渐进

底层实现机制总结

四种部署策略的底层实现机制:

ROLLING --- 任务替换
复制代码
不涉及 ALB 权重操作。
ECS 直接管理任务的启动和停止:
  1. 启动新任务
  2. 新任务健康后,停止旧任务
  3. 重复直到所有任务替换完成
BLUE_GREEN / LINEAR / CANARY --- ALB Listener Rule 权重
复制代码
三者共享相同的底层机制:通过修改 ALB Listener Rule 的 ForwardConfig.TargetGroups 权重来控制流量分配。

区别仅在于权重变化的模式:

BLUE_GREEN:  0:100 ──────────────────────→ 100:0    (一步到位)
LINEAR:      0:100 → 500:500 → (bake) → 100:0      (等量递增)
CANARY:      0:100 → 200:800 → (bake) → 100:0      (先试探再全量)
权重表示方式

ECS 使用 ALB 的权重比例来表示流量百分比:

配置百分比 ALB 权重表示
20% : 80% 200 : 800
50% : 50% 500 : 500
100% : 0% 100 : 0
TG 交替使用

ECS 会交替使用 blue 和 green TG:

  • 第 1 次部署:旧版本在 green TG,新版本注册到 blue TG
  • 第 2 次部署:旧版本在 blue TG,新版本注册到 green TG
  • 以此类推
相关推荐
yunson_Liu2 小时前
AWS EKS创建EFS存储类
云计算·aws
TG_yunshuguoji2 小时前
亚马逊云代理商:如何在AWS上部署Hermes Agent?
人工智能·云计算·aws·hermes agent·hermes
code_li2 小时前
阿里云 OSS(对象存储)接入 Cloudflare 回源流量全免,每月 1 亿次免费请求
阿里云·云计算
陈皮糖..13 小时前
27 届运维实习笔记|第三、四周:从流程熟练到故障排查,企业运维实战深化
运维·笔记·sql·nginx·ci/cd·云计算·jenkins
ZStack开发者社区14 小时前
DeepSeek-V4首发即支持,ZStack AIOS 私有化部署即刻可用
人工智能·开源·云计算
阿乔外贸日记20 小时前
土耳其包装市场需求缺口分析
大数据·人工智能·物联网·搜索引擎·云计算
阿里-于怀1 天前
【无标题】阿里云 AI 网关支持 DeepSeek V4
人工智能·阿里云·云计算·deepseek
easy_coder1 天前
一次部署阻塞的根因分析:自动提交与手动提交链路混用的代价
运维·云计算
新知图书1 天前
通过阿里云百炼平台调用DeepSeek大模型
人工智能·阿里云·云计算·langchian