亚马逊AWS官方博客

使用Amazon CodePipeline 自动部署到 Amazon ECS: 构建高效、可靠的 CI/CD 流程

1.  概述

1.1 背景与目标

在当今快速迭代的软件开发周期中,“速度”和“稳定”是决定业务成败的两个关键因素。容器化技术(如 Docker)和编排服务(如 Amazon ECS)极大地简化了应用的打包和运行,但“如何安全、快速、可靠地将新版本的应用代码部署到生产环境”仍然是一个核心挑战。

许多团队仍然依赖手动或半自动化的脚本来执行部署。这些方法不仅耗时,而且极易出错:一个配置疏忽、一个错误的命令,都可能导致服务中断。这正是我们需要一个全自动 CI/CD (持续集成/持续交付) 流程的原因。

本文将重点介绍如何使用 AWS 托管的 CI/CD 服务套件(特别是 Amazon CodePipeline)与 Amazon ECS 相结合,实现从代码提交到生产部署的全流程自动化,特别是实现蓝/绿部署 (Blue/Green Deployment),最大限度地提高发布的可靠性。

1.2 为什么选择 CodePipeline + ECS? 方案优势解读

将 Amazon CodePipeline 与 Amazon ECS 结合使用,不仅仅是“自动化”,更是构建了一个云原生的、弹性的、可观测的交付系统。

1.2.1 自动化与一致性(The “Why”)

消除人为错误: 流程中的每一步(构建、测试、部署)都由机器严格执行预定义的规范。这消除了“我本地可以, 上生产就不行”的典型问题,以及手动操作带来的疏忽(例如,忘记更新某个环境变量)。

标准化发布流程: 无论是开发环境、测试环境还是生产环境,都遵循同一套 CI/CD 流程。这保证了跨环境的一致性,使得在生产环境中发现的问题更容易在开发环境中复现。

1.2.2 快速迭代与价值交付(The Benefit)

缩短交付周期: 开发者只需 git push,剩下的事情交给 CodePipeline。从代码提交到功能上线的时间可以从几天缩短到几分钟。

聚焦核心业务: 开发团队可以从繁琐的部署工作中解放出来,专注于编写能为客户创造价值的代码,而不是维护复杂的部署脚本。

1.2.3 可靠性:蓝/绿部署(The “How-To-Be-Safe”)

零停机部署: 这是本方案的核心优势。通过与 Amazon CodeDeploy 集成,ECS 可以实现无缝的蓝/绿部署。CodeDeploy 会启动一个全新的、与当前生产环境(“蓝”环境)并行的“绿”环境,并将新版本的应用部署在“绿”环境中。

安全可控的流量切换: 一旦“绿”环境准备就绪并通过健康检查,CodeDeploy 会通过修改 Application Load Balancer (ALB) 的监听器规则,将生产流量(例如 10%、50% 乃至 100%)逐步切换到“绿”环境。

快速一键回滚: 如果在流量切换过程中发现任何问题(例如,新版本的错误率飙升),您可以在几秒钟内将流量切回仍然在运行的“蓝”环境(旧版本),实现近乎即时的回滚,将故障影响降至最低。

1.2.4 深度集成与安全性

原生集成: CodePipeline 无缝集成了 Amazon CodeCommit (代码仓库)、Amazon CodeBuild (构建服务)、Amazon ECR (容器镜像仓库) 和 Amazon ECS (运行环境)。无需管理和集成多个第三方工具。

精细的权限管控: 所有的操作都基于 IAM 角色和策略。您可以精确控制 CodeBuild 有权推送到哪个 ECR 仓库,CodePipeline 有权部署到哪个 ECS 集群,确保了整个流程的安全性。

合规与审计: 所有的 API 调用和操作都由 Amazon CloudTrail 记录,提供了完整的审计日志,满足合规性要求。

2.  方案架构概览

