<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>生产部署 on heyaohua's Blog</title><link>https://blog.heyaohua.com/tags/%E7%94%9F%E4%BA%A7%E9%83%A8%E7%BD%B2/</link><description>Recent content in 生产部署 on heyaohua's Blog</description><image><title>heyaohua's Blog</title><url>https://blog.heyaohua.com/og-image.png</url><link>https://blog.heyaohua.com/og-image.png</link></image><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sat, 22 Aug 2026 23:20:00 +0800</lastBuildDate><atom:link href="https://blog.heyaohua.com/tags/%E7%94%9F%E4%BA%A7%E9%83%A8%E7%BD%B2/index.xml" rel="self" type="application/rss+xml"/><item><title>PM2 使用手册：从安装到生产级进程管理</title><link>https://blog.heyaohua.com/posts/2026/08/pm2-process-manager-guide/</link><pubDate>Sat, 22 Aug 2026 23:20:00 +0800</pubDate><guid>https://blog.heyaohua.com/posts/2026/08/pm2-process-manager-guide/</guid><description>一份完整的 PM2 使用手册，涵盖安装、进程管理、集群模式、日志、监控、开机自启、ecosystem 配置与生产环境最佳实践。</description><content:encoded><![CDATA[<p>PM2 是 Node.js 生态中最常用的生产级进程管理器，但它的能力不止于&quot;让 Node 脚本在后台跑起来&quot;。本文从安装讲起，覆盖日常管理、集群、日志、监控、自启与生产部署的完整流程。</p>
<hr>
<h2 id="目录">目录</h2>
<ul>
<li>安装与基础概念</li>
<li>启动与进程管理</li>
<li>ecosystem 配置文件</li>
<li>集群模式（Cluster Mode）</li>
<li>日志管理</li>
<li>监控与指标</li>
<li>开机自启</li>
<li>零停机重载与发布</li>
<li>内存与异常处理策略</li>
<li>常见坑与排查</li>
<li>速查表</li>
</ul>
<hr>
<h2 id="安装与基础概念">安装与基础概念</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6272a4"># 全局安装</span>
</span></span><span style="display:flex;"><span>npm install -g pm2
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># 或 pnpm</span>
</span></span><span style="display:flex;"><span>pnpm add -g pm2
</span></span></code></pre></div><p>PM2 的核心概念只有三个：</p>
<ul>
<li><strong>process</strong>：被 PM2 托管的一个应用实例</li>
<li><strong>daemon</strong>：PM2 自身驻留后台的管理进程（<code>~/.pm2/</code> 下保存状态）</li>
<li><strong>ecosystem file</strong>：声明式配置文件（<code>ecosystem.config.js</code>），推荐的生产方式</li>
</ul>
<p>验证安装：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pm2 -v
</span></span><span style="display:flex;"><span>pm2 ping        <span style="color:#6272a4"># 检查 daemon 是否存活</span>
</span></span></code></pre></div><hr>
<h2 id="启动与进程管理">启动与进程管理</h2>
<h3 id="启动一个应用">启动一个应用</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6272a4"># 最简单的启动</span>
</span></span><span style="display:flex;"><span>pm2 start app.js
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># 指定进程名（强烈建议，否则默认用文件名）</span>
</span></span><span style="display:flex;"><span>pm2 start app.js --name api-server
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># 传递 Node 参数与应用参数</span>
</span></span><span style="display:flex;"><span>pm2 start app.js --name api --node-args<span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#34;--max-old-space-size=2048&#34;</span> -- --port <span style="color:#bd93f9">3000</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># 不限于 Node：任何可执行文件都可以托管</span>
</span></span><span style="display:flex;"><span>pm2 start python --name spider -- main.py
</span></span><span style="display:flex;"><span>pm2 start ./worker.sh --name job
</span></span></code></pre></div><h3 id="常用管理命令">常用管理命令</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pm2 list                    <span style="color:#6272a4"># 进程列表（别名 pm2 ls / pm2 status）</span>
</span></span><span style="display:flex;"><span>pm2 describe api-server     <span style="color:#6272a4"># 单个进程详情：路径、日志位置、重启次数、内存等</span>
</span></span><span style="display:flex;"><span>pm2 restart api-server      <span style="color:#6272a4"># 重启</span>
</span></span><span style="display:flex;"><span>pm2 reload api-server       <span style="color:#6272a4"># 优雅重载（集群模式下零停机）</span>
</span></span><span style="display:flex;"><span>pm2 stop api-server         <span style="color:#6272a4"># 停止（保留在列表中）</span>
</span></span><span style="display:flex;"><span>pm2 delete api-server       <span style="color:#6272a4"># 从 PM2 列表移除</span>
</span></span><span style="display:flex;"><span>pm2 save                    <span style="color:#6272a4"># 保存当前进程列表快照（供 resurrect / 自启恢复）</span>
</span></span></code></pre></div><p>所有命令都可以用进程名、进程 id，或 <code>all</code>（全部进程）作为目标。</p>
<h3 id="常用启动选项">常用启动选项</h3>
<table>
	<thead>
			<tr>
					<th>选项</th>
					<th>作用</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>--name &lt;n&gt;</code></td>
					<td>指定进程名</td>
			</tr>
			<tr>
					<td><code>-i &lt;n&gt;</code> / <code>--instances &lt;n&gt;</code></td>
					<td>实例数，<code>max</code> 表示按 CPU 核数</td>
			</tr>
			<tr>
					<td><code>--watch</code></td>
					<td>文件变更自动重启（仅开发用）</td>
			</tr>
			<tr>
					<td><code>--max-memory-restart 500M</code></td>
					<td>内存超限自动重启</td>
			</tr>
			<tr>
					<td><code>--cron-restart &quot;0 3 * * *&quot;</code></td>
					<td>定时重启</td>
			</tr>
			<tr>
					<td><code>--env KEY=VAL</code>（ecosystem 中用）</td>
					<td>注入环境变量</td>
			</tr>
			<tr>
					<td><code>--time</code></td>
					<td>日志加时间戳</td>
			</tr>
			<tr>
					<td><code>--no-autorestart</code></td>
					<td>崩溃后不自动拉起</td>
			</tr>
	</tbody>
