跳到主内容

部署到 AWS Lambda

本指南解释了如何在使用 Prisma ORM 将项目部署到 AWS Lambda 时避免常见问题。

虽然部署框架并非部署到 AWS Lambda 的必要条件,但本指南将介绍使用以下方式进行部署:

  • AWS Serverless Application Model (SAM) 是一个来自 AWS 的开源框架,可用于创建无服务器应用程序。AWS SAM 包括 AWS SAM CLI,您可以使用它来构建、测试和部署您的应用程序。
  • Serverless Framework 提供了一个 CLI,可帮助实现工作流自动化和 AWS 资源预置。虽然 Prisma ORM 可以很好地与 Serverless Framework“开箱即用”,但您可以在项目中进行一些改进,以确保顺利部署和获得良好性能。如果您正在使用 serverless-webpackserverless-bundle 库,还需要进行额外的配置。
  • SST 提供了工具,使开发人员可以轻松定义、测试、调试和部署应用程序。Prisma ORM 与 SST 配合良好,但必须进行配置,以确保您的 schema 由 SST 正确打包。

部署到 AWS Lambda 时的常规注意事项

本节介绍了无论使用何种框架,您都需要对应用程序进行的更改。完成这些步骤后,请按照您使用的框架的步骤进行操作。

在 Prisma Schema 中定义二进制目标

根据 Node.js 版本,您的 Prisma schema 的 generator 块应包含 rhel-openssl-1.0.xrhel-openssl-3.0.x

binaryTargets = ["native", "rhel-openssl-1.0.x"]

这是必需的,因为开发和部署中使用的运行时不同。添加 binaryTarget 以使兼容的 Prisma ORM 引擎文件可用。

使用 arm64 架构的 Lambda 函数

使用 arm64 架构(AWS Graviton2 处理器) 的 Lambda 函数必须使用 arm64 预编译引擎文件。

在您的 schema.prisma 文件的 generator 块中,添加以下内容:

schema.prisma
binaryTargets = ["native", "linux-arm64-openssl-1.0.x"]

Prisma CLI 二进制目标

虽然我们不建议在 AWS Lambda 中运行迁移,但某些应用程序可能需要。在这种情况下,您可以使用 PRISMA_CLI_BINARY_TARGETS 环境变量来确保 Prisma CLI 命令(包括 prisma migrate)能够访问正确的 schema 引擎。

对于 AWS lambda,您必须添加以下环境变量:

.env
PRISMA_CLI_BINARY_TARGETS=native,rhel-openssl-1.0.x
提示

prisma migrateprisma 包中的一个命令。通常,此包作为开发依赖项安装。根据您的设置,您可能需要将其作为普通依赖项安装,以便将其包含在上传到 Lambda 并执行的 bundle 或 archive 中。

连接池

在函数即服务 (FaaS) 环境中,每次函数调用通常都会创建一个新的数据库连接。与持续运行的 Node.js 服务器不同,这些连接在执行之间不会保持。为了在无服务器环境中获得更好的性能,请实施连接池,以重用现有的数据库连接,而不是为每次函数调用创建新连接。

您可以使用 Accelerate 进行连接池,或者使用内置连接池的 Prisma Postgres 来解决此问题。对于其他解决方案,请参阅无服务器环境的连接管理指南

使用 AWS SAM 进行部署

加载环境变量

AWS SAM 不直接支持从 .env 文件加载值。您必须使用 AWS 的一项服务来存储和检索这些参数。本指南 很好地概述了您的选择以及如何在 Parameters、SSM、Secrets Manager 等服务中存储和检索值。

加载必需的文件

AWS SAM 使用 esbuild 来打包您的 TypeScript 代码。但是,完整的 esbuild API 未暴露,也不支持 esbuild 插件。这在使用 Prisma ORM 的应用程序中会导致问题,因为某些文件(如 schema.prisma)必须在运行时可用。

为解决此问题,您需要在代码中直接引用所需文件以正确打包。在您的应用程序中,您可以在实例化 Prisma ORM 的位置添加以下代码行。

app.ts
import schema from './prisma/schema.prisma'
import x from './node_modules/.prisma/client/libquery_engine-rhel-openssl-1.0.x.so.node'

if (process.env.NODE_ENV !== 'production') {
console.debug(schema, x)
}

使用 Serverless Framework 进行部署

通过 .env 文件加载环境变量

您的函数将需要 DATABASE_URL 环境变量来访问数据库。serverless-dotenv-plugin 允许您在部署中使用 .env 文件。

首先,确保已安装该插件:

npm install -D serverless-dotenv-plugin

然后,将 serverless-dotenv-plugin 添加到 serverless.yml 中的插件列表:

serverless.yml
plugins:
- serverless-dotenv-plugin

现在,您的 .env 文件中的环境变量将在打包或部署时自动加载。

serverless package
显示CLI结果

仅部署所需文件

为了减小部署大小,您可以更新部署流程,只上传应用程序所需的文件。下面的 Serverless 配置文件 serverless.yml 显示了一种 package 模式,该模式仅包含与 Lambda 运行时相关的 Prisma ORM 引擎文件,并排除其他文件。这意味着当 Serverless Framework 打包您的应用程序进行上传时,它只包含一个引擎文件。这确保了打包后的 archive 尽可能小。

