簡體   English   中英

通過無服務器框架部署到 AWS Lambda 時,將 Package 文件放入應用程序包的特定文件夾

[英]Package files into specific folder of application bundle when deploying to AWS Lambda via Serverless Framework

語境

我正在使用Serverless Frameworkaws-node-typescript示例。 我的目標是將Prisma集成到其中。

到目前為止,我有:

  1. 使用serverless create在本地創建項目
  2. 在Railway建立PostgreSQL數據庫
  3. 安裝prisma ,運行prisma init ,創建基本User model 並成功運行prisma migrate dev
  4. 通過復制現有的hello function 創建第二個users function
  5. 使用serverless deploy部署 function
  6. 現在在我的 function 中,當我實例化PrismaClient時,我收到內部服務器錯誤並且 function 記錄此錯誤: "ENOENT: no such file or directory, open '/var/task/src/functions/users/schema.prisma'"

我的項目結構如下所示:

.
├── README.md
├── package-lock.json
├── package.json
├── prisma
│   ├── migrations
│   │   ├── 20221006113352_init
│   │   │   └── migration.sql
│   │   └── migration_lock.toml
│   └── schema.prisma
├── serverless.ts
├── src
│   ├── functions
│   │   ├── hello
│   │   │   ├── handler.ts
│   │   │   ├── index.ts
│   │   │   ├── mock.json
│   │   │   └── schema.ts
│   │   ├── index.ts
│   │   └── users
│   │       ├── handler.ts
│   │       └── index.ts
│   └── libs
│       ├── api-gateway.ts
│       ├── handler-resolver.ts
│       └── lambda.ts
├── tsconfig.json
└── tsconfig.paths.json

此外,這是users function 的處理程序:ts

import { formatJSONResponse } from '@libs/api-gateway';
import { middyfy } from '@libs/lambda';
import { PrismaClient } from '@prisma/client'

const users = async (event) => {

  console.log(`Instantiating PrismaClient inside handler ...`)
  const prisma = new PrismaClient()

  return formatJSONResponse({
    message: `Hello, ${event.queryStringParameters.name || 'there'} welcome to the exciting Serverless world!`,
    event,
  });
};

export const main = middyfy(users);

出現問題是因為為了實例化PrismaClientschema.prisma文件需要成為應用程序包的一部分。 具體來說,它需要位於/var/task/src/functions/users/中,如錯誤消息所示。

我已經將serverless.ts文件中的package.patterns選項調整為如下所示:

package: { individually: true, patterns: ["**/*.prisma"] },

問題

這樣,上傳到 AWS Lambda 的包在其根目錄中包含prisma目錄,這是我運行sls package后的.serverless文件夾(我在此處解壓users.zip ,以便您可以看到其內容):

.
├── cloudformation-template-create-stack.json
├── cloudformation-template-update-stack.json
├── hello.zip
├── serverless-state.json
├── users
│   ├── prisma
│   │   └── schema.prisma
│   └── src
│       └── functions
│           └── users
│               ├── handler.js
│               └── handler.js.map
└── users.zip

我還可以確認我的 AWS Lambda 的部署版本具有相同的文件夾結構。

如何使用serverless.ts文件中的patternsusers/prisma/schema.prisma文件移動到users/src/functions/users中?

我找到了一個(非常丑陋的)解決方案。 如果有人能想到更優雅的,我仍然很樂意接受,並很樂意為您提供正確答案的分數。

解決"ENOENT: no such file or directory, open '/var/task/src/functions/users/schema.prisma'"錯誤

為了解決這個問題,我只是采用了一種非常天真的方法,並手動將prisma目錄中的schema.prisma文件復制src/functions/users中。 這是我現在擁有的文件結構:

.
├── README.md
├── package-lock.json
├── package.json
├── prisma
│   ├── migrations
│   │   ├── 20221006113352_init
│   │   │   └── migration.sql
│   │   └── migration_lock.toml
│   └── schema.prisma
├── serverless.ts
├── src
│   ├── functions
│   │   ├── hello
│   │   │   ├── handler.ts
│   │   │   ├── index.ts
│   │   │   ├── mock.json
│   │   │   └── schema.ts
│   │   ├── index.ts
│   │   └── users
│   │       ├── schema.prisma
│   │       ├── handler.ts
│   │       └── index.ts
│   └── libs
│       ├── api-gateway.ts
│       ├── handler-resolver.ts
│       └── lambda.ts
├── tsconfig.json
└── tsconfig.paths.json