</table>
<hr>
<h2 id="ecosystem-配置文件">ecosystem 配置文件</h2>
<p>生产环境不要靠 <code>pm2 start</code> 手敲参数，用 <code>ecosystem.config.js</code> 固化配置：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#6272a4">// ecosystem.config.js
</span></span></span><span style="display:flex;"><span>module.exports <span style="color:#ff79c6">=</span> {
</span></span><span style="display:flex;"><span>  apps<span style="color:#ff79c6">:</span> [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      name<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;api-server&#34;</span>,
</span></span><span style="display:flex;"><span>      script<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;./dist/app.js&#34;</span>,
</span></span><span style="display:flex;"><span>      cwd<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;/srv/myapp&#34;</span>,
</span></span><span style="display:flex;"><span>      instances<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;max&#34;</span>,           <span style="color:#6272a4">// 集群实例数
</span></span></span><span style="display:flex;"><span>      exec_mode<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;cluster&#34;</span>,       <span style="color:#6272a4">// cluster | fork
</span></span></span><span style="display:flex;"><span>      env<span style="color:#ff79c6">:</span> {
</span></span><span style="display:flex;"><span>        NODE_ENV<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;production&#34;</span>,
</span></span><span style="display:flex;"><span>        PORT<span style="color:#ff79c6">:</span> <span style="color:#bd93f9">3000</span>,
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#6272a4">// 内存与稳定性
</span></span></span><span style="display:flex;"><span>      max_memory_restart<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;512M&#34;</span>,
</span></span><span style="display:flex;"><span>      min_uptime<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;10s&#34;</span>,          <span style="color:#6272a4">// 启动不足 10s 就退出视为异常
</span></span></span><span style="display:flex;"><span>      max_restarts<span style="color:#ff79c6">:</span> <span style="color:#bd93f9">10</span>,           <span style="color:#6272a4">// 短时间内最多重启次数
</span></span></span><span style="display:flex;"><span>      restart_delay<span style="color:#ff79c6">:</span> <span style="color:#bd93f9">3000</span>,        <span style="color:#6272a4">// 每次重启间隔 ms
</span></span></span><span style="display:flex;"><span>      <span style="color:#6272a4">// 日志
</span></span></span><span style="display:flex;"><span>      out_file<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;./logs/out.log&#34;</span>,
</span></span><span style="display:flex;"><span>      error_file<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;./logs/err.log&#34;</span>,
</span></span><span style="display:flex;"><span>      merge_logs<span style="color:#ff79c6">:</span> <span style="color:#ff79c6">true</span>,
</span></span><span style="display:flex;"><span>      time<span style="color:#ff79c6">:</span> <span style="color:#ff79c6">true</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#6272a4">// 优雅退出
</span></span></span><span style="display:flex;"><span>      kill_timeout<span style="color:#ff79c6">:</span> <span style="color:#bd93f9">5000</span>,         <span style="color:#6272a4">// SIGINT 后最多等 5s 再强杀
</span></span></span><span style="display:flex;"><span>      listen_timeout<span style="color:#ff79c6">:</span> <span style="color:#bd93f9">8000</span>,       <span style="color:#6272a4">// cluster 模式等待 listen 的超时
</span></span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      name<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;cron-job&#34;</span>,
</span></span><span style="display:flex;"><span>      script<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;./jobs/daily.js&#34;</span>,
</span></span><span style="display:flex;"><span>      cron_restart<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;0 4 * * *&#34;</span>,  <span style="color:#6272a4">// 每天 4 点跑一次逻辑由应用自己控制
</span></span></span><span style="display:flex;"><span>      autorestart<span style="color:#ff79c6">:</span> <span style="color:#ff79c6">false</span>,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>};
</span></span></code></pre></div><p>使用方式：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pm2 start ecosystem.config.js              <span style="color:#6272a4"># 启动全部</span>
</span></span><span style="display:flex;"><span>pm2 start ecosystem.config.js --only api-server
</span></span><span style="display:flex;"><span>pm2 reload ecosystem.config.js             <span style="color:#6272a4"># 按配置重载</span>
</span></span><span style="display:flex;"><span>pm2 delete ecosystem.config.js             <span style="color:#6272a4"># 按配置移除</span>
</span></span></code></pre></div><p>配置变更后需要 <code>pm2 reload ecosystem.config.js</code> 才会生效；只改环境变量可以配合 <code>--update-env</code>：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pm2 reload ecosystem.config.js --update-env
</span></span></code></pre></div><hr>
<h2 id="集群模式cluster-mode">集群模式（Cluster Mode）</h2>
<p>Node 单进程只用一个核。<code>exec_mode: &quot;cluster&quot;</code> 让 PM2 基于 Node 的 cluster 模块启动多个实例，PM2 内置轮询负载均衡，多实例共享同一端口：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pm2 start app.js -i max           <span style="color:#6272a4"># 按 CPU 核数</span>
</span></span><span style="display:flex;"><span>pm2 start app.js -i <span style="color:#bd93f9">4</span>             <span style="color:#6272a4"># 指定 4 个实例</span>
</span></span><span style="display:flex;"><span>pm2 scale api-server <span style="color:#bd93f9">8</span>            <span style="color:#6272a4"># 运行中动态扩缩容</span>
</span></span></code></pre></div><p>注意事项：</p>
<ul>
<li><strong>有状态数据不能放进程内存</strong>（session、内存缓存、内存队列），要放 Redis / 数据库。</li>
<li>WebSocket 类应用需要考虑粘性会话（sticky session），通常交给前面的 Nginx / LB 处理，或用 Redis adapter。</li>
<li>集群模式才支持真正的 <code>reload</code> 零停机；fork 模式的 <code>reload</code> 等价于 <code>restart</code>。</li>
</ul>
<hr>
<h2 id="日志管理">日志管理</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pm2 logs                    <span style="color:#6272a4"># 实时流式查看全部日志</span>
</span></span><span style="display:flex;"><span>pm2 logs api-server         <span style="color:#6272a4"># 指定进程</span>
</span></span><span style="display:flex;"><span>pm2 logs --lines <span style="color:#bd93f9">200</span>        <span style="color:#6272a4"># 带历史行数</span>
</span></span><span style="display:flex;"><span>pm2 logs --err              <span style="color:#6272a4"># 只看错误日志</span>
</span></span><span style="display:flex;"><span>pm2 flush                   <span style="color:#6272a4"># 清空全部日志</span>
</span></span></code></pre></div><p>默认日志在 <code>~/.pm2/logs/</code>。生产环境建议：</p>
<ol>
<li>应用自身用结构化日志库（pino / winston）输出，PM2 只负责兜底捕获 stdout/stderr。</li>
<li>安装 <code>pm2-logrotate</code> 做切割，避免日志撑爆磁盘：</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pm2 install pm2-logrotate
</span></span><span style="display:flex;"><span>pm2 <span style="color:#8be9fd;font-style:italic">set</span> pm2-logrotate:max_size 100M
</span></span><span style="display:flex;"><span>pm2 <span style="color:#8be9fd;font-style:italic">set</span> pm2-logrotate:retain <span style="color:#bd93f9">14</span>
</span></span><span style="display:flex;"><span>pm2 <span style="color:#8be9fd;font-style:italic">set</span> pm2-logrotate:compress <span style="color:#8be9fd;font-style:italic">true</span>
</span></span><span style="display:flex;"><span>pm2 <span style="color:#8be9fd;font-style:italic">set</span> pm2-logrotate:rotateInterval <span style="color:#f1fa8c">&#34;0 0 * * *&#34;</span>
</span></span></code></pre></div><hr>
<h2 id="监控与指标">监控与指标</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pm2 monit                   <span style="color:#6272a4"># 终端实时面板：CPU / 内存 / 重启次数</span>
</span></span><span style="display:flex;"><span>pm2 describe api-server     <span style="color:#6272a4"># 静态详情</span>
</span></span><span style="display:flex;"><span>pm2 plus                    <span style="color:#6272a4"># 连接 PM2 Plus（原 Keymetrics）在线面板，免费额度够用</span>
</span></span></code></pre></div><p>采集自定义指标（供 <code>pm2 monit</code> 和 PM2 Plus 显示）：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#6272a4">// 应用内
</span></span></span><span style="display:flex;"><span><span style="color:#ff79c6">const</span> io <span style="color:#ff79c6">=</span> require(<span style="color:#f1fa8c">&#34;@pm2/io&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">const</span> orderCounter <span style="color:#ff79c6">=</span> io.counter({ name<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;orders_created&#34;</span> });
</span></span><span style="display:flex;"><span><span style="color:#ff79c6">const</span> onlineUsers <span style="color:#ff79c6">=</span> io.meter({ name<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;online_users&#34;</span> });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>orderCounter.inc();
</span></span><span style="display:flex;"><span>onlineUsers.mark();
</span></span></code></pre></div><p>配合告警动作（内存超过阈值触发 webhook 等）可以用 <code>@pm2/io</code> 的 action / 阈值能力，或直接接 Prometheus：用 <code>pm2-prometheus-exporter</code> 暴露指标给 Prometheus + Grafana，这是自托管监控的主流方案。</p>
<hr>
<h2 id="开机自启">开机自启</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6272a4"># 1. 生成当前用户的 systemd / launchd 启动脚本</span>
</span></span><span style="display:flex;"><span>pm2 startup
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># 按提示复制执行它输出的命令（需要 sudo）</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># 2. 保存当前进程列表</span>
</span></span><span style="display:flex;"><span>pm2 save
</span></span></code></pre></div><p>之后机器重启，PM2 daemon 会拉起 <code>pm2 save</code> 快照里的所有进程。</p>
<p>注意：</p>
<ul>
<li><code>pm2 startup</code> 之后再新增的进程，必须重新 <code>pm2 save</code> 才会加入自启列表。</li>
<li>取消自启：<code>pm2 unstartup</code>。</li>
<li>服务器上如果以非 root 用户运行应用，startup 时要用 <code>pm2 startup systemd -u &lt;user&gt; --hp /home/&lt;user&gt;</code> 指定用户，否则权限和 <code>~/.pm2</code> 路径会对不上。</li>
</ul>
<hr>
<h2 id="零停机重载与发布">零停机重载与发布</h2>
<p><code>reload</code> 的流程：逐实例启动新进程 → 新进程 <code>listen</code> 成功 → 旧进程收到 SIGINT → 旧进程清理完毕退出。应用端需要配合两件事：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#6272a4">// 1. 处理 SIGINT，优雅关闭
</span></span></span><span style="display:flex;"><span>process.on(<span style="color:#f1fa8c">&#34;SIGINT&#34;</span>, <span style="color:#ff79c6">async</span> () =&gt; {
</span></span><span style="display:flex;"><span>  server.close();                 <span style="color:#6272a4">// 停止接新连接
</span></span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">await</span> db.disconnect();          <span style="color:#6272a4">// 释放数据库连接等资源
</span></span></span><span style="display:flex;"><span>  process.exit(<span style="color:#bd93f9">0</span>);
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4">// 2. 用 ready 信号告知 PM2 &#34;我已就绪&#34;（需配置 wait_ready: true）
</span></span></span><span style="display:flex;"><span><span style="color:#6272a4">//    listen 成功后：
</span></span></span><span style="display:flex;"><span><span style="color:#ff79c6">if</span> (process.send) process.send(<span style="color:#f1fa8c">&#34;ready&#34;</span>);
</span></span></code></pre></div><p>ecosystem 中对应配置：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>wait_ready<span style="color:#ff79c6">:</span> <span style="color:#ff79c6">true</span>,
</span></span><span style="display:flex;"><span>listen_timeout<span style="color:#ff79c6">:</span> <span style="color:#bd93f9">8000</span>,
</span></span><span style="display:flex;"><span>kill_timeout<span style="color:#ff79c6">:</span> <span style="color:#bd93f9">5000</span>,
</span></span></code></pre></div><p>典型发布流程：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">cd</span> /srv/myapp
</span></span><span style="display:flex;"><span>git pull
</span></span><span style="display:flex;"><span>pnpm install --prod
</span></span><span style="display:flex;"><span>pnpm build
</span></span><span style="display:flex;"><span>pm2 reload ecosystem.config.js --update-env   <span style="color:#6272a4"># 零停机</span>
</span></span></code></pre></div><p>PM2 也内置了 <code>pm2 deploy</code>（基于 SSH 的部署机），但现在一般用 CI/CD（GitHub Actions / GitLab CI）跑上面的脚本替代。</p>
<hr>
<h2 id="内存与异常处理策略">内存与异常处理策略</h2>
<p>生产进程一定会遇到内存泄漏和随机崩溃，PM2 的价值就在这里：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  max_memory_restart<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;512M&#34;</span>,   <span style="color:#6272a4">// 内存超限重启，兜底内存泄漏
</span></span></span><span style="display:flex;"><span>  min_uptime<span style="color:#ff79c6">:</span> <span style="color:#f1fa8c">&#34;10s&#34;</span>,            <span style="color:#6272a4">// 启动 10s 内就挂 = 启动失败
</span></span></span><span style="display:flex;"><span>  max_restarts<span style="color:#ff79c6">:</span> <span style="color:#bd93f9">10</span>,             <span style="color:#6272a4">// 1 分钟内重启 10 次后放弃，避免死循环重启
</span></span></span><span style="display:flex;"><span>  restart_delay<span style="color:#ff79c6">:</span> <span style="color:#bd93f9">3000</span>,          <span style="color:#6272a4">// 退避重启
</span></span></span><span style="display:flex;"><span>  autorestart<span style="color:#ff79c6">:</span> <span style="color:#ff79c6">true</span>,
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>排障三板斧：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pm2 describe &lt;name&gt;    <span style="color:#6272a4"># 看 unstable restarts / 最近退出码</span>
</span></span><span style="display:flex;"><span>pm2 logs &lt;name&gt; --err --lines <span style="color:#bd93f9">100</span>
</span></span><span style="display:flex;"><span>pm2 report             <span style="color:#6272a4"># 生成诊断报告（含 daemon 状态）</span>
</span></span></code></pre></div><hr>
<h2 id="常见坑与排查">常见坑与排查</h2>
<p><strong>1. <code>pm2 startup</code> 后重启机器进程没起来</strong>
多半是没有重新 <code>pm2 save</code>，或 startup 时用户不一致导致读的是另一个 <code>~/.pm2/dump.pm2</code>。</p>
<p><strong>2. 集群模式下应用启动报 <code>EADDRINUSE</code></strong>
fork 模式误当 cluster 用，或应用自己手动监听了端口冲突逻辑。cluster 模式下只需正常 <code>listen(port)</code>，由 PM2 共享端口。</p>
<p><strong>3. 日志没有内容</strong>
应用用了 daemonize / fork 后台化（比如某些 Python 框架的 <code>--daemon</code>），PM2 捕获不到输出。去掉应用自身的后台化，把前台运行交给 PM2。</p>
<p><strong>4. <code>--watch</code> 在线上把服务打挂</strong>
watch 只适合开发。生产用 CI 触发 <code>reload</code>。</p>
<p><strong>5. 环境变量不生效</strong>
<code>reload</code> 不会自动刷新 env，要加 <code>--update-env</code>；<code>.env</code> 文件建议在应用内用 dotenv 加载，而不是依赖 PM2。</p>
<p><strong>6. dump.pm2 损坏导致 daemon 起不来</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pm2 <span style="color:#8be9fd;font-style:italic">kill</span>
</span></span><span style="display:flex;"><span>rm -rf ~/.pm2/dump.pm2
</span></span><span style="display:flex;"><span>pm2 resurrect   <span style="color:#6272a4"># 或重新 pm2 start ecosystem.config.js &amp;&amp; pm2 save</span>
</span></span></code></pre></div><hr>
<h2 id="速查表">速查表</h2>
<table>
	<thead>
			<tr>
					<th>场景</th>
					<th>命令</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>启动</td>
					<td><code>pm2 start ecosystem.config.js</code></td>
			</tr>
			<tr>
					<td>列表</td>
					<td><code>pm2 ls</code></td>
			</tr>
			<tr>
					<td>详情</td>
					<td><code>pm2 describe &lt;name&gt;</code></td>
			</tr>
			<tr>
					<td>日志</td>
					<td><code>pm2 logs &lt;name&gt; --lines 100</code></td>
			</tr>
			<tr>
					<td>重启</td>
					<td><code>pm2 restart &lt;name&gt;</code></td>
			</tr>
			<tr>
					<td>零停机重载</td>
					<td><code>pm2 reload &lt;name&gt;</code></td>
			</tr>
			<tr>
					<td>扩缩容</td>
					<td><code>pm2 scale &lt;name&gt; 4</code></td>
			</tr>
			<tr>
					<td>监控面板</td>
					<td><code>pm2 monit</code></td>
			</tr>
			<tr>
					<td>保存快照</td>
					<td><code>pm2 save</code></td>
			</tr>
			<tr>
					<td>开机自启</td>
					<td><code>pm2 startup</code> + <code>pm2 save</code></td>
			</tr>
			<tr>
					<td>清空全部</td>
					<td><code>pm2 delete all</code> / <code>pm2 kill</code>（杀 daemon）</td>
			</tr>
			<tr>
					<td>升级 PM2 后刷新 daemon</td>
					<td><code>pm2 update</code></td>
			</tr>
	</tbody>
</table>
<hr>
<p>一句话总结：<strong>本地 <code>pm2 start</code> 随便玩，生产一律 ecosystem 文件 + cluster 模式 + <code>pm2 save</code> + logrotate</strong>，这四件事做完，PM2 才算真正&quot;管好&quot;了你的进程。</p>
]]></content:encoded></item></channel></rss>