serverless.yml
package:
patterns:
- '!node_modules/.prisma/client/libquery_engine-*'
- 'node_modules/.prisma/client/libquery_engine-rhel-*'
- '!node_modules/prisma/libquery_engine-*'
- '!node_modules/@prisma/engines/**'
- '!node_modules/.cache/prisma/**' # only required for Windows

如果您要部署到具有 ARM64 架构的 Lambda 函数,则应更新 Serverless 配置文件以打包 arm64 引擎文件,如下所示:

serverless.yml
package:
patterns:
- '!node_modules/.prisma/client/libquery_engine-*'
- 'node_modules/.prisma/client/libquery_engine-linux-arm64-*'
- '!node_modules/prisma/libquery_engine-*'
- '!node_modules/@prisma/engines/**'

如果您使用 serverless-webpack,请参阅下面的使用 serverless webpack 进行部署

使用 serverless-webpack 进行部署

如果您使用 serverless-webpack,则需要进行额外配置,以便正确打包您的 schema.prisma。您需要:

  1. 使用 copy-webpack-plugin 复制您的 schema.prisma
  2. 通过 serverless.yml 中的 custom > webpack > packagerOptions > scripts 运行 prisma generate
  3. 仅打包正确的 Prisma ORM 引擎文件可节省 40MB 以上的空间。

1. 安装 webpack 特定依赖项

首先,确保已安装以下 webpack 依赖项:

npm install --save-dev webpack webpack-node-externals copy-webpack-plugin serverless-webpack

2. 更新 webpack.config.js

在您的 webpack.config.js 中,确保将 externals 设置为 nodeExternals(),如下所示:

webpack.config.js
const nodeExternals = require('webpack-node-externals')

module.exports = {
// ... other configuration
externals: [nodeExternals()],
// ... other configuration
}

更新您的 webpack.config.js 文件中的 plugins 属性以包含 copy-webpack-plugin

webpack.config.js
const nodeExternals = require('webpack-node-externals')
const CopyPlugin = require('copy-webpack-plugin')

module.exports = {
// ... other configuration
externals: [nodeExternals()],
plugins: [
new CopyPlugin({
patterns: [
{ from: './node_modules/.prisma/client/schema.prisma', to: './' }, // you may need to change `to` here.
],
}),
],
// ... other configuration
}

此插件允许您将 schema.prisma 文件复制到捆绑的代码中。Prisma ORM 要求 schema.prisma 必须存在,以确保查询根据您的 schema 进行编码和解码。在大多数情况下,打包器默认不会包含此文件,这将导致您的应用程序无法运行。

提示

根据应用程序的打包方式,您可能需要将 schema 复制到除 ./ 之外的其他位置。使用 serverless package 命令在本地打包您的代码,以便您可以检查 schema 应该放在哪里。

有关其他配置,请参阅Serverless Webpack 文档

3. 更新 serverless.yml

在您的 serverless.yml 文件中,确保 custom > webpack 块在 packagerOptions > scripts 下包含 prisma generate,如下所示:

serverless.yml
custom:
webpack:
packagerOptions:
scripts:
- prisma generate

这将确保在 webpack 打包您的代码后,Prisma Client 会根据您的 schema 生成。没有此步骤,您的应用程序将无法运行。

最后,您将需要排除与 AWS Lambda 运行时不匹配的 Prisma ORM 查询引擎。通过添加以下脚本来更新您的 serverless.yml,以确保最终打包的 archive 中仅包含所需的查询引擎 rhel-openssl-1.0.x

serverless.yml
custom:
webpack:
packagerOptions:
scripts:
- prisma generate
-- find . -name "libquery_engine-*" -not -name "libquery_engine-rhel-openssl-*" | xargs rm

如果您要部署到具有 ARM64 架构的 Lambda 函数,则应将 find 命令更新为以下内容:

serverless.yml
custom:
webpack:
packagerOptions:
scripts:
- prisma generate
-- find . -name "libquery_engine-*" -not -name "libquery_engine-arm64-openssl-*" | xargs rm

4. 总结

您现在可以重新打包并重新部署应用程序。为此,运行 serverless deploy。Webpack 输出将显示 schema 正在通过 copy-webpack-plugin 移动:

serverless package
显示CLI结果

使用 SST 进行部署

使用环境变量

虽然 SST 支持 .env 文件,但并不推荐。SST 建议使用 Config 以安全的方式访问这些环境变量。

此处提供的 SST 指南 是开始使用 Config 的分步指南。假设您已创建名为 DATABASE_URL 的新 secret 并已将其绑定到您的应用程序,您可以使用以下代码设置 PrismaClient

prisma.ts
import { PrismaClient } from '@prisma/client'
import { Config } from 'sst/node/config'

const globalForPrisma = global as unknown as { prisma: PrismaClient }

export const prisma =
globalForPrisma.prisma ||
new PrismaClient({
datasourceUrl: Config.DATABASE_URL,
})

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma

export default prisma