首页 > 其他分享 >使用阿里云函数服务部署 nestjs

使用阿里云函数服务部署 nestjs

时间:2023-08-08 21:01:01浏览次数:40  
标签:Serverless 函数 yaml modules js devs 阿里 fc nestjs

一路踩坑

对于一个现有的 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 的函数服务,所以,先要配置阿里云的访问权限。

Config 命令 - Serverless Devs

第一步 创建账号,配置 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 用于配置运行环境

function字段 - Serverless Devs

方式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

相关文章

  • 怎么证明二元函数的极限是多少?& 怎么证明二元函数的极限不存在?
    怎么证明二元函数的极限是多少:https://zhaokaifeng.com/16589/怎么证明二元函数的极限不存在:https://zhaokaifeng.com/16600/......
  • 前端原型和原型链构造函数的使用
     目录前言导语代码部分运行结果前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从头再来歌谣的意志是永恒的放弃很容易但是坚持一定很酷导语前端原型和原......
  • 前端原型和原型链构造函数的使用
     目录前言导语原型的构造器指向构造函数 原型上添加方法注意的地方构造器指向构造函数本身总结前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从头再来歌......
  • 无涯教程-Perl - getgrnam函数
    描述此功能通过组名查找组文件条目。在列表context中返回以下内容-($name,$passwd,$gid,$members)$members标量包含作为组成员的登录名的空格分隔列表。在标量context中返回组名。有关检索整个组文件的更有效方法,请参阅getgrent。在Windows下,请考虑使用Win32API::Net模块。......
  • c++ std::hash<std::string> 字符串哈希函数
    msvc采用了FNV-1a的哈希算法//众所周知std::string就是一个basic_string<char>template<class_Elem,class_Traits,class_Alloc>structhash<basic_string<_Elem,_Traits,_Alloc>>{_CXX17_DEPRECATE_ADAPTOR_TYPEDEFStypedefbasic_string<_......
  • [数论第二节]欧拉函数/快速幂/扩展欧几里得算法
    欧拉函数欧拉函数\(\varphi(N)\):1-N中与N互质的数的个数若\(N=p_1^{a_1}·p_2^{a_2}·p_3^{a_3}····p_n^{a_n}\)其中p为N的所有质因子则\(\varphi(N)=N(1-\frac{1}{p_1})(1-\frac{1}{p_2})···(1-\frac{1}{p_n})\)证明:互质:两数的公共因子只有1去掉......
  • Python | isinstance函数
    isinstance函数isinstance的意思是“判断类型”;isinstance()是一个内置函数,用于判断一个对象是否是一个已知的类型,类似type()。isinstance()与type()区别type()不会认为子类是一种父类类型,不考虑继承关系。isinstance()会认为子类是一种父类类型,考虑继承关系。如果要判......
  • Linux异步通知---fasync_helper()、kill_fasync()函数介绍与使用
    转载:Linux异步通知---fasync_helper()、kill_fasync()函数介绍与使用_面朝大海0902的博客-CSDN博客一、fasync_helper()与kill_fasync()函数应用程序通过fcntl置FASYNC标志位,触发对应驱动文件的fasync()函数执行(上节有解释原因Linux异步通知—signal()、fcntl()函数介绍与使用),该......
  • 无涯教程-Perl - format函数
    如前所述,Perl代表实用提取和报告语言,我们现在将讨论使用Perl编写报告。Perl使用称为"格式"的书写模板来输出报告。要使用Perl的格式功能,您必须-定义格式PassthedatathatwillbedisplayedontheformatInvoketheFormat定义格式以下是定义Perl格式的语法format......
  • Django博客开发教程:URL与视图函数
    在讲URL与视图函数之前我们先给大家简单介绍一下用户访问网站的流程。我们访问一个网站的时候,一般先打开浏览器,然后在浏览器的地址栏里输入一个网址,也就是URL,然后回车,我们就可以在浏览器里看到这个网址返回的内容。这是我们能看得见的过程,还有一些我们看不见的过程,那就是:当我们在......