目标架构:

  1. Source (源): 开发者将代码(包括 Dockerfile、yml 和 taskdef.json 模板)推送到 Amazon CodeCommit。
  2. Trigger (触发): CodeCommit 的推送事件自动触发 Amazon CodePipeline。
  3. Build (构建):
  • CodePipeline 启动 Amazon CodeBuild 项目。
  • CodeBuild 拉取源码,基于 Dockerfile 构建 Docker 镜像。
  • CodeBuild 将新镜像推送到 Amazon ECR。
  • CodeBuild 根据新镜像的 URI 更新 json 文件。
  • CodeBuild 将更新后的 json 和 appspec.yml 作为构建产物输出。
  1. Deploy (部署):
  • CodePipeline 触发 Amazon CodeDeploy。
  • CodeDeploy 读取yml 和 new-taskdef.json。
  • CodeDeploy 在 Amazon ECS 集群上执行蓝/绿部署:
    • 启动一个包含新任务定义的“绿”部署。
    • 在 ALB 上配置测试监听器(可选),允许在切换前进行最后测试。
    • 将生产流量从“蓝”环境(旧版本)切换到“绿”环境(新版本)。
    • (可选)在一段时间后自动终止“蓝”环境。

3.  详细Demo: 一步步搭建ECS 蓝/绿部署管道

目标:搭建一个 CI/CD 管道,当 index.html 发生变化时,使用蓝/绿部署策略,自动将一个新的 Nginx 容器版本部署到 ECS Fargate 集群。

前提条件

  • 一个 AWS 中国区账户(如 cn-north-1 或 cn-northwest-1)
  • 具有管理员权限的 IAM 用户(或至少有 ECS, ECR, CodePipeline, CodeBuild, CodeDeploy, VPC, ALB, IAM 的完全访问权限)
  • 本地安装并配置 Git

3.1  准备ECS运行环境 (VPC, ALB, ECS 集群)

3.1.1 创建VPC

  • 为简单起见,您可以直接使用默认 VPC。
  • 关键: 确保您的 VPC 至少有两个位于不同可用区 (AZ) 的公有子网(如果使用 Fargate 并需要拉取公网镜像)和私有子网(推荐用于运行任务)。本 Demo 为简单起见,我们假设使用公有子网,并为 Fargate 任务分配公有 IP。

3.1.2 创建ALB

  • 导航到 EC2 控制台 > 负载均衡器 > 创建负载均衡器。
  • 选择 “Application Load Balancer”。
  • 网络映射: 选择您的 VPC 和至少两个可用区的公有子网。
  • 安全组: 创建一个新的安全组,允许来自 0.0.0.0/0 的 HTTP (80) 流量。
  • 监听器和路由:
    • 创建一个监听器,协议 HTTP,端口 80。
    • 关键: 为此监听器创建一个目标组 (Target Group),命名为 tg-blue。
      • 目标类型:IP (因为我们将使用 Fargate)。
      • 协议 HTTP,端口 80。
      • 健康检查:路径 /。
    • 将监听器的默认操作设置为转发到 tg-blue
  • 创建第二个目标组:
    • 我们还需要一个用于“绿”部署的目标组。
    • 重复上述步骤,创建第二个目标组,命名为 tg-green。创建一个监听器,协议HTTP,端口8080,关联到tg-green
  • 创建 ALB。记下 ALB 的 DNS 名称。

3.2  创建ECR 镜像仓库

  • 导航到 ECR 控制台。
  • 创建仓库
  • 记下仓库的 repositoryUri(例如:[account-id].dkr---ecr---cn-north-1.amazonaws.com.rproxy.govskope.ca.cn/<namespaces/名字>)

3.3  (手动)部署“蓝”环境 (v1)

在设置管道之前,我们必须先有一个正在运行的“v1”版本(“蓝”环境)。

3.3.1创建初始 Task Definition (任务定义):

  • 在 ECS 控制台 > 任务定义 > 创建新任务定义。
  • 选择 Fargate。
  • 命名:my-app-task-def (CodeDeploy 会在此基础上创建新版本)。
  • 任务角色:留空(或 ecsTaskExecutionRole)。
  • 重要:CPU 架构设置成arm64
  • 任务执行角色:选择(或创建)ecsTaskExecutionRole(允许 ECR 拉取镜像)。
  • 任务大小:5 vCPU, 1 GB 内存。
  • 容器定义:
    • 点击 “添加容器”。
    • 容器名称:my-app-container (**重要:**这个名字必须在后续步骤中保持一致)。
    • 镜像:nginx:alpine (我们先用一个公开的镜像作为 v1,中国区可能有无法访问镜像库, 可以参考附加内容解决)。
    • 端口映射:80 / tcp。
  • 创建任务定义。它会被命名为 my-app-task-def:1。

