AWS
高级
架构

AWS 推荐基础设施方案

OpenNext 不会自动创建底层基础设施。您可以使用偏好的工具(如 SST、AWS CDK、Terraform、Serverless Framework 等)为应用创建基础设施。

以下是推荐的基础设施架构:

架构图

以下是各 AWS 资源的推荐配置:

静态资源文件

创建一个 S3 存储桶,并将 .open-next/assets 文件夹中的内容上传至存储桶根目录。例如,文件 .open-next/assets/favicon.ico 应上传至存储桶根目录的 /favicon.ico。如需将文件上传至存储桶子文件夹,请参阅此章节

.open-next/assets 文件夹中包含两类文件:

带哈希的文件

这些文件名中包含哈希值组件,位于 .open-next/assets/_next 文件夹中,例如 .open-next/assets/_next/static/css/0275f6d90e7ad339.css。当文件内容修改时,文件名中的哈希值会相应变化。因此带哈希的文件应在 CDN 和浏览器级别都进行缓存。上传至 S3 时推荐的缓存控制设置为:

public,max-age=31536000,immutable

无哈希的文件

其他来自应用 public/ 文件夹的文件(如 .open-next/assets/favicon.ico)属于无哈希文件。当内容修改时,这些文件名可能保持不变。无哈希文件应在 CDN 级别缓存,但不在浏览器级别缓存。当无哈希文件内容更新时,应在部署时使 CDN 缓存失效。上传至 S3 时推荐的缓存控制设置为:

public,max-age=0,s-maxage=31536000,must-revalidate

缓存文件

创建一个 S3 存储桶,并将 .open-next/cache 文件夹中的内容上传至存储桶的根目录。如果需要将文件上传至存储桶内的子文件夹,请参考此章节

.open-next/cache 文件夹中包含两种类型的缓存:

  • 路由缓存:包含构建时预渲染的 htmljson 文件,用于初始化重新验证缓存。
  • 请求缓存:包含 fetch 调用的响应数据,其中可能包含敏感信息。请确保这些文件不被公开访问。

重新验证表

创建一个 DynamoDB 表,配置如下:

  • 分区键:tag(字符串类型)
  • 排序键:path(字符串类型)
  • 创建一个名为 revalidate 的索引,配置如下:
    • 分区键:path(字符串类型)
    • 排序键:revalidatedAt(数字类型)

图片优化函数

使用 .open-next/image-optimization-function 文件夹中的代码创建一个 Lambda 函数,处理程序设置为 index.mjs。同时确保函数配置如下:

  • 将架构设置为 arm64
  • 设置 BUCKET_NAME 环境变量,值为存储原始图片的 S3 存储桶名称
  • 如果资源文件上传到 S3 存储桶的子文件夹中,需设置 BUCKET_KEY_PREFIX 环境变量,值为文件夹路径(可选)
  • 授予 s3:GetObject 权限

当使用 Next.js <Image> 组件时,该函数会处理图片优化请求。函数内置的 sharp (opens in a new tab) 库用于转换图片。该库针对 arm64 架构编译,旨在运行于 AWS Lambda Arm/Graviton2 架构。了解 AWS Graviton2 处理器提供的更优性价比 (opens in a new tab)

请注意,图片优化函数会返回带有 Cache-Control 头的响应,因此图片将在 CDN 层级和浏览器层级都被缓存。

Server Lambda 函数

使用 .open-next/server-function 文件夹中的代码创建 Lambda 函数,并将处理器设置为 index.mjs。同时确保函数按以下方式配置:

  • 设置 CACHE_BUCKET_NAME 环境变量,值为存储缓存文件的 S3 存储桶名称
  • 如果缓存文件上传到 S3 存储桶的子文件夹中,请设置 CACHE_BUCKET_KEY_PREFIX 环境变量,值为文件夹路径(可选)
  • 设置 CACHE_BUCKET_REGION 环境变量,值为 S3 存储桶所在区域
  • 设置 REVALIDATION_QUEUE_URL 环境变量,值为重新验证队列的 URL
  • 设置 REVALIDATION_QUEUE_REGION 环境变量,值为重新验证队列所在区域
  • 设置 CACHE_DYNAMO_TABLE 环境变量,值为重新验证表的名称
  • 授予 s3:GetObjects3:PutObjects3:ListObjects 权限
  • 授予 sqs:SendMessage 权限

