部署

部署

部署 AdonisJS 应用与部署标准的 Node.js 应用并无二致。你需要一台运行 Node.js >= 20.6 以及 npm 的服务器来安装生产依赖。

本指南将介绍在生产环境部署和运行 AdonisJS 应用的一般性准则。

创建生产构建

第一步,你必须通过运行 build 命令创建 AdonisJS 应用的生产构建。

另见:TypeScript 构建流程

node ace build

编译后的输出被写入 ./build 目录。如果你使用了 Vite,其输出会被写入 ./build/public 目录。

创建生产构建后,你可以将 ./build 文件夹复制到你的生产服务器。从此以后,build 文件夹将成为你应用的根目录

创建 Docker 镜像

如果你使用 Docker 部署应用,可以使用以下 Dockerfile 创建 Docker 镜像。

FROM node:22.16.0-alpine3.22 AS base
# All deps stage
FROM base AS deps
WORKDIR /app
ADD package.json package-lock.json ./
RUN npm ci
# Production only deps stage
FROM base AS production-deps
WORKDIR /app
ADD package.json package-lock.json ./
RUN npm ci --omit=dev
# Build stage
FROM base AS build
WORKDIR /app
COPY --from=deps /app/node_modules /app/node_modules
ADD . .
RUN node ace build
# Production stage
FROM base
ENV NODE_ENV=production
WORKDIR /app
COPY --from=production-deps /app/node_modules /app/node_modules
COPY --from=build /app/build /app
EXPOSE 8080
CMD ["node", "./bin/server.js"]

欢迎按需修改 Dockerfile 以适应你的需求。

配置反向代理

Node.js 应用通常部署在反向代理服务器(如 Nginx)之后。因此,端口 80443 上的入站流量会先由 Nginx 处理,然后再转发到你的 Node.js 应用。

以下是一个 Nginx 配置文件示例,可作为起点使用。

请确保替换尖括号 <> 中的值。

server {
listen 80;
listen [::]:80;
server_name <APP_DOMAIN.COM>;
location / {
proxy_pass http://localhost:<ADONIS_PORT>;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache_bypass $http_upgrade;
}
}

定义环境变量

如果你将应用部署在裸金属服务器(如 DigitalOcean Droplet 或 EC2 实例)上,可以使用 .env 文件来定义环境变量。确保文件被安全地存储,并且只有授权用户才能访问它。

如果你使用的是 Heroku 或 Cleavr 之类的部署平台,可以使用它们的控制面板来定义环境变量。

假设你已在 /etc/secrets 目录中创建了 .env 文件,必须按如下方式启动你的生产服务器。

ENV_PATH=/etc/secrets node build/bin/server.js

ENV_PATH 环境变量指示 AdonisJS 在指定目录中查找 .env 文件。

启动生产服务器

你可以运行 node server.js 文件来启动生产服务器。不过,建议使用进程管理器,如 pm2

  • PM2 会在后台运行你的应用,而不会阻塞当前终端会话。
  • 如果你的应用在服务请求时崩溃,它会重启应用。
  • 此外,PM2 让你能够极其轻松地在集群模式下运行应用。

以下是一个 pm2 ecosystem 文件示例,可作为起点使用。

ecosystem.config.js
module.exports = {
apps: [
{
name: 'web-app',
script: './server.js',
instances: 'max',
exec_mode: 'cluster',
autorestart: true,
},
],
}
启动服务器
pm2 start ecosystem.config.js

迁移数据库

如果你使用的是 SQL 数据库,必须在生产服务器上运行数据库迁移以创建所需的表。

如果你使用的是 Lucid,可以运行以下命令。

node ace migration:run --force

在生产环境中运行迁移时需要使用 --force 标志。

何时运行迁移

此外,你最好始终在重启服务器之前运行迁移。这样,如果迁移失败,就不要重启服务器。

使用 Cleavr 或 Heroku 这类托管服务时,它们可以自动处理这种情况。否则,你将不得不在 CI/CD 流水线中运行迁移脚本,或通过 SSH 手动运行。

不要在生产环境回滚

在生产环境回滚迁移是一项危险的操作。迁移文件中的 down 方法通常包含破坏性操作,例如删除表移除列等。

建议在生产环境的 config/database.ts 文件中关闭回滚,转而创建一个新的迁移来修复问题,并在生产服务器上运行它。

在生产环境关闭回滚将确保 node ace migration:rollback 命令会报错。

{
pg: {
client: 'pg',
migrations: {
disableRollbacksInProduction: true,
}
}
}

并发迁移

如果你在具有多个实例的服务器上运行迁移,必须确保只有一个实例运行迁移。

对于 MySQL 和 PostgreSQL,Lucid 会获取 advisory lock(咨询锁)以确保不允许并发迁移。不过,最好还是从一开始就避免从多个服务器运行迁移。

文件上传的持久化存储

Amazon EKS、Google Kubernetes、Heroku、DigitalOcean Apps 等环境会在临时文件系统中运行你的应用代码,这意味着每次部署默认都会清除现有的文件系统并创建一个全新的文件系统。

如果你的应用允许用户上传文件,必须使用持久化存储服务(如 Amazon S3、Google Cloud Storage 或 DigitalOcean Spaces),而不是依赖本地文件系统。

写入日志

AdonisJS 默认使用 pino 日志记录器,以 JSON 格式将日志写入控制台。你可以设置外部日志服务从 stdout/stderr 读取日志,或者将它们转发到同一台服务器上的本地文件。

提供静态资源

高效地提供静态资源对应用的性能至关重要。无论你的 AdonisJS 应用速度多快,静态资源的传输对更好的用户体验起着巨大作用。

使用 CDN

最佳方案是使用 CDN(内容分发网络)来传输来自 AdonisJS 应用的静态资源。

使用 Vite 编译的前端资源默认会被指纹化,这意味着文件名是基于内容进行哈希的。这让你能够永久缓存这些资源并从 CDN 提供它们。

根据你使用的 CDN 服务和部署方式,你可能需要在部署流程中增加一个步骤,将静态文件移动到 CDN 服务器。简而言之,它应该这样工作。

  1. 更新 vite.config.jsconfig/vite.ts 配置以使用 CDN URL

  2. 运行 build 命令编译应用和资源。

  3. public/assets 的输出复制到你的 CDN 服务器。例如,这是我们用来将资源发布到 Amazon S3 存储桶的命令

使用 Nginx 提供静态资源

另一种选择是将提供资源的任务卸载给 Nginx。如果你使用 Vite 编译前端资源,由于它们已被指纹化,你必须激进地缓存所有静态文件。

将以下代码块添加到你的 Nginx 配置文件中。请确保替换尖括号 <> 中的值

location ~ \.(jpg|png|css|js|gif|ico|woff|woff2) {
root <PATH_TO_ADONISJS_APP_PUBLIC_DIRECTORY>;
sendfile on;
sendfile_max_chunk 2m;
add_header Cache-Control "public";
expires 365d;
}

使用 AdonisJS 静态文件服务器

你也可以依赖 AdonisJS 内置静态文件服务器public 目录提供静态资源,以保持简单。

无需额外配置。像往常一样部署你的 AdonisJS 应用,对静态资源的请求将自动被提供。

不建议在生产环境使用静态文件服务器。最好使用 CDN 或 Nginx 来提供静态资源。