生产环境部署

你已经开发了一个 Payload 应用,完成了全面测试,并且在本地运行良好。现在是时候发布了。太棒了!做得好! 那么接下来该做什么呢?

有多种方式可以将 Payload 部署到生产环境。在评估如何部署 Payload 时,你需要考虑以下主要方面:

  1. 基础配置
  2. 安全性
  3. 数据库
  4. 永久文件存储
  5. Docker

Payload 可以部署在任何能运行 Next.js 的地方——包括 Vercel、Netlify、SST、DigitalOcean、AWS 等等。由于它是开源的,你也可以自行托管。

但重要的是要记住,大多数 Payload 项目还需要数据库、文件存储、邮件服务提供商和 CDN。无论选择哪种部署平台,请确保满足项目的所有需求。

通常,最简单快捷的部署方式是使用 Payload Cloud——它能开箱即用地提供你所需的一切,包括:

  1. MongoDB Atlas 数据库
  2. S3 文件存储
  3. Resend 邮件服务
  4. Cloudflare CDN
  5. 蓝绿部署
  6. 日志
  7. 以及其他更多功能

基础配置

Payload 完全运行在 Next.js 中,因此构建 Payload 时会使用 Next.js 构建流程。如果你使用 create-payload-app 创建项目,执行 build npm 脚本将为生产环境构建 Payload。

安全性

Payload 提供了一系列安全功能,你可以依赖它们来增强应用的安全性。在部署到生产环境时,最好再次检查你是否正确使用了每一项功能。

密钥(Secret Key)

初始化 Payload 时,你需要提供一个 secret 属性。这个属性应该是无法猜测的,并且极难被暴力破解。确保你的生产环境 secret 是一个长且复杂的字符串。

仔细检查并全面测试所有访问控制

由于 完全掌控着谁可以对你的数据执行哪些操作,因此在部署到生产环境之前,应该反复检查以确保你负责任地行使这一权力。

默认情况下,所有访问控制功能都要求用户成功登录 Payload 才能创建、读取、更新或删除数据。

但是,例如如果你允许公开用户注册,则需要确保你的访问控制函数更加严格 - 只允许适当的用户执行适当的操作。

生产环境运行

根据你部署 Payload 的位置,可能需要向部署平台提供一个启动脚本,以便在生产模式下启动 Payload。

请注意,这与运行 next dev 不同。通常,Next.js 应用会配置一个 start 脚本,该脚本会运行 next start

你应该为生产环境的 Payload 实例使用 SSL 证书,这意味着你可以在启用了身份验证的 Collection 配置中启用安全 cookie

防止 API 滥用

Payload 内置了一套强大的防滥用措施,例如在多次登录失败后锁定用户、GraphQL 查询复杂度限制、最大 depth 设置等。点击此处了解更多

数据库

Payload 可以与任何 Postgres 数据库或 MongoDB 兼容的数据库(包括 AWS DocumentDB 或 Azure Cosmos DB)一起使用。确保你的生产环境可以访问 Payload 使用的数据库。

开箱即用的 Payload 模板会将 process.env.DATABASE_URI 环境变量传递给其数据库适配器,因此请确保你的部署平台已分配该环境变量(以及你使用的所有其他变量)。

DocumentDB

当使用 AWS DocumentDB 时,你需要在传递给 mongooseAdapterconnectOptions 中配置认证连接选项。同时需要将 connectOptions.useFacet 设置为 false 来禁用不支持的 $facet 聚合操作。

CosmosDB

当使用 Azure Cosmos DB 时,任何需要排序的字段都需要建立索引。要为管理界面中可能排序的所有字段添加排序索引,可以使用 indexSortableFields 选项。

文件存储

如果你使用 Payload 来管理文件上传,就需要考虑上传文件将永久存储在何处。如果你不使用 Payload 进行文件上传,那么本节内容对你的应用没有任何影响。

持久化 vs 临时文件系统

一些云应用托管服务如 Heroku 使用 临时 文件系统,这意味着上传到服务器的文件只会在服务器重启或关闭前存在。Heroku 和类似提供商会不受控制地安排重启和关闭,导致你的上传文件会意外消失且无法恢复。

相反,持久化文件系统永远不会删除你的文件,可以可靠地永久托管上传内容。