该函数处理来自 Next.js 应用的所有其他类型请求,包括服务器端渲染(SSR)请求和 API 请求。OpenNext 以 standalone 模式构建 Next.js 应用。standalone 模式会生成包含处理请求的 NextServer 类的 .next 文件夹,以及运行 NextServer 所需的 所有依赖项node_modules 文件夹。结构如下:

  .next/                -> NextServer
  node_modules/         -> 依赖项

server 函数适配器封装了 NextServer 并导出一个支持 Lambda 请求和响应的处理器函数。server-function 打包后的结构如下:

  .next/                -> NextServer
+ .open-next/
  node_modules/         -> 依赖项
+ index.mjs             -> server 函数适配器

Monorepo 项目

在 monorepo 项目中,构建输出的结构会稍有不同。例如,如果应用位于 packages/web 目录下,构建输出将呈现如下结构:

  packages/
    web/
      .next/            -> NextServer 服务器
      node_modules/     -> 来自根目录 node_modules 的依赖项(可选)
  node_modules/         -> 来自包目录 node_modules 的依赖项

这种情况下,服务器函数适配器需要创建在 packages/web 目录下,与 .next/ 同级。这是为了确保适配器能够同时从两个 node_modules 文件夹导入依赖项。我们不建议将 Lambda 配置与项目结构耦合,因此不会直接将 Lambda 处理器设置为 packages/web/index.mjs,而是在 server-function 打包根目录添加一个重新导出适配器的包装器 index.mjs。最终的结构如下所示:

  packages/
    web/
      .next/                -> NextServer 服务器
+     .open-next/
      node_modules/          -> 来自根目录 node_modules 的依赖项(可选)
+     index.mjs              -> 服务器函数适配器
  node_modules/              -> 来自包目录 node_modules 的依赖项
+ index.mjs                  -> 适配器包装器

这样可以确保 Lambda 处理器始终指向根目录的 index.mjs

CloudFront 分发配置

创建一个 CloudFront 分发,并将请求分发到对应的处理程序(行为)。配置了以下行为:

行为请求类型CloudFront 函数源站
/_next/static/*哈希静态文件-S3 存储桶
/favicon.ico
/my-images/*
了解原因
公共资源-S3 存储桶
/_next/image图片优化-图片优化函数
/_next/data/*数据请求设置 x-forwarded-host
了解原因
服务器函数
/api/*API设置 x-forwarded-host
了解原因
服务器函数
/*兜底路由设置 x-forwarded-host
了解原因。另请参阅此说明
服务器函数

边缘运行

服务器函数也可以通过配置为 Origin Request 上的 Lambda@Edge 在边缘位置运行。该服务器函数可以同时处理区域性请求事件(API 负载版本 2.0)和边缘请求事件(CloudFront Origin Request 负载)。根据 Lambda 事件对象的格式,函数会相应地处理请求。

配置 CloudFront 分配的方案:

行为请求类型CloudFront 函数Lambda@Edge源站
/_next/static/*哈希静态文件--S3 存储桶
/favicon.ico
/my-images/*
了解原因
公共资源--S3 存储桶
/_next/image图片优化--图片优化函数
/_next/data/*数据请求设置 x-forwarded-host
了解原因
服务器函数-
/api/*API设置 x-forwarded-host
了解原因
服务器函数-
/*兜底路由设置 x-forwarded-host
了解原因
服务器函数-

重新验证函数

使用 .open-next/revalidation-function 文件夹中的代码创建一个 Lambda 函数,处理程序设置为 index.mjs

同时创建一个 SQS FIFO 队列,并将其设置为该函数的事件源。

此函数会轮询队列中的重新验证消息。当收到消息时,函数会向指定路由发送 HEAD 请求以触发其重新验证。

预热函数

使用 .open-next/warmer-function 文件夹中的代码创建一个 Lambda 函数,处理程序设置为 index.mjs。确保函数配置如下:

  • 设置 FUNCTION_NAME 环境变量,其值为服务器 Lambda 函数的名称
  • 设置 CONCURRENCY 环境变量,其值为需要预热的服务器函数数量
  • 授予 lambda:InvokeFunction 权限,允许预热函数调用服务器函数

同时创建一个 EventBridge 定时规则,每 5 分钟调用一次预热函数。

了解更多关于预热工作原理

DynamoDB 提供者函数

此函数用于填充重新验证表。它是 CDK 中的一个自定义资源处理程序,详见此处 (opens in a new tab)。确保函数配置如下:

  • 设置 CACHE_DYNAMO_TABLE 环境变量,其值为存储重新验证表的 DynamoDB 表名称
  • 授予 dynamodb:PutItem 权限,允许函数写入 DynamoDB 表