亚马逊AWS官方博客

ECS + CodePipeline 企业轻量级容器化 CI/CD 实战—基于 GitHub + CodePipeline + CodeBuild + ECS Fargate 的全托管容器化部署方案

摘要:在企业应用现代化过程中,容器化部署已成为主流选择。然而,很多企业在评估容器编排方案时,往往默认考虑 Kubernetes(EKS),却忽视了其带来的巨大运维复杂性:Ingress Controller 配置、Helm Chart 管理、RBAC 策略、集群升级等问题让开发团队苦不堪言。对于大多数企业级 Web 应用、API 服务和微服务场景,Amazon ECS(Elastic Container Service)配合 Fargate 无服务器计算引擎,提供了一种更为轻量级、开箱即用的容器部署方案。结合 亚马逊云科技 CodePipeline 和 亚马逊云科技 CodeBuild,可以快速搭建一套完整的 CI/CD 流水线,实现代码提交即自动构建、测试、部署。本文介绍如何使用 GitHub + CodePipeline + CodeBuild + ECR + ECS Fargate 实现企业级容器化 CI/CD,实现 git push 即自动构建镜像并滚动更新线上服务——全程零停机。


一、背景介绍

在企业应用现代化过程中,容器化部署已成为主流选择。然而,很多企业在评估容器编排方案时,往往默认考虑 Kubernetes(EKS),却忽视了其带来的巨大运维复杂性:Ingress Controller 配置、Helm Chart 管理、RBAC 策略、集群升级等问题让开发团队苦不堪言。

对于大多数企业级 Web 应用、API 服务和微服务场景,Amazon ECS(Elastic Container Service)配合 Fargate 无服务器计算引擎,提供了一种更为轻量级、开箱即用的容器部署方案。结合 亚马逊云科技 CodePipeline亚马逊云科技 CodeBuild,可以快速搭建一套完整的 CI/CD 流水线,实现代码提交即自动构建、测试、部署。

本文介绍如何使用 GitHub + CodePipeline + CodeBuild + ECR + ECS Fargate 实现企业级容器化 CI/CD,实现 git push 即自动构建镜像并滚动更新线上服务——全程零停机。

二、ECS vs EKS 技术选型对比

在亚马逊云科技上运行容器化应用,主要有两种编排服务:ECS(Elastic Container Service)和 EKS(Elastic Kubernetes Service)。以下从多个维度对比两种方案:

对比维度 ECS + Fargate EKS + Kubernetes
运维复杂度 低——无需管理控制平面、etcd、升级策略 高——需维护集群升级、插件版本、RBAC、CRD
学习曲线 平缓——Task Definition + Service 概念简单 陡峭——Pod/Deployment/Service/Ingress/ConfigMap/…
负载均衡 ALB 原生对接,控制台一键关联 需配置 ALB Ingress Controller / Nginx Ingress
控制台操作 完整 GUI——集群/服务/任务全可视化管理 依赖 kubectl CLI,控制台功能有限
CI/CD 集成 CodePipeline 原生 Deploy to ECS 阶段 需额外 kubectl / Helm 脚本集成
适用场景 企业 Web/API、微服务、快速上线 大规模微服务、多租户、需 K8S 生态工具

核心结论:如果你的团队不需要 Kubernetes 生态的高级特性(如 Service Mesh、CRD 扩展、多集群联邦等),ECS + Fargate 是更务实的选择——更低的入门门槛、更少的运维负担、更快的交付速度。

三、解决方案架构

本方案采用亚马逊云科技完全托管服务构建端到端 CI/CD 流水线,架构如下:

GitHub (Private Repo)
    │ push to main
    ▼
CodePipeline [Source Stage]
    │ CodeConnections 拉取代码
    ▼
CodeBuild [Build Stage]
    │ docker build (ARM64) → push ECR
    │ 产出 imagedefinitions.json
    ▼
Manual Approval [审批门]
    │ 人工确认后继续
    ▼
CodePipeline [Deploy Stage]
    │ 注册新 Task Definition
    │ UpdateService 滚动更新
    ▼
