更灵活的 serverless framework 配置文件
前言
再经过前置教程的部署之后,不知道你有没有注意这样一个问题,就是我们部署的函数名,以及 API网关
的 endpoint
,它们的名称和路径都带一个 dev
?
这个就是 stage
导致的了,我们执行 sls deploy
部署的时候,由于没有指定 --stage
的参数,导致它默认就是 dev
,所以我们之前部署的函数名称,网关里面都带它。
那么它有什么作用呢?实际上这个值就是用来给你的函数,以及对应的服务去区分阶段/环境的。
比如我们一个提供 web
服务的函数,我们自己人为划分出三个环境:
dev
用于给开发者自行测试sit
用于进行集成测试prod
生产环境
不同的环境,它们各自的 API网关
分配的 endpoint
也是不同的。
假如你有一个域名,你就可以把域名多配置一些主或多级域名,把它们的
CNAME
解析到指定的API网关
地址来使用。当然你仅仅在域名控制台,直接解析是不生效的,因为API网关
有个双重验证,你必须进入API网关
的自定义域名
界面,创建自定义域名,并绑定ACM 证书
,审核通过后解析才能生效。什么是
ACM证书
?ACM
完整名称为Amazon Certificate Manager
,点击这里查看更多放心,
ACM 证书
申请和颁发非常简单,它的功能和申请流程和我们申请免费的SSL/TLS
证书是一致的,都是我们域名多解析一个CNAME
的事情。
这时候我们自然可以利用 cli option
去设置:
json
"scripts": {
"deploy:dev": "sls deploy",
"deploy:sit": "sls deploy -s sit",
"deploy:prod": "sls deploy -s prod"
},
但是随着项目的日益复杂,你会发现使用 CLI
命令,不断的去增加配置项,这种方式既繁琐,又效率低下,有什么方式可以用一个变量去控制大量的配置呢?
在serverless framework
里,通常我们可以使用 动态变量
的方式去解决这个问题。
动态变量
什么是动态变量?实际上它们就是特殊写法的字符串罢了。变量常见的写法如下所示:
yml
${variableSource}
${sls:stage}-lambdaName
${env:MY_API_KEY}
${file(create_request.json)}
${self:service}:${sls:stage}:UsersTableArn
serverless.yml
支持使用上述变量的方式,来实现配置的引用与读取,它们是非常有用的,毕竟你不可能把某些配置项,诸如 secret
什么的直接 inline
写在 yml
文件里。
其中 ${}
就是变量引用的写法,会在 sls cli
运行的时候,把它们替换成真正的值。
yml
# ${} 第二个参数是默认值
otherYamlKey: ${variableSource, defaultValue}
这里介绍一些常用的变量:
self
首先必须要讲的就是 self
了,它是我们应用配置自身其他值的关键,self
指向的就是我们 yml
配置的根节点。这里我给出一个示例,相信聪明的你可以一眼看出它的用法。
yml
service: new-service
provider: aws
custom:
globalSchedule: rate(10 minutes)
# 引用的第一行 service: new-service
serviceName: ${self:service}
# 引用的上一行 serviceName: ${self:service}
exportName: ${self:custom.serviceName}-export
functions:
hello:
handler: handler.hello
events:
# ${self:someProperty}
# self 指向的就是 yml 配置的根节点,所以才能
- schedule: ${self:custom.globalSchedule}
resources:
Outputs:
NewServiceExport:
Value: 'A Value To Export'
Export:
Name: ${self:custom.exportName}
env
顾名思义,使用系统配置的环境变量:
yml
service: new-service
provider: aws
functions:
hello:
name: ${env:FUNC_PREFIX}-hello
handler: handler.hello
这个一般配合 dotenv
等工具比较好用。
sls
这个变量可以获取一些 Serverless Core
值,比如 instanceId
和 stage
,用例如下
yml
service: new-service
provider: aws
functions:
func1:
name: function-1
handler: handler.func1
environment:
APIG_DEPLOYMENT_ID: ApiGatewayDeployment${sls:instanceId}
STAGE: ${sls:stage}
其中 ${sls:stage}
指令的实质实际上是 ${opt:stage, self:provider.stage, "dev"}
的缩写形式,所以你也明白为什么默认值是 dev
了。
opt
这个变量就是去取 CLI 传入的 Options 里面的值:
yml
service: new-service
provider: aws
functions:
hello:
name: ${opt:stage}-hello
handler: handler.hello
file
模块化配置的核心方法/变量,使用这个变量方法可以去读取文件,并进行引用,例如我们可以在这里引入另外的 yml
,json
,js
文件:
yml
# 你甚至可以引入整个yml文件作为配置
custom: ${file(./myCustomFile.yml)}
provider:
name: aws
environment:
# 引入 json 文件
MY_SECRET: ${file(./config.${opt:stage, 'dev'}.json):CREDS}
functions:
hello:
handler: handler.hello
events:
- schedule: ${file(./myCustomFile.yml):globalSchedule} # Or you can reference a specific property
world:
handler: handler.world
events:
# 甚至可以引入 `js` 文件
- schedule: ${file(./scheduleConfig.js):rate}
这时候它就会根据我们传入的 stage
参数,去读取相对路径下不同的配置文件了。
其中引入 js
文件的代码有一定的限制,它必须是 commonjs
格式,且必须导出一个js
对象,或者是导出一个function
。导出对象方式很简单且泛用性不强,我们这里以方法为例:
js
// 目前必须是 commonjs 格式,且返回一个对象作为值
// 方法同步/异步的都可以
module.exports = async ({ options, resolveVariable }) => {
// We can resolve other variables via `resolveVariable`
const stage = await resolveVariable('sls:stage');
const region = await resolveVariable('opt:region, self:provider.region, "us-east-1"');
...
// Resolver may return any JSON value (null, boolean, string, number, array or plain object)
return {
prop1: 'someValue',
prop2: 'someOther value'
}
}
我们可以在里面获取到其他的变量,并进行额外的计算处理,或者我们可以在这里请求远端获取数据作为部署的额外配置。
更多
除此之外,它还能引用到更多服务的变量,诸如 CloudFormation
,S3
,SSM
等等服务,更多更全面的变量详见:官方的变量大全地址
配置支持的其他格式
实际上目前的 serverless cli
不止可以接受 serverless.yml
,也可以接受包括 serverless.ts
, serverless.json
, serverless.js
格式。
这里笔者只推荐两种格式 serverless.yml
和 serverless.js
为什么不是 serverless.json
?因为 json
文件表现力弱,甚至要 jsonc
才支持注释,所以放弃。
为什么不是 serverless.ts
,因为直接使用会出错,详见 issues/48,不够省心。这点不如使用 js
+ jsdoc
的组合,智能提示有了,灵活性也有了。
所以我们总结一下 serverless.yml
和 serverless.js
的优点:
-
serverless.yml
: 足够简单,配合动态变量比较灵活 -
serverless.js
: 可以使用nodejs api
,也可以配合动态变量,更加灵活,而且配置可以使用原生js
写法。
这里我们以 serverless.js
配置文件为例:
智能提示, 需要
npm i -D @serverless/typescript
js
/**
* @typedef {import('@serverless/typescript').AWS} AWS
* @type {AWS}
*/
const serverlessConfiguration = {
service: 'aws-node-ts-hello-world',
frameworkVersion: '3',
provider: {
// ...
},
functions: {
// ...
},
}
module.exports = serverlessConfiguration
当然由于它是 nodejs
运行时的缘故,你可以把配置文件拆分成多个文件,再使用 commonjs
原生的方式去引用它们。你也可以使用环境变量,node:fs
,node:path
等等模块去按条件动态去生成配置文件,甚至使用某些第三方库做一些额外的工作,这实在是太灵活了!
Next Chapter
现在你已经浅尝辄止了 serverless framework 动态配置文件。
下一篇,《与传统 nodejs web 框架的结合》中,将会详细介绍如何部署 express/koa
和以它们2
个为基底的 serverless
应用,欢迎阅读。
完整示例及文章仓库地址
如果你遇到什么问题,或者发现什么勘误,欢迎提 issue
给我