一路踩坑
对于一个现有的 nestjs 项目,如何在阿里云上进行函数部署
Serverless Devs
按照官方推荐,使用 Serverless Devs,
具体而言,先全局安装 npm install @serverless-devs/s -g
,然后在项目中添加 s.yaml
配置文件。
配置文件详细说明:
Yaml规范 - Serverless Devs
fc/docs/zh/yaml at main · devsapp/fc
但是这个是完整版本的配置,初次使用完全是懵的,所以,有个偷懒的办法是,使用现成的组件,生成一个 yaml 文件,然后复制粘贴过来。
Serverless Regsitry - Serverless package management platform
找一个测试目录,运行 s init start-nest -d start-nest
,就能得到一个新的 nestjs 模板。
edition: 1.0.0
name: web-framework-app
# access 是当前应用所需要的密钥信息配置:
# 密钥配置可以参考:https://www.serverless-devs.com/serverless-devs/command/config
# 密钥使用顺序可以参考:https://www.serverless-devs.com/serverless-devs/tool#密钥使用顺序与规范
access: "xxxxxx"
vars: # 全局变量
region: "cn-hangzhou"
functionName: "start-nest"
service:
name: "web-framework-1jl5"
description: 'Serverless Devs Web Framework Service'
services:
framework: # 业务名称/模块名称
# 如果只想针对 framework 下面的业务进行相关操作,可以在命令行中加上 framework,例如:
# 只对framework进行构建:s framework build
# 如果不带有 framework ,而是直接执行 s build,工具则会对当前Yaml下,所有和 framework 平级的业务模块(如有其他平级的模块,例如下面注释的next-function),按照一定顺序进行 build 操作
component: fc # 组件名称,Serverless Devs 工具本身类似于一种游戏机,不具备具体的业务能力,组件类似于游戏卡,用户通过向游戏机中插入不同的游戏卡实现不同的功能,即通过使用不同的组件实现不同的具体业务能力
actions: # 自定义执行逻辑,关于actions 的使用,可以参考:https://www.serverless-devs.com/serverless-devs/yaml#行为描述
pre-deploy: # 在deploy之前运行
- run: npm install --registry=https://registry.npmmirror.com # 要执行的系统命令,类似于一种钩子的形式
path: . # 执行系统命令/钩子的路径
- run: npm run build
path: .
props: # 组件的属性值
region: ${vars.region} # 关于变量的使用方法,可以参考:https://www.serverless-devs.com/serverless-devs/yaml#变量赋值
service: ${vars.service}
function:
name: ${vars.functionName}
description: 'Serverless Devs Web Framework Function'
codeUri: .
runtime: custom
memorySize: 512
timeout: 6
caPort: 9000
customRuntimeConfig:
command:
- ./bootstrap
triggers:
- name: http-trigger
type: http
config:
authType: anonymous
methods:
- GET
- POST
customDomains:
- domainName: auto
protocol: HTTP
routeConfigs:
- path: /*
有如下关键信息需要修改
账号相关
最终的目标,是通过 s deploy
这个命令,就可以一键部署到 aliyun 的函数服务,所以,先要配置阿里云的访问权限。
第一步 创建账号,配置 AccessKey SecretKey
在 RAM 访问控制 中创建账号,但是,这个账号创建之后,并没有部署函数服务的权限
创建账号之后,使用 s config add
命令,在本地添加 AccessKey SecretKey,这里会让你配置一个别名,s.yaml
中会用到,如这里的别名为 ali-fc-account
第二步 给账号权限
在权限策略中,创建一个自定义的权限策略,选上 FC(函数计算)相关的权限,
回到用户界面,为创建的用户添加权限,选择刚刚创建的权限策略。
或者,也可以不自定义策略,参考 AliyunFCServerlessDevsRole
的权限,给账号授予相关的权限。
授权之后,使用 s deploy
推送部署到阿里云,才不会报错。
未授权时的错误提示
POST /services failed with 403. requestid: 1-64d1fbb6-398045ba8576a96c74a485c9, message: the caller is not authorized to perform 'fc:CreateService' on resource 'acs:fc:cn-hangzhou:1111:services/serverless-devs-check' with condition '[fc:EnableServiceInternetAccess=unknown, fc:EnableServiceSLSLogging=unknown]'.
第三步 修改 s.yaml 中的配置
其实就是 access: "ali-fc-account"
这一行,其中 ali-fc-account 为使用 s config add
时,配置的别名
s.yaml 中的路径配置
使用 s init start-nest -d start-nest
生成的项目模板中,将源码放在了 code 子目录中,不是很喜欢,所以我将其移动到了根目录,那这里的几个路径,也需要跟着修改。但你如果不在意,建议别改,后面有坑。
其它文件
除了在项目中添加 s.yaml
文件,还需要添加 .fcignore
文件,在部署打包时,忽略不需要的文件。一般配置如下
./test
./src
./.s
运行环境
在 s.yaml
中,runtime 用于配置运行环境
方式1
runtime: custom
customRuntimeConfig:
command:
- ./bootstrap
这里的 custom,在阿里云上,是 debian 环境,带了 nodejs
在使用 custom 这个配置时,需要带上 customRuntimeConfig 的配置。这里的配置就是说,使用 bootstrap
文件来启动程序,所以,此时你的项目根目录中,还需要有一个 bootstrap 文件(没有后缀名)
bootstrap 的具体内容是:
#!/usr/bin/env bash
node ./dist/main.js
如果一切顺利的话,此时运行 s deploy
,就可以将服务部署到阿里云函数服务上了。
遇到的问题
1 疑似 nodejs 版本问题
不知道上面的 custom 运行环境中,node 的版本具体是多少,但我这里运行起来,会有如下错误。
Function instance exited unexpectedly(code 1, message:operation not permitted) with start command './bootstrap '.\nLogs:/code/node_modules/@nestjs/common/decorators/http/route-params.decorator.js:53\r\n if (options?.passthrough) {\r\n ^\r\n\r\nSyntaxError: Unexpected token .\r\n at Module._compile (internal/modules/cjs/loader.js:723:23)\r\n at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)\r\n at Module.load (internal/modules/cjs/loader.js:653:32)\r\n at tryModuleLoad (internal/modules/cjs/loader.js:593:12)\r\n at Function.Module._load (internal/modules/cjs/loader.js:585:3)\r\n at Module.require (internal/modules/cjs/loader.js:692:17)\r\n at require (internal/modules/cjs/helpers.js:25:18)\r\n at Object.<anonymous> (/code/node_modules/@nestjs/common/decorators/http/index.js:5:22)\r\n at Module._compile (internal/modules/cjs/loader.js:778:30)\r\n at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)"
chatgpt 是这么说的
这个错误 Function instance exited unexpectedly (code 1, message: operation not permitted) 表示函数实例异常退出,返回了一个退出码为 1 的错误,并且错误消息显示操作不允许。
根据错误日志中的内容,看起来是在加载 @nestjs/common 模块时发生了语法错误 SyntaxError: Unexpected token .。这可能是由于你的 Node.js 版本不支持可选链操作符 ?. 导致的。
可选链操作符是在 ECMAScript 2020 中引入的新特性,它用于简化访问可能为 null 或 undefined 的属性或方法。如果你的 Node.js 版本较旧,不支持可选链操作符,那么就会出现语法错误。
要解决这个问题,你可以尝试以下方法:
更新 Node.js 版本:确保你的 Node.js 版本符合支持可选链操作符的要求。建议使用较新的 LTS(长期支持)版本,以确保你可以使用最新的语言特性。
修改代码:如果你无法更新 Node.js 版本,你可以修改代码,将可选链操作符 ?. 替换为传统的属性访问方式,以避免语法错误。
例如,将 options?.passthrough 替换为 options && options.passthrough。
检查依赖项:如果你的代码依赖于其他模块或库,确保这些依赖项与你使用的 Node.js 版本兼容,并且能够正确加载和执行。
请注意,具体的解决方法可能因你的代码和环境而异。如果问题仍然存在,请提供更多详细信息,以便我可以更好地帮助你解决问题。
如果真是版本问题,还不好处理了。
2 修改 runtime 为 nodejs16 之后的问题
尽管文档中说只支持到 nodejs14,但好像设置成 16 也可以,不过,会有其它的问题。
函数计算运行时的支持情况和终止策略_函数计算-阿里云帮助中心
function:
codeUri: ./
runtime: nodejs16
handler: main.fcHandler
此时,需要配置 handler,文档见 什么是函数Node.js运行时的请求处理程序_函数计算-阿里云帮助中心
于是在 nestjs 项目的 main.ts 中,添加了如下代码
export async function fcHandler() {
await bootstrap();
}
然后得到了一个有趣的报错,说 main.js 没有找到,因为去 code 目录下找了,但是我的 codeUri
已经配置成根目录了啊。感觉这里有只 BUG。我不想把代码放到 code 子目录中验证了,考虑其它方案。
{
"errorMessage": "Module '/code/main.js' is missing.",
"errorType": "FunctionUnhandledError: ImportModuleError",
"stackTrace": [
"ImportModuleError: Module '/code/main.js' is missing."
]
}
未完待续
标签:Serverless,函数,yaml,modules,js,devs,阿里,fc,nestjs From: https://www.cnblogs.com/jasongrass/p/17615357.html