ECS Fargate (Service)
    │ 新任务通过 ALB 健康检查后替换旧任务
    ▼
ALB → 用户流量(零停机)

核心组件职责

组件 职责
GitHub 代码托管,以 push 事件触发流水线
CodeConnections 亚马逊云科技与 GitHub 之间的安全 OAuth 连接(原 CodeStar Connections)
CodePipeline 流水线编排:Source → Build → Approval → Deploy 四阶段自动执行
CodeBuild 构建 Docker 镜像并推送到 ECR,产出 imagedefinitions.json
ECR 私有容器镜像仓库,存储每次构建的镜像版本
ECS Fargate 无服务器容器运行时,滚动更新服务实例
ALB 应用负载均衡,健康检查 + 流量切换,实现零停机部署

四、具体方案实现

以下分步骤详细介绍如何通过亚马逊云科技控制台配置完整的 CI/CD 流水线。全程可在图形化界面完成,无需编写复杂的 IaC 代码。

注:本文以 Job Board AI(基于 Next.js 的求职平台)为例,实际使用时请将相关名称替换为你的应用名称。

[图 1:CodePipeline 四阶段流水线(Source → Build → Approve → Deploy)]

4.1 前提条件

资源 要求
亚马逊云科技账户 已开通 ECS、ECR、CodePipeline、CodeBuild 服务
ECS 集群 + Service 已创建 ECS 集群和 Service(Fargate 启动类型)
GitHub 仓库 包含 buildspec.yml 和 Dockerfile(位于仓库根目录)
IAM 角色 CodeBuild 服务角色(ECR push + Logs)、CodePipeline 服务角色(ECS deploy)
ALB + Target Group 已配置 Application Load Balancer 并关联 ECS Service

4.2 第一步:创建 GitHub 连接(CodeConnections)

说明:亚马逊云科技 CodeStar 服务已于 2024-07-31 停用,GitHub 连接功能已重命名为 CodeConnections,入口位于 CodePipeline → Settings → Connections。

  1. 进入 CodePipeline 控制台 → 左下 Settings → Connections
  2. 点击 Create connection → 选择 GitHub → 填写连接名称
  3. 点击 Connect to GitHub → 弹窗中 Install a new app(安装 AWS Connect or for GitHub)→ 选择目标仓库
  4. 返回后点击 Connect → 连接状态变为 Available ✅
⚠️ 注意:
授权必须在控制台完成。CLI 创建的连接为 PENDING 状态,仍需来控制台完成 OAuth 授权。

4.3 第二步:创建 ECR 镜像仓库

在 ECR 控制台创建私有镜像仓库,用于存储每次构建的容器镜像。建议开启镜像扫描(Scan on Push)以确保安全合规:

aws ecr create-repository \
  --repository-name job-board-ai \
  --image-scanning-configuration scanOnPush=true \
  --region <your-region>

4.4 第三步:编写 buildspec.yml

buildspec.yml 是 CodeBuild 的构建指令文件,放置于仓库根目录。以下是标准模板:

version: 0.2
env:
  variables:
    IMAGE_REPO_NAME: "job-board-ai"
phases:
  install:
    runtime-versions:
      nodejs: 20
    commands:
      - npm install
  pre_build:
    commands:
      - echo "Logging in to 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
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}
  build:
    commands:
      - echo "Building Docker image..."
      - docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
      - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG \
          $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
  post_build:
    commands:
      - echo "Pushing to ECR..."
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
      - printf '[{"name":"job-board-ai-app","imageUri":"%s"}]' \
          $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG \
          > imagedefinitions.json
artifacts:
  files:
    - imagedefinitions.json

关键说明:使用 Git Commit 短哈希作为镜像 Tag,确保每次构建可追溯。post_build 阶段产出的 imagedefinitions.json 是 ECS Deploy 阶段的关键输入——它告诉 CodePipeline 用哪个新镜像更新哪个容器。

4.5 第四步:编写 Dockerfile

Dockerfile 同样放置于仓库根目录,采用多阶段构建(Multi-stage Build)减小最终镜像体积:

# ===== Stage 1: Build =====
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ===== Stage 2: Production =====
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
# 复制构建产物
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]

4.5.1 关键要点

  • 使用 alpine 基础镜像减小体积,生产镜像通常 < 200MB
  • EXPOSE 3000 必须与 Task Definition 的 containerPort 一致

4.6 第五步:配置 ECS Task Definition

Task Definition 是 ECS 的核心配置,定义容器运行的全部参数。以下是完整的 JS ON 示例:

{
  "family": "job-board-ai-web",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "1024",
  "memory": "2048",
  "executionRoleArn": "arn:aws:iam::<account>:role/ecs-execution-role",
  "taskRoleArn": "arn:aws:iam::<account>:role/ecs-task-role",
  "runtimePlatform": {
    "cpuArchitecture": "ARM64",
    "operatingSystemFamily": "LINUX"
  },
  "containerDefinitions": [
    {
      "name": "job-board-ai-app",
      "image": "<account>.dkr.ecr.<region>.amazonaws.com/job-board-ai:latest",
      "essential": true,
      "portMappings": [
        { "containerPort": 3000, "protocol": "tcp", "appProtocol": "http" }
      ],
      "healthCheck": {
        "command": ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1"],
        "interval": 30,
        "timeout": 5,
        "retries": 3,
        "startPeriod": 60
      },
      "environment": [
        { "name": "NODE_ENV", "value": "production" },
        { "name": "PORT", "value": "3000" },
        { "name": "HOSTNAME", "value": "0.0.0.0" }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/job-board-ai",
          "awslogs-region": "<region>",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
}

4.6.1 关键说明

  • name 字段必须与 buildspec.yml 产出的 imagedefinitions.json 中的 name 完全一致
  • runtimePlatform 选择 ARM64 可使用 Graviton 处理器,性价比更优(同配置约便宜 20%)
  • executionRoleArn:执行角色,用于拉取 ECR 镜像和写入 CloudWatch Logs
  • taskRoleArn:任务角色,应用运行时访问亚马逊云科技服务的权限
  • healthCheck 路径必须与 ALB Target Group 的 Health check path 保持一致
  • containerPort 必须与 Dockerfile 中 EXPOSE 的端口一致

[图 2:ECS Task Definition 控制台(Fargate / ARM64 / awsvpc)]

五、关键配置要点

以下是实践中最容易出错的配置点,建议重点检查:

配置点 正确值 配错后果
CodeBuild 镜像 aarch64(ARM64) 用 x86 镜像 → Fargate Graviton 无法拉起容器
Privileged 模式 必须勾选 不勾 → docker build 报 Cannot connect to Docker daemon
Deploy provider Amazon ECS (Standard) 选 Blue/Green → 需额外配置 CodeDeploy,复杂度陡增
Image definitions file imagedefinitions.json 名称不符 → Deploy 阶段找不到文件失败
容器名称 与 Task Definition 中容器名一致 不一致 → Deploy 找不到要替换的容器
区域一致性 ECR/CodeBuild/ECS 同区域 跨区域 → ECS 拉不到镜像

六、ALB + Target Group 配置

ECS 与 ALB 的原生集成是其相比 EKS 的核心优势之一。无需配置 Ingress Controller 或编写 YAML,在控制台即可完成全部配置:

6.1 Target Group 配置

配置项 推荐值
Target type IP(Fargate 必须选 IP 类型,非 instance)
Protocol / Port HTTP : 3000(与容器端口一致)
Health check path /api/health
Health check interval 30 秒
Healthy threshold 2 次(连续 2 次成功即判定健康)
Unhealthy threshold 3 次
Deregistration delay 60 秒(旧任务排干等待时间)

6.2 ALB 配置

配置项 推荐值
Scheme Internet-facing(对外服务)或 Internal(内部服务)
Listener HTTPS:443 → Target Group(启用 ACM 证书)
HTTP:80 重定向到 HTTPS:443
安全组 (ALB) 入站: 443/80 对 0.0.0.0/0 开放
安全组 (ECS) 入站: 3000 仅允许 ALB 安全组

6.3 ECS Service 与 ALB 关联

在 ECS Service 创建/更新时,在 Load Balancer 配置中指定:

  • Target Group: 选择上方创建的 TG
  • Container name: job-board-ai-app(与 Task Definition 中的容器名一致)
  • Container port: 3000

[图 3:ECS Service 配置页面——ALB + Target Group 关联]

滚动更新工作原理:Deploy 阶段触发 UpdateService 后,ECS 启动新任务并自动注册到 Target Group。ALB 对新任务执行健康检查,连续通过 Healthy threshold 次数后开始接收流量;同时旧任务进入 Deregistration delay 安全排干期,待现有连接完成后下线——全程零停机。

✅ 与 EKS Ingress 对比:EKS 需安装 ALB Ingress Controller、编写 Ingress YAML、管理 annotations;而 ECS 在控制台勾选即可完成相同功能——0 行 YAML,0 个额外插件。

七、CI/CD 自动更新机制

每次向 main 分支 push 代码后,完整的自动化流程如下:

  1. Source 阶段:CodeConnections 检测到 GitHub push 事件,自动拉取仓库代码打包为 artifact
  2. Build 阶段:CodeBuild 按 buildspec.yml 执行构建:npm install → docker build → push ECR → 产出 imagedefinitions.json
  3. Manual Approval:Pipeline 暂停等待审批人确认(通过 SNS/Slack 通知),点击 Approve 后继续
  4. Deploy 阶段:CodePipeline 读取 imagedefinitions.json → 注册新 Task Definition 修订版 → UpdateService 触发滚动更新
  5. 滚动更新:ECS 启动新任务,通过 ALB Target Group 健康检查后替换旧任务——全程零停机

镜像版本策略:使用 Git Commit 短哈希(前 7 位)作为镜像 Tag 而非 :latest,确保每次部署可追溯且 ECS 能检测到实际变化。回滚时只需重新部署上一个 Task Definition 修订版即可。

八、总结

本文介绍了基于 GitHub + CodePipeline + CodeBuild + ECS Fargate 的企业级轻量化 CI/CD 方案。相比 EKS + Kubernetes 的复杂架构,本方案具有以下优势:

  • 无需管理 Kubernetes 控制平面,运维成本大幅降低
  • ALB 原生对接 ECS,无需配置 Ingress Controller,控制台一键关联
  • 全程控制台图形化操作,学习曲线平缓,开发团队可快速上手
  • CodePipeline 原生支持 Deploy to ECS,无需编写额外部署脚本
  • Fargate Graviton (ARM64) 提供更优性价比,按实际资源付费,无闲置浪费
  • 基于 Git Commit 哈希的镜像版本策略,确保每次部署可追溯、可回滚
  • 滚动更新 + ALB 健康检查,实现真正的零停机部署

8.1 适用场景

  • 企业级 Web 应用和 API 服务的容器化部署
  • 中小型微服务架构(10 个以内服务)
  • 希望快速上线、简化 DevOps 流程的初创和中型企业
  • 从 VM 迁移到容器的第一步,低风险过渡

8.2 核心注意事项

  • buildspec.yml 与 Dockerfile 必须位于 GitHub 仓库根目录
  • CodeConnections 授权只能在控制台完成,无法纯 CLI
  • ECR、CodeBuild、ECS 必须在同一 Region,否则拉取镜像失败
  • imagedefinitions.json 中的容器名必须与 Task Definition 中的容器名完全一致
  • 使用 Graviton (ARM64) 时,CodeBuild 镜像也必须选择 aarch64 架构

➡️ 下一步行动:

相关产品:

相关文章:

九、引文链接

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

本篇作者

董杰

西云数据解决方案架构师,曾就职于多家外企、知名民企,主要负责支持企业客户上云,专注于亚马逊云科技解决方案设计和技术咨询,15+年软件开发、项目交付、售前咨询等丰富的行业实践建议。


AWS 架构师中心:云端创新的引领者

探索 AWS 架构师中心,获取经实战验证的最佳实践与架构指南,助您高效构建安全、可靠的云上应用