3.3.2 创建ECS服务(service)

    • ECS 服务 > 创建集群> my-app-cluster。
    • 启动类型:FARGATE。
    • 任务定义:my-app-task-def,版本 1。
    • 服务名称:my-app-service。
    • 所需任务:1。
    • 部署类型: 选择 “Blue/green deployment (powered by Amazon CodeDeploy)”。
    • CodeDeploy 配置:
      • CodeDeploy 会自动为您创建 “CodeDeploy 应用程序” 和 “部署组”。您可以保留默认名称(例如 AppECS-my-app-cluster-my-app-service)
    • CodeDeploy Role:
  • 负载均衡:
    • 选择 “Application Load Balancer”。
    • 选择您在步骤 1 中创建的 ALB。
    • 容器: 选择 my-app-container:80。
    • 生产监听器: 选择 HTTP: 80。
    • 目标组 1 (Blue): 选择 tg-blue。
    • 目标组 2 (Green): 选择 tg-green。
  • 网络配置:
    • 选择您的 VPC 和公有子网。
    • 安全组:选择您为 ALB 创建的安全组。
    • 自动分配公有 IP: 启用 (ENABLED)。
  • 创建服务。

几分钟后,服务应变为 ACTIVE。访问您的 ALB DNS,您应该能看到 Nginx 的欢迎页面。我们的“蓝”环境 (v1) 现在已上线。

3.4  准备代码仓库和CI/CD配置文件

3.4.1创建 CodeCommit 仓库:

  • 导航到 CodeCommit 控制台 > 创建存储库 > 命名为 my-ecs-app-repo。
  • 在本地克隆该仓库:git clone [your-repo-url]
  • 进入新目录

cd my-ecs-app-repo

3.4.2在本地仓库中创建以下文件:

  • 创建 index.html: (这是我们的 v2 版本)
cat << 'EOF' > index.html
<!DOCTYPE html>
<html>
<head><title>v2.0</title></head>
<body>
    <h1>Hello World - v2.0</h1>
    <p>Deployed by Amazon CodePipeline!</p>
</body>
</html>
EOF
  • 创建 Dockerfile (适配中国区): (使用的是ECR中的模版,具体操作查看附录)
cat << 'EOF' > Dockerfile
# 使用我们 V1 的 ECR 镜像作为 V2 的基础
FROM <your account id>.dkr---ecr---cn-north-1.amazonaws.com.rproxy.govskope.ca.cn/my-hello-app:v1

# 将 V2 的 index.html 复制进去,覆盖 V1 的内容
COPY index.html /usr/share/nginx/html/
EOF
  • taskdef-template.json: (这是任务定义的模板,注意 image 字段的占位符)
cat << 'EOF' > taskdef-template.json
{
    "family": "my-app-task-def",
    "containerDefinitions": [
        {
            "name": "my-app-container",
            "image": "REPLACE_IMAGE_URI",
            "cpu": 512,
            "memory": 1024,
            "portMappings": [
                {
                    "containerPort": 80,
                    "hostPort": 80,
                    "protocol": "tcp"
                }
            ],
            "essential": true
        }
    ],
    "requiresCompatibilities": [
        "FARGATE"
    ],
    "networkMode": "awsvpc",
    "cpu": "512",
    "memory": "1024",
    "executionRoleArn": "arn:aws-cn:iam::<your account ID>:role/ecsTaskExecutionRole",
    "runtimePlatform": {
        "operatingSystemFamily": "LINUX",
        "cpuArchitecture": "ARM64"
    }
}
EOF
  • appspec.yml: (这是告诉 CodeDeploy 如何部署的文件)
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        # <TASK_DEFINITION> 是 CodePipeline 自动填充的特殊占位符
        TaskDefinition: "<TASK_DEFINITION>"
        LoadBalancerInfo:
          ContainerName: "my-app-container"
          ContainerPort: 80
  • buildspec.yml: (这是告诉 CodeBuild 如何构建的文件)