這顯然是解決此問題的一種可怕方法,因為我現在在不同位置有兩個 Prisma 模式文件,並且必須確保在更改prisma/schema.prisma中的原始文件后始終更新src/functions/users/schema.prisma中的文件prisma/schema.prisma讓它們保持同步。

復制此文件並重新部署后, schema.prisma文件就位於 AWS Lambda 中的正確位置,錯誤消失, PrismaClient可以實例化。

然后我在處理程序中添加了一個簡單的 Prisma Client 查詢:

const users = async (event) => {
  console.log(`Instantiating PrismaClient inside handler ...`)
  const prisma = new PrismaClient()

  const userCount = await prisma.user.count()
  console.log(`There are ${userCount} users in the database`)
  return formatJSONResponse({
    message: `Hello, ${event.queryStringParameters.name || 'there'} welcome to the exciting Serverless world!`,
    event,
  });
};

export const main = middyfy(users);

...這次遇到了一個關於查詢引擎的新錯誤:

Invalid `prisma.user.count()` invocation:


Query engine library for current platform \"rhel-openssl-1.0.x\" could not be found.
You incorrectly pinned it to rhel-openssl-1.0.x

This probably happens, because you built Prisma Client on a different platform.
(Prisma Client looked in \"/var/task/src/functions/users/libquery_engine-rhel-openssl-1.0.x.so.node\")

Searched Locations:

  /var/task/.prisma/client
  /Users/nikolasburk/prisma/talks/2022/serverless-conf-berlin/aws-node-typescript/node_modules/@prisma/client
  /var/task/src/functions
  /var/task/src/functions/users
  /var/task/prisma
  /tmp/prisma-engines
  /var/task/src/functions/users
  
  
  To solve this problem, add the platform \"rhel-openssl-1.0.x\" to the \"binaryTargets\" attribute in the \"generator\" block in the \"schema.prisma\" file:
generator client {
  provider      = \"prisma-client-js\"
  binaryTargets = [\"native\"]
  }

Then run \"prisma generate\" for your changes to take effect.
Read more about deploying Prisma Client: https://pris.ly/d/client-generator

解決Query engine library for current platform \"rhel-openssl-1.0.x\" could not be found. 錯誤

我對 Prisma 非常熟悉,知道 Prisma Client 依賴於一個查詢引擎二進制文件,該二進制文件必須專門為運行 Prisma Client 的平台構建。 這可以通過我的 Prisma 模式中generator塊上的binaryTargets字段進行配置。 AWS Lamda 的目標是rhel-openssl-1.0.x

所以我相應地調整了schema.prisma文件(在兩個位置):

generator client {
  provider = "prisma-client-js"
  binaryTargets = ["native", "rhel-openssl-1.0.x"]
}

之后,我運行npx prisma generate來更新node_modules中生成的 Prisma Client。

但是,這還沒有解決錯誤,問題仍然是 Prisma Client 找不到查詢引擎二進制文件。

因此,當它丟失時,我采用了與schema.prisma文件相同的方法:

  1. 我手動將它復制到src/functions/users中(這次是從它在node_modules/.prisma/libquery_engine-rhel-openssl-1.0.x.so.node
  2. 我將新路徑添加到我的serverless.ts中的package.patterns屬性:
     package: { individually: true, patterns: ["**/*.prisma", "**/libquery_engine-rhel-openssl-1.0.x.so.node"], },

我重新部署測試function后,又出現了一個錯誤:

Invalid `prisma.user.count()` invocation:


error: Environment variable not found: DATABASE_URL.
-->  schema.prisma:11
| 
10 |   provider = \"postgresql\"
11 |   url      = env(\"DATABASE_URL\")
| 

Validation Error Count: 1

解決Environment variable not found: DATABASE_URL. 錯誤

這一次,它非常簡單,我進入 AWS 控制台https://us-east-1.console.aws.amazon.com/lambda/home?region=us-east-1#/functions/aws-node-typescript-dev-users?tab=configure並通過控制台 UI 添加了一個DATABASE_URL環境變量,指向我在 Railway 上的 Postgres 實例:

在此處輸入圖像描述

我通常會潛伏,但上面的答案讓我想出了一個相當優雅的解決方案,我覺得我應該分享下一個糟糕的 sap 出現並嘗試在 Typescript 中集成無服務器和 prisma(不過,我打賭這個解決方案和過程會起作用在其他構建系統中)。

我正在使用示例aws-nodejs-typescript模板,該模板存在一個錯誤,需要我通過修補本地 node_modules 無服務器 package 應用修復程序。

然后,我必須基本上完成@nburk 的回答才能讓自己啟動並運行,如前所述,這是不雅的。

在我嘗試了解 prisma 的行為、特定平台二進制文件的要求以及如何修復它的過程中,我發現如果我可以手動將二進制文件側加載到編譯后的構建文件夾中,我可以將無服務器捆綁器設置為 zip向上。

我遇到了'serverless-plugin-scripts'插件,它允許我們通過無服務器生命周期掛鈎來做到這一點。

我把它放在我的serverless.ts中: plugins: ['serverless-esbuild', 'serverless-plugin-scripts'],

我將以下內容放入package.json

  "scripts": {
    "test": "echo 'Error: no test specified' && exit 1",
    "postbuild": "yarn fix-scrape-scheduler",
    "fix-scrape-scheduler": "cp ../../node_modules/.prisma/client/schema.prisma .esbuild/.build/src/functions/schedule-scrapes/. && cp ../../node_modules/.prisma/client/libquery_engine-rhel-openssl-1.0.x.so.node .esbuild/.build/src/functions/schedule-scrapes/."
  },

這也在我的serverless.ts中:

    scripts: {
      hooks: {
        'before:package:createDeploymentArtifacts': 'yarn run postbuild'
      }
    }

這會導致'serverless-plugin-scripts'插件調用我post-build yarn 腳本並修復 esbuild 創建的.build文件夾。 我想如果您的構建系統(例如 webpack 或其他)以不同的名稱(例如lib )創建構建目錄,則可以相應地修改此過程。

我將不得不為每個單獨打包的 function 創建一個 yarn 腳本來執行此操作,但這是動態的,排除了在源中保留schema.prisma的多個副本以及從動態生成的.prisma文件夾中復制文件的需要node_modules

請注意,我在這里使用的是紗線工作區,因此您的node_modules文件夾的位置將根據您的回購設置而有所不同。

此外,我確實遇到了這個錯誤Please make sure your database server is running at通過確保正確的安全組被列入白名單,出站為 lambda function,入站為 RDS。 還要確保檢查您的 su.net ACL 和路由表。

一個選項是將webpackcopy-webpack-plugin一起使用並更改應用程序的結構,將所有處理程序放在handlers 文件夾中。

文件夾結構

.
├── handlers/
│   ├── hello.ts
│   └── ...
└── services/
    ├── hello.ts
    └── ...

webpack.config.js

/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path");
// const nodeExternals = require("webpack-node-externals");
const CopyPlugin = require("copy-webpack-plugin");
const slsw = require("serverless-webpack");
const { isLocal } = slsw.lib.webpack;

module.exports = {
  target: "node",
  stats: "normal",
  entry: slsw.lib.entries,
  // externals: [nodeExternals()],
  mode: isLocal ? "development" : "production",
  optimization: { concatenateModules: false },
  resolve: { extensions: [".js", ".ts"] },
  output: {
    libraryTarget: "commonjs",
    filename: "[name].js",
    path: path.resolve(__dirname, ".webpack"),
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: "ts-loader",
        exclude: /node_modules/,
      },
    ],
  },
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "./prisma/schema.prisma",
          to: "handlers/schema.prisma",
        },
        {
          from: "./node_modules/.prisma/client/libquery_engine-rhel-openssl-1.0.x.so.node",
          to: "handlers/libquery_engine-rhel-openssl-1.0.x.so.node",
        },
      ],
    }),
  ],
};

如果您需要在組裝 package 之前運行 npx prisma generate ,您可以使用插件 serverless-scriptable -plugin (放在 webpack 之前):

plugins:
  - serverless-scriptable-plugin
  - serverless-webpack

custom:
  scriptable:
    hooks:
      before:package:createDeploymentArtifacts: npx prisma generate
  webpack:
    includeModules: false

依賴項:

npm install -D webpack serverless-webpack webpack-node-externals copy-webpack-plugin serverless-scriptable-plugin

我們最近遇到了同樣的問題。 但是我們的上下文略有不同: schema.prisma文件的路徑是/var/task/node_modules/.prisma/client/schema.prisma

我們通過使用 Serverless Package 配置解決了這個問題。

serverless.yml

service: 'your-service-name'

plugins:
 - serverless-esbuild

provider:
  # ...

package:
  include:
    - 'node_modules/.prisma/client/schema.prisma' # <-------- this line
    - 'node_modules/.prisma/client/libquery_engine-rhel-*'

這樣只有包含 lambda 函數的src文件夾和包含這兩個 Prisma 文件的node_modules文件夾被打包並上傳到 AWS。

盡管不推薦使用serverless.package.includeserverless.package.exclude以支持serverless.package.patterns ,但這是讓它工作的唯一方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM