site logo

Marico's space

生产级 Serverless Web 应用 AWS 架构(CloudFront、S3、API Gateway、Lambda、DynamoDB)

服务器技术 2026-04-27 18:02:01 3

译者前言:Serverless 架构的 demo 遍地都是,但真正能上生产的少。这篇文章的作者直接给出了 CloudFront + S3 + API Gateway + Lambda + DynamoDB + Cognito + WAF 的完整方案,包括 CI/CD 分开流水线、Lambda 版本 + 别名滚动发布、SPA 路由的 CloudFront Function 代码——这是那种你拿来就能落地的生产级指南。

构建内容

为了使架构具体,我将使用一个单页 Web 应用(SPA)配合 serverless 后端:

  • 前端:React/Vite 风格 SPA,托管在 S3,通过 CloudFront 提供服务
  • 认证:Amazon Cognito User Pool(基于 JWT 的认证)
  • API:API Gateway + Lambda
  • 数据:DynamoDB

架构概览

高级组件

  • Route 53 + ACM:app.example.com 和 api.example.com 的 DNS;CloudFront 和 API 自定义域名的 TLS 证书
  • CloudFront(app 域名):从 S3 提供 SPA 资产;在边缘激进缓存静态内容;处理 SPA 路由重写;由 WAF 提供前端保护
  • S3(私有源存储桶):存储版本化前端资产(JS/CSS/images);仅通过 CloudFront Origin Access Control(OAC)访问
  • Cognito:用户登录和 JWT 颁发
  • API Gateway(api 域名):Regional 端点配合自定义域名;Cognito JWT 授权器;Lambda 集成;分阶段/方法限流
  • Lambda:无状态应用逻辑;从授权器上下文读取 JWT claims;执行授权检查
  • DynamoDB:应用数据的低延迟存储
  • CI/CD:前端和后端独立流水线;安全发布策略和回滚钩子

端到端请求流程

1) 用户加载 Web 应用

浏览器请求 https://app.example.com,请求到达 CloudFront,CloudFront 提供缓存的 index.html(短 TTL)和版本化的 JS/CSS bundles(长 TTL,不可变)。只有在缓存未命中时 CloudFront 才从 S3 获取。

2) 用户登录

前端重定向到 Cognito Hosted UI(或使用 Cognito SDK),Cognito 认证用户并返回 JWT(ID token / access token),前端安全存储令牌(强烈建议使用 Authorization Code + PKCE)。

3) 前端调用 API

浏览器调用 https://api.example.com/projects,发送 Authorization: Bearer JWT。

4) API Gateway 验证 JWT

Cognito 授权器验证签名、颁发者和受众,令牌无效/过期时提前拒绝请求。

5) Lambda 执行业务逻辑

Lambda 从 API Gateway 请求上下文读取认证上下文(用户 claims),运行应用级授权检查,读写 DynamoDB。

6) 响应返回前端

API Gateway 返回 JSON 响应,浏览器渲染更新后的 UI,可观测性数据流向 CloudWatch / X-Ray。

边缘缓存策略

我的缓存策略一句话总结

对哈希化静态资产激进缓存,但让 index.html 保持较短 TTL 以便新版本快速可见。

推荐缓存行为

index.html(短 TTL):Cache-Control: public, max-age=60, s-maxage=60,让 CloudFront 短暂缓存,同时允许新部署快速生效。