version: 0.2

phases:
  pre_build:
    commands:
      # $AWS_ACCOUNT_ID 和 $AWS_DEFAULT_REGION 是 CodeBuild 提供的环境变量
      # $ECR_REPO_NAME 是我们在 CodeBuild 项目中定义的环境变量
      - echo Logging in to Amazon ECR...
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com.rproxy.govskope.ca.cn
      - REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com.rproxy.govskope.ca.cn/$ECR_REPO_NAME
      - IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7) # 使用 Git commit ID 作为标签
  build:
    commands:
      - echo Building the Docker image...
      - docker build -t $REPOSITORY_URI:$IMAGE_TAG .
  post_build:
    commands:
      - echo Pushing the Docker image to ECR...
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - echo Preparing artifact files...
      # 1. 创建新的 taskdef.json
      - echo "Updating task definition..."
      - sed -e "s|REPLACE_IMAGE_URI|$REPOSITORY_URI:$IMAGE_TAG|" taskdef-template.json > new-taskdef.json
      # 2. 将 appspec.yml 和 new-taskdef.json 放入 artifacts
      - echo "Build complete."

artifacts:
  files:
    - new-taskdef.json
    - appspec.yml

3.4.3提交文件:

git add .
git commit -m "Initial commit v2 files"
git push origin master

3.5  创建CodeBuild 项目

3.5.1导航到 CodeBuild 控制台 > 创建构建项目。

3.5.2项目名称:ecs-app-builder。

3.5.3源:

  • 源提供程序:Amazon CodeCommit。
  • 存储库:my-ecs-app-repo。
  • 分支:main。

3.5.4环境:

  • 镜像:托管镜像。
  • 操作系统:Amazon Linux 2 (或 Ubuntu), ARM64。
  • 运行时:Standard。
  • 关键: 勾选 “Privileged” (特权),因为需要构建 Docker 镜像。

3.5.5服务角色:

  • 选择 “新建服务角色”。
  • 角色名称:codebuild-ecs-app-builder-role。

3.5.6环境变量 (重要):

  • AWS_ACCOUNT_ID: 您的账户 ID。
  • ECR_REPO_NAME: my-hello-app (步骤 2 中创建的 ECR 仓库名)。

3.5.7 BuildSpec:

  • 选择 “使用 buildspec 文件” (默认)。

3.5.8创建构建项目。

修改 CodeBuild 角色权限:

  • 构建项目创建后,它会创建一个 IAM 角色。
  • 导航到 IAM 控制台 > 角色 > 找到 codebuild-ecs-app-builder-role。
  • 添加权限: 附加策略 AmazonEC2ContainerRegistryPowerUser。这将允许 CodeBuild 登录 ECR 并推送镜像。

3.6  创建CodePipeline

这是将所有组件串联起来的最后一步。

3.6.1 CodePipeline 控制台 > 创建流水线。

流水线名称:my-ecs-app-pipeline。

服务角色:新服务角色。

阶段 1Source ()

  • 源提供程序:Amazon CodeCommit。
  • 存储库名称:my-ecs-app-repo。
  • 分支名称:main。
  • 检测选项:Amazon CloudWatch Events (推荐)。

阶段 2Build (构建)

  • 构建提供程序:Amazon CodeBuild。
  • 项目名称:ecs-app-builder (上一步创建的)。
  • 构建类型:单个构建。

阶段 3Deploy (部署)

  • 部署提供程序:Amazon ECS (Blue/Green)。
  • 区域:(您的区域)。
  • 应用程序名称: 选择由 ECS 自动创建的名称 (例如 AppECS-my-app-cluster-my-app-service)。
  • 部署组名称: 选择由 ECS 自动创建的名称 (例如 DgpECS-my-app-cluster-my-app-service)。
  • Amazon ECS 任务定义:
    • 输入构件:BuildArtifact (或您在 Build 阶段的输出构件名)。
    • 文件名:new-taskdef.json (这必须匹配yml 中 artifacts 的定义)。
    • Amazon CodeDeploy AppSpec 文件:
    • 输入构件:BuildArtifact。
    • 文件名:appspec.yml。

查看并创建管道。

3.7  触发和验证部署  (v2)

创建管道后,它会自动从 CodeCommit 拉取最新代码 (v2) 并开始执行。

观察管道:

  • Source: 变为绿色 (成功)。
  • Build: 变为蓝色 (进行中),然后变为绿色。您可以点击 “详细信息” 查看 CodeBuild 日志,确认 docker build 和 docker push 成功。
  • Deploy: 变为蓝色 (进行中)。

切换到 CodeDeploy 控制台:

  • 在 CodeDeploy > 部署 > 找到正在进行的部署。
  • 您将看到蓝/绿部署的三个步骤:
      • 步骤 1:部署开始。 CodeDeploy 正在拉取新任务定义,并在 tg-green 上启动新的 Fargate 任务。
      • 步骤 2:流量重新路由。 此时,CodeDeploy 会等待(默认 5 分钟)。
    • 步骤 3:终止原始任务。

验证 v2:

  • 在“步骤 2”期间,立即访问您的 ALB DNS。您应该仍然看到 Nginx 的默认页面 (v1)。
  • 等待 5 分钟(或您在 CodeDeploy 部署组中设置的等待时间)。
  • CodeDeploy 会自动将 ALB 监听器的规则从 tg-blue 切换到 tg-green。
  • 再次刷新 ALB DNS 页面。 您现在应该能看到:
    • Hello World – v2.0 Deployed by Amazon CodePipeline!
  • 部署成功!CodeDeploy 稍后会终止“蓝”环境 (v1) 的旧任务。

3.8  测试v3 (CI/CD 闭环)

3.8.1在本地,修改 index.html 文件:

<h1>Hello World - v3.0 - Fully Automated!</h1>

3.8.2 提交并推送代码:

git add index.html
git commit -m "Deploy v3.0"
git push origin master

3.8.3 返回 CodePipeline 控制台。您会发现管道在几秒钟内被自动触发。

3.8.4重复步骤 7 的验证过程。几分钟后,您的 ALB DNS 将自动显示 “v3.0″。

4.  结语

在本文中,我们利用 Amazon CodePipeline、CodeBuild、CodeDeploy 和 Amazon ECS (Fargate) 搭建了一条从代码提交到生产环境的全自动 CI/CD 管道。

这套方案的核心价值在于,通过自动化的蓝/绿部署,我们实现了零停机、低风险的安全发布。这不仅彻底告别了复杂易错的手动部署,还提供了近乎即时的回滚能力,确保了生产环境的稳定。

开发团队最终得以从繁琐的运维工作中解放出来,真正专注于业务创新和价值交付。这套 AWS 原生集成的 CI/CD 流程,是实现快速、可靠迭代的坚实起点。

附加内容:

中国区无法访问nginx: alpine 解决方案:

Docker pull nginx:alpine

下载镜像到本地,然后上传到ECR:

aws ecr get-login-password --region cn-north-1 | docker login --username AWS --password-stdin <your account id>.dkr---ecr---cn-north-1.amazonaws.com.rproxy.govskope.ca.cn

docker push <your account id>.dkr---ecr---cn-north-1.amazonaws.com.rproxy.govskope.ca.cn/my-hello-app:v1

配置修订版时使用:

<your account id>.dkr---ecr---cn-north-1.amazonaws.com.rproxy.govskope.ca.cn/my-hello-app:v1

注意:ecsTaskExecutionRole 需要有AmazonECSTaskExecutionRolePolicy权限

*前述特定亚马逊云科技生成式人工智能相关的服务目前在亚马逊云科技海外区域可用。亚马逊云科技中国区域相关云服务由西云数据和光环新网运营,具体信息以中国区域官网为准。

本篇作者

阎泽

西云数据技术客户经理,拥有14+年IT行业经验,熟悉网络,数据库,云计算等领域,致力为制造/零售/网络等行业头部客户,提供企业级架构、运维治理与优化、技术支持等服务。