跳至主要内容

部署到 AWS Lambda

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

虽然部署到 AWS Lambda 不需要部署框架,但本指南涵盖了使用以下框架进行部署:

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

部署到 AWS Lambda 时的通用注意事项

本节介绍您需要对应用程序进行的更改,无论使用哪个框架。完成以下步骤后,请按照您所使用框架的步骤进行操作。

在 Prisma Schema 中定义二进制目标

根据 Node.js 的版本,您的 Prisma 模式应该在 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)可以访问正确的模式引擎。

在 AWS lambda 的情况下,您需要添加以下环境变量

.env
PRISMA_CLI_BINARY_TARGETS=native,rhel-openssl-1.0.x
info

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

连接池

通常,当您使用函数即服务 (FaaS) 环境与数据库交互时,每次函数调用都会导致与数据库的新连接。对于持续运行的 Node.js 服务器来说,这不是问题。因此,最好使用数据库连接池来提高性能。您可以使用 加速 来解决此问题。有关其他解决方案,请参阅 无服务器环境的连接管理指南

使用 AWS SAM 部署

加载环境变量

AWS SAM 不直接支持从 .env 文件加载值。您需要使用 AWS 的一项服务来存储和检索这些参数。 本指南 提供了关于您的选择以及如何存储和检索参数、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)
}

您还需要定义如何使用 esbuild 捆绑这些文件,方法是在 template.yaml 中的 Metadata.BuildProperties 中添加以下几行代码。

template.yaml
Loader:
- .prisma=file
- .so.node=file
AssetNames: '[name]'

这将确保 Prisma ORM 所需的文件包含在 AWS SAM 构建中。

使用 Serverless Framework 部署

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

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

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

npm install -D serverless-dotenv-plugin

然后,在您的serverless.yml文件中,将serverless-dotenv-plugin添加到您的插件列表中。

serverless.yml
plugins:
- serverless-dotenv-plugin

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

serverless package
展示CLI结果

仅部署必需的文件

为了减少您的部署占用空间,您可以更新您的部署过程,只上传您的应用程序需要的文件。下面的 Serverless 配置文件serverless.yml展示了一个package模式,该模式仅包含与 Lambda 运行时相关的 Prisma ORM 引擎文件,并排除其他文件。这意味着当 Serverless Framework 打包您的应用程序以供上传时,它只包含一个引擎文件。这确保了打包的归档文件尽可能小。

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存在,以确保查询根据您的模式进行编码和解码。在大多数情况下,捆绑器默认情况下不会包含此文件,并且会导致您的应用程序无法运行。

info

根据您应用程序的捆绑方式,您可能需要将模式复制到./以外的位置。使用serverless package命令本地打包您的代码,以便您可以查看应将您的模式放在哪里。

有关更多配置,请参阅Serverless Webpack 文档

3. 更新serverless.yml

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

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

这将确保在 webpack 捆绑您的代码后,根据您的模式生成 Prisma 客户端。如果没有此步骤,您的应用程序将无法运行。

最后,您将想要排除与 AWS Lambda 运行时不匹配的 Prisma ORM 查询引擎。通过添加以下脚本更新您的serverless.yml,以确保最终打包的归档文件中只包含必需的查询引擎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 输出将显示使用copy-webpack-plugin移动的模式。

serverless package
展示CLI结果

使用 SST 部署

使用环境变量

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

此处提供的 SST 指南此处提供是使用Config入门的分步指南。假设您已创建一个名为DATABASE_URL的新秘密,并且已将该秘密绑定到您的应用程序,您可以使用以下方法设置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