使用临时文件系统的流行云提供商:

  • Heroku
  • DigitalOcean Apps

使用持久化文件系统的流行云提供商:

  • DigitalOcean Droplets
  • Amazon EC2
  • GoDaddy
  • 许多其他传统网络主机

警告:

如果你依赖 Payload 的上传功能,请确保使用具有持久化文件系统的主机,或者与 Amazon S3 等第三方文件托管服务集成。

使用云存储提供商

如果你不使用 Payload 的 upload 功能,可以完全忽略本节内容。

但如果你使用上传功能,同时又想使用临时文件系统存储,你可以选择 Payload 官方提供的云存储插件,或者自己编写适配器,将用户上传的文件保存到更持久的存储解决方案中,如 Amazon S3 或 DigitalOcean Spaces。

Payload 提供了一系列官方云存储适配器供你选择:

按照文档说明配置这些存储提供商。对于本地开发环境,将上传文件存储在本地电脑可能更方便,而在生产环境中,只需启用你选择的云存储插件即可。

Docker

这是一个用于生产环境的 Payload 多阶段 Docker 构建示例。请确保在部署时设置了必要的环境变量,如 PAYLOAD_SECRETPAYLOAD_CONFIG_PATHDATABASE_URI(如果需要)。如果你的构建需要数据库连接但不想实际连接数据库,可以参考这里了解如何避免这种情况。

在你的 Next.js 配置中,设置 output 属性为 standalone

// next.config.js
const nextConfig = {
  output: 'standalone',
}

Dockerfile


# Dockerfile

# 来自 https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile

FROM node:18-alpine AS base


# 仅在需要时安装依赖
FROM base AS deps

# 请参考 https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine 了解为何可能需要安装 libc6-compat
RUN apk add --no-cache libc6-compat
WORKDIR /app


# 根据首选的包管理器安装依赖
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
  if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
  elif [ -f package-lock.json ]; then npm ci; \
  elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
  else echo "未找到锁文件。" && exit 1; \
  fi



# 仅在需要时重新构建源代码
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .


# Next.js 会收集完全匿名的遥测数据用于分析使用情况。

# 了解更多:https://nextjs.org/telemetry

# 如果你想在构建时禁用遥测,请取消以下行的注释。

# ENV NEXT_TELEMETRY_DISABLED 1

RUN \
  if [ -f yarn.lock ]; then yarn run build; \
  elif [ -f package-lock.json ]; then npm run build; \
  elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
  else echo "未找到锁文件。" && exit 1; \
  fi


# 生产环境镜像,复制所有文件并运行 next
FROM base AS runner
WORKDIR /app

ENV NODE_ENV production

# 如果你想在运行时禁用遥测,请取消以下行的注释。

# ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public


# 为预渲染缓存设置正确的权限
RUN mkdir .next
RUN chown nextjs:nodejs .next


# 自动利用输出追踪来减小镜像体积

# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT 3000

# server.js 由 next build 从独立输出创建

# https://nextjs.org/docs/pages/api-reference/next-config-js/output
CMD HOSTNAME="0.0.0.0" node server.js

Docker Compose

以下是可用于开发的 docker-compose.yml 文件示例

version: '3'

services:
  payload:
    image: node:18-alpine
    ports:
      - '3000:3000'
    volumes:
      - .:/home/node/app
      - node_modules:/home/node/app/node_modules
    working_dir: /home/node/app/
    command: sh -c "corepack enable && corepack prepare pnpm@latest --activate && pnpm install && pnpm dev"
    depends_on:
      - mongo
      # - postgres
    env_file:
      - .env

  # 确保你的 DATABASE_URI 使用 'mongo' 作为主机名,例如 mongodb://mongo/my-db-name
  mongo:
    image: mongo:latest
    ports:
      - '27017:27017'
    command:
      - --storageEngine=wiredTiger
    volumes:
      - data:/data/db
    logging:
      driver: none

  # 取消以下注释以使用 postgres
  # postgres:
  #   restart: always
  #   image: postgres:latest
  #   volumes:
  #     - pgdata:/var/lib/postgresql/data
  #   ports:
  #     - "5432:5432"

volumes:
  data:
  # pgdata:
  node_modules: