跳到主要内容

部署到 AWS Lambda

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

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

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

部署到 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 服务器不同,这些连接不会在执行之间维护。为了在 Serverless 环境中获得更好的性能,请实施连接池以重用现有数据库连接,而不是为每个函数调用创建新连接。

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

使用 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 打包您的代码后,根据您的 schema 生成 Prisma Client。没有此步骤,您的应用将无法运行。

最后,您将希望排除与 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 输出将显示使用 copy-webpack-plugin 移动的 schema

serverless package
显示CLI结果

使用 SST 部署

使用环境变量

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

SST 指南 在此处可用 是开始使用 Config 的分步指南。假设您已创建一个名为 DATABASE_URL 的新 secret,并且已将 该 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