教程 搜索 / 发布与基础设施 / 将容器部署到 ECS Fargate
📝 文字 ● 高级 更新于 2026-05-13

将容器部署到 ECS Fargate

五项 AWS 服务协同运行一个容器:ECR 存储镜像,ECS 调度任务,Fargate 无需管理服务器即可运行,ALB 分发流量,Route 53 + ACM 提供自定义域名和 HTTPS。生产级、可自动扩缩、原生 AWS——值得投入,前提是你打算长期使用;否则就是过度设计。

"为什么选 Fargate 而不是 Lightsail?"这是第一个值得追问的问题,因为如果你没有明确答案,那答案就是别用。Fargate 存在的意义在于:无需管理 EC2 即可自动扩缩、容器原生部署天然适配 CI 流水线,以及与 AWS 其他服务深度集成(IAM 任务角色、VPC 网络、ALB 健康检查、CloudWatch)。对于一个小型 Node API 加单个域名的场景,一台 $5 的 Lightsail 实例才是真正正确的答案,Fargate 纯属大材小用。但如果你的服务需要根据流量从 1 个实例弹性扩展到 100 个、并行运行多个版本、访问私有 VPC 资源,并在基础设施滚动更新时保持可用——Fargate 就是为此而生的。

让 AWS 文档变得可读的心智模型是:ECR(Elastic Container Registry)是容器镜像的存放地,类似私有的 Docker Hub,位于你的 AWS 账户内部。ECS(Elastic Container Service)是编排器——负责下达指令:"我需要 3 个这个容器的实例持续运行,任何宕掉的都要替换,流量只路由到健康实例上。"Fargate 是计算模式——不同于在你自己管理的 EC2 上运行 ECS,Fargate 意味着"AWS 负责在某处运行这些容器,你看不到底层服务器。"任务定义是规格说明书("用这些环境变量在这些端口上运行镜像 X")。服务表示"根据这个任务定义,在这个负载均衡器后面持续运行 N 个任务。"ALB(Application Load Balancer)是公共入口点,将流量分发到各个运行中的任务。ACM + Route 53 负责 TLS 和 DNS,与其他教程中一样。

本教程演示端到端的首次部署:将应用容器化、推送到 ECR、编写任务定义、在 ALB 后创建 Fargate 服务、配置 DNS 和证书、完成部署。每个步骤做过一次之后都是机械操作;第一次有很多活动部件需要应对。为首次部署预留两小时;后续部署(步骤 9)只需一条命令。

你将学到什么

前置条件:本地已安装 Docker、已配置 AWS CLI、有一个可容器化的 Web 应用(Node、Python、Go 等,任何监听端口的应用都可以)、域名的 DNS 托管在 Route 53(强烈推荐——非 Route 53 也可以,但操作更繁琐),以及项目中已有可用的 Dockerfile

步骤 1:容器化应用

1

先构建一个小而可用的镜像

如果你还没有 Dockerfile,这是一个合理的 Node 起点:

# Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENV PORT=8080
EXPOSE 8080
CMD ["node", "server.js"]

在本地构建并测试:

docker build -t mybrand-api:latest .
docker run -p 8080:8080 mybrand-api:latest
curl http://localhost:8080/health

如果本地跑不通,放到 Fargate 上也不会通。在往 AWS 走之前,先确保本地镜像健康。

步骤 2:创建 ECR 仓库并推送镜像

2

每个镜像对应一个仓库;默认私有

# Create the repo
aws ecr create-repository --repository-name mybrand-api --region us-east-1

# Authenticate Docker to ECR
aws ecr get-login-password --region us-east-1 \
  | docker login --username AWS --password-stdin \
    <ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com

# Tag the local image with the ECR URL
docker tag mybrand-api:latest \
  <ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/mybrand-api:latest

# Push
docker push <ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/mybrand-api:latest

完整的镜像 URI——<ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/<REPO>:<TAG>——将被任务定义引用。记下账户 ID 和区域,后面会频繁用到。

步骤 3:创建 ECS 所需的 IAM 角色

3

两个角色:执行角色与任务角色