版本化资产(/assets/*.js、/assets/*.css):Cache-Control: public, max-age=31536000, immutable,文件名变化时无需失效。

SPA 路由的 CloudFront Function

对于 SPA,像 /dashboard/settings 这样的深链接应返回 index.html 而不是 S3 的 404。我通常用 CloudFront Function 解决这个问题。

按领域分离 API

随着应用增长,后端比前端更难运营。按领域边界分离 API 表面有助于管理所有权、部署和安全策略。

分离级别 1:前端 vs API 域名

至少使用:app.example.com -> CloudFront + S3,api.example.com -> API Gateway。这提供了独立的缓存和安全控制、更干净的 CORS 配置、更清晰的可观测性和事件范围。

分离级别 2:API bounded contexts

对于更大规模系统,按业务领域分离 API:users.api.example.com、projects.api.example.com、billing.api.example.com,或保持单一主机通过基础路径分割。

Cognito 认证集成

认证是"serverless demo"和"生产级"经常分道的地方。我选择对浏览器应用安全且易于在后端验证的流程。

我的 SPA 默认选择

  • Amazon Cognito User Pool
  • Authorization Code Flow 配合 PKCE
  • API Gateway 的 JWT 验证
  • Lambda 内部细粒度授权

Lambda 处理器读取 Cognito claims 示例

我不将 JWT 验证当作完整授权。Lambda 仍然检查:租户范围、角色/组 claims、资源所有权、操作级权限。那是生产安全之所在。

WAF 和限流

在生产环境中,我不依赖单一机制进行滥用保护。我组合使用 WAF、API Gateway 限流和(可选)Lambda 并发控制。

层级 1:AWS WAF(边缘和/或 API)

我用 WAF 处理常见漏洞模式(托管规则组)、IP 信誉/机器人信号、请求大小约束、对明显滥用基于速率的屏蔽、地理限制(如适用)。

层级 2:API Gateway 限流

我用 API Gateway 限流保护后端容量免受突发影响:阶段级默认值、昂贵端点的路由/方法级覆盖。

层级 3:Lambda 保留并发(断路器)

对于特别危险的功能,我有时设置保留并发来限制爆炸半径。这不是限流的替代品,但保护下游系统和预算。

部署流水线和回滚设计

这一节是我看到 demo 架构和真实运营之间最大差距的地方。

流水线分离

前端流水线(CloudFront + S3):构建静态资产 -> 运行测试/lint -> 上传版本化资产到 S3 -> 最后上传 index.html -> 使 CloudFront 的 index.html 失效 -> 冒烟测试 app.example.com

后端流水线(API Gateway + Lambda + DynamoDB 变更):运行单元/集成测试 -> 构建 Lambda artifacts -> 部署基础设施变更 -> 使用版本 + 别名部署 Lambda 代码 -> Canary/线性发布 -> 冒烟测试 api.example.com -> 自动提升或回滚

为什么最后上传 index.html

如果先上传 index.html,用户可能收到引用尚未上传资产的 HTML。最后上传哈希化资产可以防止这种竞态条件。

前端回滚策略

如果我保持部署不可变,前端回滚通常很容易:每个构建输出唯一的文件名,S3 版本控制已启用,index.html 是发布指针。回滚选项:重新上传之前的 index.html、恢复之前 S3 对象版本、重新运行之前发布 artifact 的流水线。

后端回滚策略

后端回滚更复杂,因为 API 和数据会演进。我使用 Lambda 版本 + 别名 + CodeDeploy canary/线性部署 + 自动化回滚(针对 elevated 5xx、延迟峰值、告警违规)。

实际构建顺序

  • 第 1 步:配置域名、证书和 DNS
  • 第 2 步:构建前端托管(S3 + CloudFront + OAC + SPA rewrite + WAF)
  • 第 3 步:设置 Cognito
  • 第 4 步:构建 API 层(API Gateway + Lambda + CORS)
  • 第 5 步:添加 DynamoDB
  • 第 6 步:添加 WAF 和限流
  • 第 7 步:添加可观测性
  • 第 8 步:构建 CI/CD 和回滚控制

常见错误

  • 公有不 S3 存储桶用于网站:改用私有 S3 + CloudFront Origin Access Control(OAC)
  • 像不可变资产一样缓存 index.html:导致版本陈旧和令人困惑的用户行为。改用 index.html 短 TTL,哈希化资产长 TTL
  • 一个巨大的 API 没有领域边界:随着团队成长变得难以部署、测试和安全。改用按 capability/领域分离
  • 仅 JWT 验证,无业务授权:有效令牌不等于执行操作的权限。改用 Lambda 检查 roles/tenant/资源所有权
  • 仅有 WAF,无 API 限流:WAF 很好,但不是端点级限流和后端保护的替代品。改用分层控制
  • 数据形状变更无回滚计划:这是"快速部署"变成事故的地方。改用加性 DynamoDB 变更、功能开关和分阶段迁移

最终思考

生产级 AWS Serverless Web 应用架构不是关于使用更多服务。而是选择明确的边界和安全默认值。

对我来说,制胜组合是:CloudFront + S3 用于快速、可缓存的前端交付;API Gateway + Lambda 用于清晰、独立可部署的后端逻辑;DynamoDB 用于低延迟扩展;Cognito 用于标准化认证;WAF + 限流用于分层保护;独立流水线 + 回滚设计用于运营信心。

如果我在早期就把缓存策略、认证边界和部署回滚模型做对了,随着应用增长,架构保持易于演进。