[英]Error deploying Python package to AWS lambda using Serverless framework
[英]Package files into specific folder of application bundle when deploying to AWS Lambda via Serverless Framework
我正在使用Serverless Framework的aws-node-typescript
示例。 我的目標是將Prisma集成到其中。
到目前為止,我有:
serverless create
在本地創建項目prisma
,運行prisma init
,創建基本User
model 並成功運行prisma migrate dev
hello
function 創建第二個users
functionserverless deploy
部署 functionPrismaClient
時,我收到內部服務器錯誤並且 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);
出現問題是因為為了實例化PrismaClient
, schema.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
文件中的patterns
將users/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
文件相同的方法:
src/functions/users
中(這次是從它在node_modules/.prisma/libquery_engine-rhel-openssl-1.0.x.so.node
)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 和路由表。
一個選項是將webpack與copy-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.include
和serverless.package.exclude
以支持serverless.package.patterns
,但這是讓它工作的唯一方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.