ECS 需要两个不同的 IAM 角色,混淆它们是首次部署失败的头号原因:

  • 任务执行角色ecsTaskExecutionRole)——由 ECS/Fargate 本身使用,用于从 ECR 拉取镜像并将日志写入 CloudWatch。AWS 提供了一个托管策略(AmazonECSTaskExecutionRolePolicy),正好满足需求。
  • 任务角色——由你的应用代码在调用 AWS API 时使用(例如从 S3 读取数据)。默认为空;按需为你的应用添加策略。

每个账户只需创建一次执行角色:

aws iam create-role --role-name ecsTaskExecutionRole \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [{"Effect":"Allow","Principal":{"Service":"ecs-tasks.amazonaws.com"},"Action":"sts:AssumeRole"}]
  }'
aws iam attach-role-policy --role-name ecsTaskExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

任务角色因项目而异;如果你的服务不调用 AWS API,可以省略。

步骤 4:编写任务定义

4

描述单个运行实例的 JSON

保存为 task-definition.json

{
  "family": "mybrand-api",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "executionRoleArn": "arn:aws:iam::<ACCOUNT_ID>:role/ecsTaskExecutionRole",
  "containerDefinitions": [{
    "name": "api",
    "image": "<ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/mybrand-api:latest",
    "portMappings": [{"containerPort": 8080, "protocol": "tcp"}],
    "essential": true,
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-group": "/ecs/mybrand-api",
        "awslogs-region": "us-east-1",
        "awslogs-stream-prefix": "ecs",
        "awslogs-create-group": "true"
      }
    }
  }]
}

Fargate 最小任务规格为 256 CPU 单元(¼ vCPU)+ 512 MB 内存。大多数 API 在这个配置下运行良好。注册任务定义:

aws ecs register-task-definition --cli-input-json file://task-definition.json

步骤 5:创建集群、目标组和 ALB

5

将流量引入任务的网络层

这部分在控制台操作确实更快。AWS 控制台 → ECS → 创建集群 → 命名为 mybrand-prod → 基础设施选择 AWS Fargate(无服务器) → 创建。

然后单独创建 Application Load Balancer:EC2 → 负载均衡器 → 创建负载均衡器Application Load Balancer

  • 方案:面向互联网。IP 地址类型:IPv4。
  • VPC:使用默认 VPC。映射:至少选择两个不同可用区的子网(Fargate 强制要求)。
  • 安全组:新建一个,允许来自任意来源的入站 80 + 443 端口。
  • 监听器:HTTP:80,转发到新建的目标组 mybrand-api-tg,目标类型为 IP,协议 HTTP,端口 8080,健康检查路径 /health(或你的应用返回 200 的任意路径)。

创建完成后,ALB 会有类似 mybrand-prod-12345.us-east-1.elb.amazonaws.com 的 DNS 名称。记下它;步骤 8 会将你的域名指向它。

健康检查路径至关重要。如果你的应用没有一个返回 200 的 /health 端点,ALB 会将每个任务标记为不健康,Fargate 会循环地将其杀死。在部署之前先为你的应用添加一行健康端点。app.get('/health', (req, res) => res.send('ok')) 就足够了。

步骤 6:创建 ECS 服务

6

"在这个 ALB 后面持续运行 N 个任务"

ECS 控制台 → 你的集群 → 创建服务

  • 启动类型:FARGATE。
  • 任务定义:mybrand-api(最新修订版)。
  • 服务名称:mybrand-api
  • 所需任务数:冗余部署填 2,测试填 1。
  • 网络:默认 VPC,与 ALB 相同的两个子网,步骤 5 中创建的安全组。
  • 负载均衡:关联到 ALB。目标组:mybrand-api-tg。负载均衡的容器:api:8080

创建。Fargate 拉取镜像、启动任务,并将其注册到目标组。2–3 分钟后,服务显示"ACTIVE",目标组显示健康目标。测试:

curl http://<ALB_DNS_NAME>/health
# Expected: ok

步骤 7:申请 ACM 证书

7

与 CloudFront 不同:ALB 使用所在区域的证书

CloudFront 不同(CloudFront 只读取 us-east-1 的证书),ALB 使用与自身同区域的证书。如果你的 ALB 在 us-east-1,证书需要在 us-east-1;如果在 eu-west-2,证书就需要在那里。

aws acm request-certificate \
  --domain-name api.mybrand.com \
  --validation-method DNS \
  --region <SAME_REGION_AS_ALB>

如果 DNS 在同账户的 Route 53 中,控制台会提供"在 Route 53 中创建记录"来完成验证 CNAME。否则在你的 DNS 提供商处手动添加 CNAME。等待 ACM 显示"已颁发"——通常需要 5–15 分钟。

步骤 8:添加 HTTPS 监听器并配置 DNS

8

一个 HTTPS 监听器;一条 Route 53 别名记录

EC2 → 负载均衡器 → 你的 ALB → 监听器标签页 → 添加监听器

  • 协议:HTTPS。端口:443。
  • 默认操作:转发到同一目标组(mybrand-api-tg)。
  • SSL/TLS 证书:步骤 7 中申请的 ACM 证书。

可选:编辑 HTTP:80 监听器,将其重定向到 HTTPS:443。保存。

现在将 DNS 指向 ALB。在 Route 53 → 你的托管区 → 创建记录

  • 名称:api
  • 类型:A。别名:开启。流量路由到:Application 和 Classic Load Balancer 的别名 → 区域 → 你的 ALB。

保存。测试:curl -I https://api.mybrand.com/health。你应该看到 HTTP/2 200

步骤 9:部署循环

9

构建、推送、强制新部署

完成所有初始配置后,每次后续部署只需三条命令:

# Build the new image
docker build -t mybrand-api:latest .

# Push to ECR (auth refresh might be needed first)
docker tag mybrand-api:latest \
  <ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/mybrand-api:latest
docker push <ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/mybrand-api:latest

# Tell ECS to roll out new tasks with the same task definition (which references :latest)
aws ecs update-service \
  --cluster mybrand-prod \
  --service mybrand-api \
  --force-new-deployment

ECS 执行滚动部署:启动新任务,等待它们通过 ALB 健康检查,排空旧任务的连接,然后终止它们。默认零停机。在 ECS 控制台你的服务的部署标签页下可以观察部署进度。

回滚方法:ECS 永久保留历史任务定义修订版本。将服务更新为使用上一个修订版:aws ecs update-service --cluster mybrand-prod --service mybrand-api --task-definition mybrand-api:<PREVIOUS_REVISION>。滚动部署机制相同,只是方向反过来了。

正式工作负载中,生产任务定义避免使用 :latest如果推送尚未完成时滚动部署就已启动,可能会拉取到旧的或不完整的镜像。建议使用不可变标签(mybrand-api:<git-sha>),并在每次推送时更新任务定义。多一点工作量,少很多潜在事故。

实际费用

2 个最小规格(0.25 vCPU + 0.5 GB 内存)的 Fargate 任务全天候运行的费用:

一个小型 API 带 2 个常驻任务的现实月费:约 $35–40/月。同等配置的 Lightsail 实例是 $5–10/月。4–8 倍的成本差价,换来的是自动扩缩、滚动部署、ALB 健康检查和其余生产级基础设施。需要时物有所值,不需要时就是冤枉钱。

Fargate 不适用的场景

单实例、无扩缩需求。Lightsail 在成本和复杂度上完胜。只有在流量真正倒逼时才升级。

突发型工作负载、大量空闲时段。Fargate 按秒计费,但常驻任务一直在计费。如果你的服务大部分时间处于空闲,可以考虑 Lambda(仅按调用次数收费)或 App Runner(Fargate 加上更简洁的操作体验以及空闲时缩容至零)。

你不想运维 AWS 网络。ECS Fargate 屏蔽了 Fargate 的计算层,但仍然把 VPC、子网、安全组、目标组、监听器暴露给你。如果这些太繁琐,AWS App Runner 是对同一底层机制更薄的抽象——旋钮更少、能力相近、控制力略低。

下一步