使用 PM2 运行 Strapi
页面摘要: 构建管理面板,在一个
NODE_ENV设为production的ecosystem.config.js文件中描述你的应用,然后用pm2 start启动它。运行pm2 startup和pm2 save,使应用在重启后自动恢复。
通过 npm run start 启动时,Strapi 在前台运行,并在你关闭终端或进程崩溃时停止。进程管理器则能让它持续运行:在失败时重启应用,在重启后将其拉起,并收集其日志。PM2 是 Node.js 应用的一个常见选择。本指南涵盖安装 PM2、向 PM2 描述一个 Strapi 应用,以及随之而来的运维任务。
安装 PM2
全局安装 PM2,使 pm2 命令对你的 shell 和启动脚本都可用:
Yarn
yarn global add pm2
NPM
npm install -g pm2
确认安装成功:
pm2 --version
构建管理面板
管理面板是一个静态包,必须在服务器于生产环境启动之前构建。使用 NODE_ENV 设为 production 来构建,以便构建使用你的生产配置:
Yarn
NODE_ENV=production yarn build
NPM
NODE_ENV=production npm run build
在构建命令本身上设置 NODE_ENV=production,而不是为整个 shell 会话设置。如果在安装依赖之前导出它,包管理器会跳过 devDependencies,而构建正需要它们。
创建 ecosystem 文件
PM2 可以从命令行启动应用,但配置文件能让设置纳入版本控制,并使每次部署都可重复。在 Strapi 项目的根目录创建该文件:
module.exports = {
apps: [
{
name: 'strapi',
cwd: '/srv/strapi',
script: 'npm',
args: 'start',
env: {
NODE_ENV: 'production',
HOST: '0.0.0.0',
PORT: 1337,
},
},
],
};
这些选项的作用如下:
| 选项 | 作用 |
|---|---|
name | 你传递给其他 PM2 命令的名称,例如 pm2 restart strapi。 |
cwd | 项目的绝对路径。PM2 相对于它解析 script,因此无论你从哪个目录运行 pm2,应用都能正确启动。 |
script 与 args | PM2 运行的命令。使用包管理器而非文件路径,可以保持与手动启动 Strapi 时完全一致的行为。 |
env | 进程的环境变量。NODE_ENV 必须为 production,以便 Strapi 加载生产配置。 |
如果 ecosystem 文件被提交到版本控制,请勿在其中放入诸如 APP_KEYS、ADMIN_JWT_SECRET 或数据库凭据之类的密钥。请将它们保留在服务器启动时 Strapi 会读取的 .env 文件中,或从你的部署工具中注入(参见环境配置)。
使用 PM2 启动 Strapi
从 ecosystem 文件启动应用:
pm2 start ecosystem.config.js
检查它是否启动成功:
pm2 list
Strapi 条目应显示状态为 online。如果状态为 errored,或者重启计数持续攀升,说明进程正在失败并退出。请阅读日志查找原因:
pm2 logs strapi --lines 100
让 Strapi 在重启后持续运行
PM2 自身无法在重启后存活。它需要一个初始化脚本,以及一份关于要启动哪些应用的已保存快照。
-
生成初始化脚本。该命令会打印第二条命令,并已为你的初始化系统和用户预填好:
pm2 startup -
原样运行它打印出的命令。这一步才需要
sudo。 -
保存当前的进程列表,以便 PM2 知道要恢复什么:
pm2 save
每当你添加、移除或重命名一个应用时,都要再次运行 pm2 save。否则,PM2 会恢复到上次保存时的列表。
管理日志
PM2 将每个应用的输出写入 ~/.pm2/logs/ 中的文件。这些文件会无限增长,因此请安装日志轮转模块:
pm2 install pm2-logrotate
设置大小上限以及保留多少个轮转后的文件:
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
更新正在运行的部署
只要你的代码或管理面板配置发生变化,就必须重新构建管理面板包,并且重建必须在进程重启之前完成。请按以下顺序部署:
-
在服务器上获取新代码。
-
安装依赖。
-
使用
NODE_ENV=production构建管理面板。 -
重启应用:
pm2 restart strapi --update-env
--update-env 标志会让 PM2 重新读取环境,而不是复用进程首次启动时的取值。如果没有它,你对 .env 文件或 ecosystem 文件所做的更改将被忽略。
运行多个实例
PM2 可以通过其 cluster 模式运行一个应用的多个副本,共享同一个端口。cluster 模式构建于 Node.js 的 cluster 模块之上,该模块需要一个 JavaScript 入口点。本指南前面使用的 script: 'npm' 值运行的是一个二进制文件,而 PM2 只能在 fork 模式下管理二进制文件。
在项目根目录添加一个直接启动 Strapi 的入口点:
const strapi = require('@strapi/strapi');
strapi.createStrapi(/* {...} */).start();
对于 TypeScript 项目,请向 createStrapi 传入 distDir 选项,以便服务器从编译后的输出启动(参见使用 createStrapi 工厂)。
将 ecosystem 文件指向该文件,并选择 cluster 模式:
module.exports = {
apps: [
{
name: 'strapi',
cwd: '/srv/strapi',
// highlight-start
script: './server.js',
exec_mode: 'cluster',
instances: 'max',
// highlight-end
env: {
NODE_ENV: 'production',
HOST: '0.0.0.0',
PORT: 1337,
},
},
],
};
Strapi 可以运行在此模式下,但实例之间没有任何协调机制,因此某些行为会发生变化。在启用之前请阅读本节。
Strapi 在启动时运行其模式同步,每个进程运行一次,实例之间不共享锁。如果重启后内容类型不变则是安全的,因为同步会检测到模式未变而不做任何操作。
同步在两种情况下不安全:发布会更改内容类型的版本,以及带有待处理迁移的版本。此时同时启动的实例会对同一数据库发出并发模式更改和迁移。
对于此类版本,先启动单个实例并让其完成启动,再启动下一个实例。这是在多个进程针对一个数据库运行时的固有属性,因此将实例分布在不同主机上也无法避免。
运行多个实例还有另外 3 个值得了解的后果:
- CRON 任务在每个 Strapi 进程内调度,因此任务在每个实例上运行一次,而不是整体运行一次。设置为夜间运行的任务会运行与实例数量相同次数。如果项目设置
cron.enabled为true,要么将任务移出 Strapi,要么将它们保留在单个实例上。 - Strapi 在内存中保存的任何内容都只属于一个实例,因为每个实例都是独立进程。Strapi 核心不会在它们之间复制任何状态。
- 默认的本地上传提供方将文件写入实例自身的磁盘。共享同一台机器和项目目录的实例共享该磁盘,但不同机器或不同容器中的实例则不共享。在这种情况下,请使用基于对象存储的媒体库提供方之一。
鉴于这些限制,请从位于反向代理之后的单个实例开始。只有在实测单个实例确实不够时,再按上述方式增加实例。
验证应用是否正在运行
确认应用正在运行且可达:
-
运行
pm2 list,检查 Strapi 条目是否显示为online且重启计数没有攀升。 -
请求健康检查路由,服务器就绪时会返回 HTTP
204 No Content:curl -I http://localhost:1337/_health -
重启服务器。
-
再次运行
pm2 list。应用应处于online状态,而无需你手动启动。
故障排查
以下每个症状都指向一个具体的原因。症状以粗体显示,其后为导致该症状的原因以及需要更改的内容:
-
pm2 list中显示进程为errored。 请阅读pm2 logs strapi --lines 100。常见原因包括:缺少构建产物、数据库不可达,或缺少环境变量。 -
PM2 在循环中重启 Strapi。 应用在启动时立即退出。日志中会指明原因。在修复之前,请使用
pm2 stop strapi停止该循环,而不是任由其重试。 -
Strapi 在重启后没有恢复。 要么从未运行过
pm2 startup打印出的命令,要么在添加应用后没有运行pm2 save。请重新执行这两步。 -
对环境变量的更改没有生效。 PM2 会复用进程首次启动时的环境。请使用
pm2 restart strapi --update-env重启。 -
部署后管理面板显示旧版本。 管理面板包没有被重新构建。请使用
NODE_ENV=production运行构建,然后重启。