如何在单个Serverless Stack中部署多Stage?该方案是否可行?
首先明确:单个栈承载多Stage不是Serverless Framework和CloudFormation的常规设计思路,但如果有特殊需求,还是有办法实现,不过需要权衡复杂度和维护成本。
为什么默认部署会覆盖资源?
你当前的配置里,stage是动态变量,每次部署时CloudFormation会根据当前模板生成资源——如果模板里只定义了当前Stage的资源(比如lambdaName-${sls:stage}),之前部署的其他Stage资源不在本次模板中,就会被CloudFormation删除,这是栈的原子更新特性决定的。
实现单个栈多Stage的可行方法
1. 手动在模板中静态定义所有Stage的资源
把每个Stage的资源都显式写在serverless.yml里,比如同时定义dev和prod的函数、API:
service: serviceName frameworkVersion: "3" provider: name: aws runtime: nodejs18.x stackName: stackName region: eu-central-1 deploymentBucket: name: sl-deployment-bucket httpApi: shouldStartNameWithService: true functions: lambdaName-dev: name: lambdaName-dev handler: handler.handle events: - httpApi: method: get path: /dev/get-stuff lambdaName-prod: name: lambdaName-prod handler: handler.handle events: - httpApi: method: get path: /prod/get-stuff
这种方式每次部署都会同时维护两个Stage的资源,不会互相覆盖,但缺点也很明显:模板会越来越臃肿,新增Stage或修改配置时要重复操作,扩展性差。
2. 使用Serverless插件辅助生成多Stage资源
部分第三方插件(适配v3版本的serverless-plugin-multi-stage类工具)可以自动在同一个栈中生成多Stage的隔离资源,帮你处理命名后缀、配置隔离等问题。不过要注意插件的兼容性,测试是否能满足你的需求(比如是否保留历史Stage资源)。
3. 换用Lambda版本/别名替代多Stage资源
这是更推荐的折中方案:不需要在栈里搞多套资源,而是部署一套核心资源,通过Lambda的版本和别名区分环境。比如:
- 每次部署生成新的Lambda版本
- 给
dev别名指向最新版本,prod别名指向经过测试的稳定版本 - API网关可以通过不同的路径或自定义域名映射到不同的别名
这种方式既复用了同一个栈的资源,又实现了环境隔离,符合CloudFormation的设计理念,维护成本更低。
单个栈多Stage是否合理?
从最佳实践角度看,不推荐这么做,原因包括:
- CloudFormation栈是原子更新单元,多Stage资源共存会让模板复杂度陡增,容易出现配置冲突或删除错误
- 环境隔离不彻底:同一个栈的资源共享IAM角色、部署桶等底层资源,prod环境的风险可能传导到dev环境
- 不利于扩展:后续新增Stage或修改资源时,维护成本会线性上升
建议方案
如果没有特殊限制,最好还是回到常规做法:给每个Stage分配独立的栈,比如修改stackName为stackName-${sls:stage},这样每个Stage的资源完全隔离,部署、维护、排查问题都更简单。
内容的提问来源于stack exchange,提问作者Sev

