如何在Ubuntu上安装Node.js和PM2:完整生产环境指南
Node.js 是一个基于 Chrome V8 引擎构建的异步、事件驱动 JavaScript 运行时,专为在服务器端以高吞吐量执行 JavaScript 代码而设计。PM2 是一款面向生产环境的 Node.js 应用程序进程管理器,通过单一 CLI 界面提供守护进程化、自动崩溃恢复、日志聚合、集群模式负载均衡以及启动脚本生成等功能。
本指南涵盖在生产环境中于 Ubuntu 20.04、22.04 或 24.04 LTS 上可靠运行 Node.js 应用程序所需的每种安装方法、配置选项和操作模式。
前提条件
在继续之前,请确认以下内容:
- 操作系统: Ubuntu 20.04、22.04 或 24.04 LTS(服务器或桌面版)
- 用户权限: `sudo` 或 root 访问权限
- 网络访问: 出站 HTTPS 以下载软件包和脚本
- 已安装 curl: 如尚未安装,请运行 `sudo apt install curl -y`
如果您在云服务器上运行,VPS 托管环境是 Node.js 工作负载最常见的部署目标,本指南中的所有内容均可直接适用。
第一步:更新系统软件包
在安装新软件之前,请务必同步软件包索引并应用待处理的升级。过时的软件包元数据是依赖冲突的常见来源。
“`bash
sudo apt update
sudo apt upgrade -y
“`
升级完成后,如果内核已更新,请重启系统:
“`bash
sudo reboot
“`
第二步:安装 Node.js — 选择正确的方法
在 Ubuntu 上安装 Node.js 有三种主要方法。每种方法在版本控制、隔离性和系统范围可用性方面各有不同的权衡。
方法比较
| 功能 | NodeSource (apt) | NVM | 系统 apt (universe) |
|---|
| — | — | — | — |
|---|
| 版本控制 | 单一固定主版本 | 每用户多版本 | 通常为过时的 LTS |
|---|
| 系统范围安装 | 是 | 否(默认按用户) | 是 |
|---|
| 版本切换 | 需要重新运行设置脚本 | `nvm use <version>` | 不支持 |
|---|
| 最适合 | CI/CD、单版本服务器 | 开发、多项目 | 仅用于快速测试 |
|---|
| npm 全局包需要 sudo | 是 | 否 | 是 |
|---|
| 生产适用性 | 高 | 中 | 低 |
|---|
方法一:通过 NodeSource 安装 Node.js(推荐用于服务器)
NodeSource 为每个活跃的 Node.js 发布线维护最新的 Debian/Ubuntu 软件仓库。这是生产服务器的首选方法,适用于需要系统范围内单一稳定版本的场景。
为当前 LTS 版本添加 NodeSource 软件仓库:
“`bash
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash –
“`
该脚本执行以下几项操作:检测您的 Ubuntu 版本、添加相应的 NodeSource apt 软件仓库、导入 GPG 签名密钥,并运行 `apt-get update`。`-E` 标志在提升至 sudo 时保留您的环境变量,如果您配置了代理设置,这一点尤为重要。
安装 Node.js 和 npm:
“`bash
sudo apt install nodejs -y
“`
NodeSource 软件包将 `node` 和 `npm` 捆绑在单个 `nodejs` 软件包中。与 Ubuntu universe 软件包不同,它不会将两者分开。
验证安装:
“`bash
node -v
npm -v
“`
预期输出示例:
“`
v20.14.0
10.7.0
“`
固定到特定主版本:如果您需要 Node.js 18 而非当前 LTS,请在 curl 命令中将 `setup_lts.x` 替换为 `setup_18.x`。可用的版本流包括 `setup_18.x`、`setup_20.x` 和 `setup_22.x`。
方法二:通过 NVM 安装 Node.js(推荐用于开发和多版本环境)
NVM(Node 版本管理器)将 Node.js 安装到您的主目录中,无需 root 权限即可安装 Node.js 本身或全局 npm 软件包。这消除了在系统安装的 Node.js 上运行 `npm install -g` 时常见的权限问题。
安装 NVM:
“`bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
“`
在运行此命令之前,请检查官方 NVM 仓库以获取最新的发布标签——URL 中的版本号会随每次发布而变化。
重新加载 shell 环境:
“`bash
source ~/.bashrc
“`
如果您使用 Zsh,请改为 source `~/.zshrc`。NVM 安装程序会将其初始化块追加到它检测到的 shell 配置文件中。
安装最新的 LTS 版本 Node.js:
“`bash
nvm install –lts
“`
在 LTS 旁边安装特定版本:
“`bash
nvm install 18
nvm install 20
“`
在已安装版本之间切换:
“`bash
nvm use 20
nvm alias default 20
“`
`alias default` 命令设置在新 shell 会话中激活的版本,这对于不加载交互式 shell 配置文件的脚本和 cron 任务至关重要。
验证:
“`bash
node -v
npm -v
“`
NVM 在生产环境中的关键陷阱:由于 NVM 是用户范围的,PM2 的启动脚本和 systemd 单元将无法访问 NVM 管理的 Node.js 二进制文件,除非您明确配置路径。请参阅第五步了解如何正确处理此问题。
第三步:安装 PM2
PM2 作为 npm 软件包分发,应全局安装以使其 CLI 在系统范围内可用。
安装 PM2:
“`bash
sudo npm install -g pm2
“`
如果您通过 NVM 安装了 Node.js,请省略 `sudo`:
“`bash
npm install -g pm2
“`
验证安装:
“`bash
pm2 -v
“`
安装特定 PM2 版本(在固定基础设施时很有用):
“`bash
sudo npm install -g pm2@5.3.1
“`
PM2 5.x 版本是当前稳定版本线。与 v4 相比,它对 Web 仪表板(pm2 plus)、日志轮转和模块系统进行了重大改进。
第四步:使用 PM2 管理 Node.js 应用程序
启动应用程序
导航到您的应用程序目录并启动它:
“`bash
cd /var/www/my-app
pm2 start app.js –name "my-app"
“`
始终指定 `–name` 标志。如果没有它,PM2 会使用文件名作为进程名称,当您有多个服务时会产生歧义。
使用附加选项启动:
“`bash
pm2 start app.js –name "my-app" –watch –max-memory-restart 300M
“`
- `–watch`:当源文件发生变化时重启进程(在预发布环境中有用,不建议在生产环境中使用)
- `–max-memory-restart 300M`:如果进程超过 300 MB RSS 内存,则自动重启——这是防止内存泄漏的关键保障措施
查看运行中的进程
“`bash
pm2 list
“`
这将输出一个表格,显示进程 ID、名称、模式(fork/cluster)、PID、状态、CPU 使用率、内存消耗和重启次数。
实时仪表板:
“`bash
pm2 monit
“`
控制进程
“`bash
pm2 restart my-app # Graceful restart
pm2 reload my-app # Zero-downtime reload (cluster mode only)
pm2 stop my-app # Stop without removing from process list
pm2 delete my-app # Stop and remove from process list
“`
`restart` 与 `reload` 的区别:`restart` 会终止进程并启动新进程,导致短暂停机。`reload`(仅在集群模式下可用)逐个循环工作进程,在整个过程中保持可用性。在生产集群部署中始终使用 `reload`。
日志管理
“`bash
pm2 logs # Stream logs from all processes
pm2 logs my-app # Stream logs for a specific process
pm2 logs my-app –lines 200 # Show last 200 lines
pm2 flush # Clear all log files
“`
PM2 默认将日志存储在 `~/.pm2/logs/`。对于生产服务器,请安装日志轮转模块以防止磁盘耗尽:
“`bash
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 10
pm2 set pm2-logrotate:compress true
“`
这会在日志达到 50 MB 时进行轮转,保留 10 个压缩归档,并防止日志无限增长——这是长期运行服务器上的常见问题,通常在磁盘空间耗尽之前都被忽视。
第五步:配置 PM2 在系统启动时运行
必须将 PM2 配置为在服务器重启后继续运行。这通过由 PM2 自身生成的 systemd 服务单元来处理。
生成启动命令:
“`bash
pm2 startup
“`
PM2 将输出一个针对您的 init 系统和当前用户定制的命令,如下所示:
“`
[PM2] Init System found: systemd
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu –hp /home/ubuntu
“`
复制并执行该确切命令。请勿修改它——PM2 二进制文件的路径和主目录必须与您系统的配置匹配。
NVM 特定的启动配置:如果您通过 NVM 安装了 Node.js,生成命令中的路径将指向您的 NVM 管理的二进制文件。这是正确的,但您必须在运行 `pm2 startup` 之前确保将 NVM 版本设置为默认版本:
“`bash
nvm alias default 20
pm2 startup
“`
保存当前进程列表:
“`bash
pm2 save
“`
这会将进程列表写入 `~/.pm2/dump.pm2`。重启时,systemd 单元读取此文件并恢复所有已保存的进程。如果您之后添加或删除应用程序,请再次运行 `pm2 save` 以更新快照。
验证 systemd 单元是否处于活动状态:
“`bash
systemctl status pm2-ubuntu
“`
将 `ubuntu` 替换为您的实际用户名。
第六步:使用 PM2 生态系统文件进行生产部署
对于超出单脚本应用程序的任何情况,请使用 PM2 的生态系统配置文件。它提供可重现的、版本控制的进程定义,并消除了记忆冗长 CLI 标志的需要。
生成生态系统文件
“`bash
pm2 ecosystem
“`
这会在当前目录中创建 `ecosystem.config.js`。
生产就绪的生态系统配置
“`javascript
module.exports = {
apps: [
{
name: 'api-server',
script: './src/server.js',
instances: 'max',
exec_mode: 'cluster',
watch: false,
max_memory_restart: '500M',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
error_file: '/var/log/pm2/api-server-error.log',
out_file: '/var/log/pm2/api-server-out.log',
merge_logs: true,
env: {
NODE_ENV: 'development',
PORT: 3000
},
env_production: {
NODE_ENV: 'production',
PORT: 8080
}
},
{
name: 'worker',
script: './src/worker.js',
instances: 2,
exec_mode: 'fork',
cron_restart: '0 2 * * *',
env_production: {
NODE_ENV: 'production'
}
}
]
};
“`
关键配置决策说明:
- `instances: 'max'`:PM2 自动为每个逻辑 CPU 核心生成一个工作进程。在 4 核服务器上,这将创建 4 个 Node.js 进程,充分利用硬件资源。
- `exec_mode: 'cluster'`:使用 Node.js 内置的集群模块。所有实例通过操作系统的套接字负载均衡共享同一端口。
- `exec_mode: 'fork'`:将进程作为独立子进程运行。对于非 HTTP 服务器的应用程序(队列工作进程、定时任务、具有粘性会话的 WebSocket 服务器)是必需的。
- `merge_logs: true`:将所有集群实例的标准输出合并到单个日志文件中,使日志分析更加简便。
- `cron_restart`:使用 cron 语法安排自动重启。适用于积累状态的工作进程或应用夜间配置更改。
使用生态系统文件启动
“`bash
pm2 start ecosystem.config.js –env production
pm2 save
“`
零停机部署工作流
部署应用程序新版本时:
“`bash
git pull origin main
npm install –production
pm2 reload ecosystem.config.js –env production
“`
`pm2 reload` 逐个向每个工作进程发送 `SIGINT`,等待新工作进程就绪,然后终止旧进程。您的应用程序必须优雅地处理 `SIGINT`,并使用 `process.send('ready')` 发出就绪信号,这样才能正常工作。
应用程序中的优雅关闭处理程序:
“`javascript
process.on('SIGINT', () => {
server.close(() => {
console.log('HTTP server closed');
process.exit(0);
});
});
“`
第七步:PM2 监控与可观测性
内置监控
“`bash
pm2 monit
“`
在终端中显示每个进程的实时 CPU 和内存图表。
进程信息
“`bash
pm2 show my-app
“`
输出详细元数据:运行时间、重启次数、版本信息、解释器路径、环境变量和日志文件位置。
PM2 Web 仪表板(PM2 Plus)
PM2 在 pm2.io 提供托管监控仪表板。连接您的服务器:
“`bash
pm2 link <secret_key> <public_key>
“`
这提供历史指标、告警和远程进程管理——在管理多台服务器或需要在没有 SSH 访问权限的情况下获得可见性时尤为有价值。
第八步:更新 Node.js 和 PM2
更新 PM2
“`bash
sudo npm install -g pm2@latest
pm2 update
“`
升级 PM2 二进制文件后,`pm2 update` 是必不可少的——它在不中断运行进程的情况下更新内存中的 PM2 守护进程。
通过 NodeSource 更新 Node.js
为新主版本重新运行设置脚本:
“`bash
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash –
sudo apt install nodejs -y
“`
更新 Node.js 后,重启 PM2 以确保它使用新的二进制文件:
“`bash
pm2 restart all
“`
通过 NVM 更新 Node.js
“`bash
nvm install 22
nvm alias default 22
nvm use 22
pm2 restart all
“`
如果您更改了默认 NVM 版本,请重新生成 PM2 启动脚本以更新 systemd 单元中的二进制文件路径:
“`bash
pm2 unstartup
pm2 startup
pm2 save
“`
安全加固注意事项
在生产环境中运行 Node.js 应用程序需要关注进程管理之外的事项:
- 以非 root 用户运行:切勿以 root 身份运行 PM2 或 Node.js。创建专用系统用户(`adduser –system –group nodeapp`)并在该账户下运行 PM2。
- 环境变量管理:不要在 `ecosystem.config.js` 中硬编码密钥。使用通过 `dotenv` 加载的 `.env` 文件,或通过部署管道注入密钥。生态系统文件通常会提交到版本控制系统。
- 反向代理:在 Node.js 应用程序前面放置 Nginx 或 Caddy。这处理 TLS 终止、静态文件服务、速率限制和请求缓冲。配合SSL 证书解决方案以强制执行 HTTPS。
- 防火墙规则:阻止从公共互联网直接访问您的 Node.js 端口(例如 3000、8080)。只有反向代理应与 Node.js 通信。
- 资源限制:在 PM2 中设置 `max_memory_restart`,并配置系统级 `ulimit` 值,以防止单个失控进程破坏服务器稳定性。
对于资源隔离至关重要的高流量生产部署,独立服务器环境提供完整的硬件控制,并消除了共享基础设施固有的”嘈杂邻居”问题。
为 Node.js 选择合适的托管环境
| 工作负载 | 推荐环境 | 理由 |
|---|
| — | — | — |
|---|
| 个人项目、预发布环境 | [VPS 托管](https://alexhost.com/zh/vps/) | 性价比高、完整 root 访问权限、可扩展 |
|---|
| 高流量 API | [独立服务器](https://alexhost.com/zh/dedicated-servers/) | 性能可预测,无资源争用 |
|---|
| ML 推理 + Node.js | [GPU 托管](https://alexhost.com/zh/gpu-hosting/) | 将计算密集型任务卸载到 GPU 工作进程 |
|---|
| 托管控制面板 | [带 cPanel 的 VPS](https://alexhost.com/zh/vps/control-panels/cpanel-vps/) | 基于 GUI 的进程和文件管理 |
|---|
技术决策检查清单
在将 Node.js 和 PM2 部署到生产环境之前,请使用此检查清单:
- [ ] Node.js 通过 NodeSource(服务器)或 NVM(开发)安装——而非 Ubuntu universe 软件包
- [ ] PM2 以正确权限全局安装,适配您的安装方法
- [ ] 应用程序以 `–name` 标志启动并定义了 `–max-memory-restart` 阈值
- [ ] HTTP 服务器启用集群模式;后台工作进程使用 fork 模式
- [ ] 已执行 `pm2 startup` 并使用 sudo 运行生成的命令
- [ ] 所有进程配置完成后运行 `pm2 save`
- [ ] 已安装并配置日志轮转模块
- [ ] Nginx 或 Caddy 已配置为带 TLS 的反向代理
- [ ] 应用程序优雅处理 `SIGINT` 以实现零停机重载
- [ ] 密钥在 `ecosystem.config.js` 之外管理
- [ ] 已通过 `systemctl status pm2-<username>` 验证 PM2 systemd 单元
- [ ] 防火墙阻止从公共互联网直接访问 Node.js 端口
常见问题
PM2 fork 模式和集群模式有什么区别?
Fork 模式将应用程序作为单个子进程生成——一个实例,利用一个 CPU 核心。集群模式使用 Node.js 的原生集群模块生成多个工作进程,这些进程共享同一 TCP 端口,实现真正的多核利用和零停机重载。对 HTTP/HTTPS 服务器使用集群模式,对工作进程、cron 任务或维护与多进程共享不兼容的内部状态的应用程序使用 fork 模式。
为什么即使运行了 `pm2 startup`,PM2 在服务器重启后也不会重启?
最常见的原因是在配置进程后没有运行 `pm2 save`,导致转储文件为空或缺失。第二个最常见的原因是 NVM 路径不匹配:如果在生成启动脚本后更改了 NVM 默认版本,systemd 单元会指向不存在的二进制文件。解决方法是运行 `pm2 unstartup`,设置正确的 NVM 默认版本,然后重新运行 `pm2 startup` 和 `pm2 save`。
PM2 可以管理非 Node.js 进程吗?
可以。PM2 可以通过指定解释器来管理任何可执行文件。例如:`pm2 start script.py –interpreter python3`。这使 PM2 可作为混合语言微服务架构的通用进程监督器。
如何在单台服务器后面的不同端口上运行多个 Node.js 应用程序?
在 `ecosystem.config.js` 中将每个应用程序定义为单独的条目,并使用不同的 `PORT` 环境变量。将 Nginx 配置为反向代理,使用单独的 `server` 块或 `location` 指令路由到每个端口。所有应用程序共享同一个 PM2 守护进程,并通过单个 `pm2 list` 视图进行管理。
对于生产 VPS,我应该使用 NVM 还是 NodeSource?
NodeSource 通常更适合生产服务器。它将 Node.js 作为系统软件包安装,使其对所有用户和系统服务可用,无需 shell 初始化依赖。NVM 的按用户、按 shell 激活模型在 systemd 单元、cron 任务以及在交互式 shell 会话之外运行的 CI/CD 管道中会引入微妙的故障模式。将 NVM 保留用于本地开发机器,在那里同时管理多个 Node.js 版本是真正的需求。
