Serverless 架构下 CI/CD 流程怎么设计才能稳定又高效?
Serverless 应用没有服务器要管,但部署流程反而更容易出问题——函数版本混乱、环境配置泄露、上线后错误率飙升却无法快速回退。一个设计不当的 CI/CD 流程,会把 Serverless 的灵活性变成运维灾难。
Serverless CI/CD 和传统 CI/CD 有什么不同?
传统应用的 CI/CD 关注点集中在构建产物(Docker 镜像、JAR 包)和运行环境(K8s Pod、虚拟机)。Serverless 场景下,部署单元变成了函数和基础设施配置的集合,两者必须同步变更。
具体区别体现在三个层面:
- 部署粒度更细:一个 API 可能由十几个 Lambda 函数组成,每次变更可能只涉及其中一两个。传统整体构建-部署的方式会拖慢发布节奏,需要按函数粒度做增量部署。
- 基础设施即代码成为必须:API Gateway 路由、DynamoDB 表、IAM 权限这些资源和函数代码耦合在一起,任何部署都必须同时处理代码和基础设施。手动在控制台操作配置漂移是定时炸弹。
- 冷启动影响发布策略:传统应用滚动更新时新实例预热完毕才切流量,Lambda 的冷启动无法提前预热,部署策略必须把流量切换和函数预热纳入考量。
部署工具选哪个:Serverless Framework、SAM 还是 CDK?
三个工具各有定位,选错工具比没有工具更麻烦。
Serverless Framework
最易上手的选择。用 serverless.yml 声明函数和事件触发器,serverless deploy 一条命令完成部署。适合以函数为中心的纯 Serverless 应用。它的插件生态丰富,比如 serverless-python-requirements 自动打包 Python 依赖,serverless-offline 支持本地调试。
局限在于对非 Serverless 资源的管理能力偏弱,复杂 VPC 配置或跨服务编排需要大量自定义插件。另外,蓝绿部署和金丝雀发布没有原生支持,需要借助外部工具。
AWS SAM
AWS 官方的 Serverless 应用模型,在 CloudFormation 之上扩展了 AWS::Serverless::Function 等资源类型。最大优势是对 CodeDeploy 的深度集成——在模板里加一个 DeploymentPreference 就能配置金丝雀发布,不需要额外写部署逻辑。
yaml# SAM 模板中的金丝雀发布配置 MyFunction: Type: AWS::Serverless::Function Properties: CodeUri: src/ Handler: app.handler AutoPublishAlias: live DeploymentPreference: Type: Canary10Percent5Minutes Alarms: - !Ref MyFunctionErrorAlarm
适合深度绑定 AWS 生态、需要内置部署策略的团队。缺点是跨云场景不适用,学习曲线比 Serverless Framework 陡。
AWS CDK
用 TypeScript、Python 等编程语言定义基础设施,编译成 CloudFormation 模板。灵活度最高,能管理 Serverless 和非 Serverless 混合架构。CDK Pipelines 可以在代码里定义完整的 CI/CD 流水线,部署逻辑和应用逻辑放在一起维护。
代价是复杂度也最高,团队需要同时掌握编程语言和 CloudFormation 底层逻辑。适合基础设施复杂、需要精细控制的大规模项目。
怎么选?
| 场景 | 推荐工具 |
|---|---|
| 纯函数应用,快速启动 | Serverless Framework |
| AWS 原生,需要内置金丝雀发布 | SAM |
| 混合架构,需要编程式控制 | CDK |
GitHub Actions 如何集成 Serverless 部署?
GitHub Actions 是目前最常用的 Serverless CI/CD 执行引擎,原因是配置简单、和代码仓库天然集成、免费额度充足。
基本工作流
一个完整的 Serverless 部署工作流包含四个阶段:检出代码、安装依赖、运行测试、部署函数。
yamlname: Deploy Serverless on: push: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' - run: npm ci - run: npm test deploy: needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' - run: npm ci - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1 - run: npx serverless deploy --stage prod
几个关键点需要注意:
needs: test确保测试通过才部署,这是基本的安全底线。- AWS 凭证通过 GitHub Secrets 注入,绝不能硬编码在仓库里。建议为 CI/CD 创建专用 IAM 用户,只授予
lambda:UpdateFunctionCode、cloudformation:CreateChangeSet等必要权限。 - 部署命令前加上
npx可以确保使用项目本地版本的 Serverless Framework,避免全局版本不一致导致的部署失败。
多环境部署
用矩阵策略实现多环境按顺序部署:
yamljobs: deploy-dev: needs: test runs-on: ubuntu-latest steps: - run: npx serverless deploy --stage dev deploy-staging: needs: deploy-dev runs-on: ubuntu-latest steps: - run: npx serverless deploy --stage staging deploy-prod: needs: deploy-staging runs-on: ubuntu-latest steps: - run: npx serverless deploy --stage prod
开发环境自动部署,预发布和线上环境可以加上 environment 审批门控,要求人工确认后才执行。
蓝绿部署和金丝雀发布怎么做?
Serverless 场景下没有传统意义的"蓝绿服务器",但 Lambda 的版本和别名机制提供了等价能力。
Lambda 版本与别名
每次部署 Lambda 时可以发布一个不可变版本(v1、v2、v3),别名(如 PROD、STAGING)是指向特定版本的指针。切流量只需要改别名指向,不需要改 API Gateway 或 EventBridge 的配置。
金丝雀发布
通过 AWS CodeDeploy 控制流量切换比例。比如先让 10% 的流量打到新版本,观察 5 分钟,如果没有告警再逐步放大到 100%。SAM 的 DeploymentPreference 和 CDK 的 CfnDeploymentGroup 都支持这种配置。
CloudWatch 告警是金丝雀发布的安全网。配置错误率超过阈值时,CodeDeploy 自动回滚到上一个稳定版本,不需要人工介入。
蓝绿部署
Lambda 层面的蓝绿部署本质上是维护两个版本的别名,通过 API Gateway 的流量权重控制切换。和金丝雀的区别是蓝绿切换是瞬间完成的——100% 流量从旧版本切到新版本,出现问题时同样瞬间切回。
选择哪种策略取决于风险承受能力:金丝雀适合对稳定性要求极高的线上服务,蓝绿适合需要快速发布且回滚干脆的场景。
出了问题怎么回滚?
回滚策略必须在设计 CI/CD 流程时就规划好,而不是出了事故才临时想办法。
版本回滚
Lambda 每次部署生成的版本是永久的、不可变的。回滚就是把别名重新指向之前稳定版本:
bashaws lambda update-alias --name PROD --function-version 2
这条命令秒级完成,API Gateway 和事件源绑定的是别名而非版本号,所以不需要额外修改。
CloudFormation 回滚
如果用 SAM 或 CDK 部署,CloudFormation 的变更集(Change Set)机制提供了额外保护。部署前先查看变更集,确认变更内容符合预期再执行。部署失败时 CloudFormation 自动回滚到上一个稳定状态。
自动回滚
结合 CloudWatch 告警和 CodeDeploy 实现自动回滚。配置方式:
- 创建 CloudWatch 告警,监控 Lambda 错误率或执行时长
- 在 CodeDeploy 部署组中关联告警
- 部署过程中一旦告警触发,CodeDeploy 自动回滚到上一版本
这是生产环境最推荐的方式。人工监控和回滚的反应时间通常在分钟级,自动回滚可以做到秒级。
回滚注意事项
- 始终绑定事件源到别名而非
$LATEST。$LATEST会随每次更新变化,无法回滚。 - 数据库 Schema 变更不在 Lambda 回滚范围内,需要单独的数据库迁移回滚策略。
- 定期演练回滚流程,确保别名指向的旧版本在依赖没有变化的情况下仍然可用。
dev/staging/prod 环境怎么管?
环境管理不当是 Serverless 项目出事故的重灾区。开发环境随便改的配置污染了生产环境,或者三个环境的 IAM 权限不一致导致本地能跑线上挂。
独立 AWS 账号隔离
最推荐的做法是每个环境使用独立的 AWS 账号,通过 AWS Organizations 统一管理。账号级隔离确保开发环境的资源操作不可能影响生产,安全边界在最外层就建立起来了。
成本可能是一个顾虑,但 Lambda 的免费额度是按账号独立的,三个账号反而比一个账号获得更多免费额度。
资源命名规范
无论是否用独立账号,资源命名必须包含环境标识:
shellmy-api-dev-us-east-1 my-api-staging-us-east-1 my-api-prod-us-east-1
Serverless Framework 通过 stage 参数自动处理命名,SAM 和 CDK 也支持类似机制。
配置管理
每个环境维护独立的配置文件:
shellconfig.dev.json config.staging.json config.prod.json
在 Serverless Framework 中通过变量引用加载对应环境的配置:
yamlcustom: stage: ${opt:stage, 'dev'} config: ${file(./config.${self:custom.stage}.json)}
数据库连接串、第三方 API Key 等敏感配置不要放在代码仓库里,使用 AWS Secrets Manager 或 SSM Parameter Store 存储,运行时动态获取。
监控告警怎么搭?
Serverless 应用的可观测性是运维的基础。没有监控的部署等于闭着眼睛上线。
核心指标
三个必须监控的 Lambda 指标:
- 错误率:Errors 指标除以 Invocations,超过 1% 就需要告警。建议设置复合告警,错误率升高且持续 3 分钟以上才触发,避免偶发错误导致误报。
- 执行时长:Duration 指标,接近超时阈值时告警。冷启动导致的延迟尖峰也需要关注,如果某个函数冷启动频率异常,可能需要调整内存配置或使用 Provisioned Concurrency。
- 并发数:ConcurrentExecutions,接近账号配额时告警,防止雪崩。
日志聚合
Lambda 的日志默认输出到 CloudWatch Logs,但分散在多个日志组中难以关联查询。建议将日志统一汇聚到 OpenSearch 或第三方日志平台(如 Datadog、Lumigo),添加请求 ID 做分布式链路追踪。
告警渠道
告警必须推到有人响应的渠道。Slack/飞书 Webhook 是最轻量的方式,严重告警同时触发 PagerDuty 电话通知。注意告警分级——所有告警都打电话会导致告警疲劳,真正严重的问题反而被忽视。
部署监控
在 CI/CD 流程中加入部署后的自动验证:部署完成后触发冒烟测试,检查核心 API 端点返回正常,关键业务流程跑通。验证失败自动触发回滚。这一步把"部署成功"的定义从"CloudFormation 返回 COMPLETE"升级到"服务确实可用"。
设计 Serverless CI/CD 流程的核心思路:把函数、基础设施和部署策略当作一个整体来管理,用版本和别名控制流量切换,用自动告警和回滚兜底风险,用账号隔离保护环境边界。工具选型没有唯一答案,但部署安全网——版本管理、渐进发布、自动回滚、监控告警——这套机制缺一不可。