十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

pm2 进程管理实战:Node 服务状态、重启与日志全解析

pm2 进程管理实战:Node 服务状态、重启与日志全解析 1. 为什么我把 Node 服务从 nohup 挪到了 pm2我第一次把 Node 服务扔到服务器上用的是最土的办法nohup node app.js app.log 21 。当天晚上跑得挺好第二天早上登录一看进程没了日志最后一行停在凌晨三点多没有任何报错。后来才知道是内存吃满被系统清掉了。那之后我陆续试过写 systemd 单元文件、写 shell 守护脚本直到换成 pm2才算真正把「查看服务状态、重启服务、看日志」这三件每天都在做的事固定成了一套顺手流程。这篇内容就是围绕这三件事展开的怎么用 pm2 快速看清现在有几个进程、各自什么状态怎么在不停服或者少停服的前提下重启日志到底写在哪、怎么看、怎么防止它把磁盘写满。整套东西适合已经在跑 Node 服务但还在用nohuppstail -f手动折腾的人也适合刚接手一台别人配置过的服务器、需要快速搞清楚现状的人。命令不复杂真正费时间的是那些「重启完发现没生效」「日志文件把盘吃满了」「开了 cluster 结果内存翻倍」的坑我会把我踩过的都写出来。需要先说清楚一点pm2 是一个进程管理器它本身不解决业务代码的问题。进程反复重启根因多半在代码或环境pm2 只是把这件事暴露得更明显。想清楚这个前提后面的排查思路才不会跑偏。2. 查看服务状态先把家底盘清楚接手一台服务器我的第一条命令永远是pm2 list而不是先去看代码。原因很简单代码可以慢慢读但进程现在是不是活着、重启了多少次、吃了多少内存这些是会变的先固化下来一份现场快照后面所有判断才有依据。2.1 pm2 list 以及那几个等价的写法pm2 list、pm2 ls、pm2 status、pm2 ps这四个命令输出完全一样纯粹是照顾不同人的肌肉记忆。我自己习惯敲pm2 ls因为短。输出大致是这样一张表pm2 ls┌────┬──────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐ │ id │ name │ namespace │ version │ mode │ pid │ uptime │ │ status │ cpu │ mem │ user │ watching │ ├────┼──────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼────────────────────┼──────────┼──────────┤ │ 0 │ api-gateway │ default │ 1.4.2 │ cluster │ 18231 │ 3D │ 2 │ online │ 0.3% │ 118.4mb │ deploy │ disabled │ │ 1 │ worker │ default │ 1.4.2 │ fork │ 18302 │ 3D │ 18 │ online │ 1.1% │ 240.7mb │ deploy │ disabled │ └────┴──────────────┴──────────────────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘这张表里我最常盯的是三列。第一列是↺也就是 restart 次数这是判断服务健康度最直观的指标刚部署完是 0跑了三天还是 0说明稳如果一小时内涨到几十那基本可以判定进程在反复崩溃此时不要急着重启先去翻日志。第二列是uptime如果某个进程的 uptime 明显比其他实例短说明它刚刚重启过可能是被内存阈值干掉也可能是被手动重启。第三列是memNode 应用的常驻内存在 100 到 300MB 之间算正常区间如果某个实例涨到 1.5GB 还在一路往上爬那基本就是内存泄漏的早期信号。提示pm2 ls的输出在终端宽度不够时会被截断这时候加--no-color或者把终端拉宽比反复滚动屏幕省事得多。2.2 pm2 describe看单个进程的全部家底列表只给概览要看细节得用pm2 describe或者它的同义词pm2 show。pm2 describe api-gateway # 也可以用 id pm2 show 0它的输出信息量很大我一般重点看这几块script path确认跑的是哪个文件服务器上有多个部署目录时这一步能救命避免你在错误的目录里改了半小时代码却毫无效果exec mode确认是 fork 还是 cluster这个直接决定了后面reload能不能做到平滑node args和args确认启动参数有没有带上error log path和out log path直接告诉你日志文件在哪省得去猜路径restarts和历史重启时间点则能帮你判断崩溃是持续性的还是偶发的。还有一个细节值得说pm2 describe会显示unstable restarts和created at。如果unstable restarts不为 0说明进程在启动后极短时间内就挂了被 pm2 判定为「不稳定重启」。这种情况光看主日志往往没用得去 error 日志里找启动阶段的报错常见的是端口占用、环境变量缺失、依赖没装全。2.3 pm2 monit给不懂命令的人看的实时面板如果同事不熟悉命令行或者我在排查 CPU 尖刺的时候想直观看到内存曲线我会开pm2 monit。pm2 monit它是一个全屏的交互式面板左边是进程列表右边分为上下两块上面是选中的进程的实时日志流下面是 CPU 和内存的实时数值。按上下方向键可以切换进程CtrlC 退出。这个面板有个很大的优势它把「日志」和「资源占用」放在同一个屏幕里。很多性能问题的线索就藏在两者的时间关系里——比如你看到日志里每隔 30 秒出现一次批量任务开始同时内存台阶式上跳那基本可以确定是定时任务在处理大批数据时没有释放中间对象。不过它不适合长时间挂着因为它是实时渲染通过 SSH 连接时会持续占用带宽。我要长期观察某个指标的时候更倾向于用pm2 describe加上系统自带的top或者监控面板而不是把 monit 开着不管。2.4 查看命令的横向对比命令输出形态适合场景是否会持续刷新pm2 ls表格概览快速确认进程数量与状态否一次输出pm2 describe name详细键值对排查配置、路径、重启历史否pm2 monit全屏实时面板边看日志边看资源占用是直到手动退出pm2 jlistJSON交给脚本解析、接入自建监控否pm2 prettylist格式化 JSON人工阅读 JSON 字段否pm2 jlist这个命令值得单独提一句。它输出的是机器可读的 JSON内容比pm2 ls的表丰富得多包括每个进程的环境变量、内存上限、重启延迟等。我早期自己写过一个每分钟执行一次的采集脚本就是把pm2 jlist的输出解析后写进时序库这样就能画出过去一周的重启次数曲线。很多团队上来就上完整的监控体系其实在规模不大的时候用pm2 jlist加一个定时任务就能解决大部分「什么时候开始变坏的」问题。3. 重启服务的几种姿势和它们的区别「重启」这个词在 pm2 里对应着好几个不同的命令它们的语义差别很大用错了要么白重启要么短暂中断服务。我见过最常见的误用是在 cluster 模式下用restart去更新代码结果所有实例同时挂掉又同时起来接口出现几秒钟的 502。3.1 restart、reload、stop 加 start 到底差在哪先说结论restart是杀死进程再拉起来reload是平滑重启仅在 cluster 模式下有意义stop再start等于彻底停止再启动多了一步状态清理。pm2 restart的行为是直接向进程发送信号默认是SIGINT进程收到后被终止然后 pm2 立刻用同样的配置重新拉起。这个过程有个明显特征进程 ID 会变uptime归零↺计数加一。它快但在这几十毫秒到几秒的窗口里进程是不处理请求的。如果是单实例部署用户就会遇到一瞬间的连接失败。pm2 reload则完全是另一套逻辑。它只在 cluster 模式下有效做的事情是逐个替换 worker先启动一个新的 worker等它报告 ready默认通过 Node 的process.send(ready)或者监听端口成功来判断再把老的 worker 优雅关闭。整个过程对外始终有实例在监听端口所以能做到用户无感知。代价是内存会短暂翻倍因为新旧 worker 会有一段时间共存。pm2 stop加pm2 start和restart的区别在于stop会把进程状态标记为stopped你可以在停止期间修改配置、调整环境变量然后再启动。有些场景下必须走这条路比如要修改exec_mode、实例数量或者启动脚本本身这些改动restart是不认的——restart只复用已有配置改instances这类参数后必须delete再加start才会生效。# 强制杀死再拉起最快但有短暂中断 pm2 restart api-gateway # 平滑重启cluster 模式下用户无感知 pm2 reload api-gateway # 停止后修改配置再启动 pm2 stop api-gateway # ...调整 ecosystem 配置... pm2 start api-gateway3.2 精准重启单个进程与批量操作pm2 支持通过三种标识来指定目标进程名、进程 id、以及all。pm2 restart api-gateway # 按名字 pm2 restart 0 # 按 id pm2 restart all # 全部 pm2 restart api-* # 按名字前缀匹配带引号的通配符匹配这个用法在实际运维里非常实用。比如我把网关相关的几个服务统一命名成gateway-http、gateway-ws、gateway-admin那更新网关层代码后只需要一条pm2 restart gateway-*不用担心漏掉某个或者误伤业务服务。命名规范这件事在只有两三个服务时看不出价值等到服务超过十个你会庆幸当初统一了前缀。还有一种不常见的操作只重启某个进程组里的部分实例。pm2 本身没有直接支持但可以借助cluster模式下实例的独立 id 来实现先pm2 ls找到具体实例 id再pm2 restart id。这个技巧在做灰度验证时有用——比如你想先让一个新版本只在一个实例上跑五分钟看看错误率确认无异常后再 reload 全部。3.3 重启后没生效这三个坑我都踩过第一个坑是环境变量。pm2 在启动进程时会继承当时的 shell 环境变量并在restart时复用这份快照。也就是说如果你在.bashrc里加了一个新的环境变量然后执行pm2 restart进程里是看不到这个新变量的。必须pm2 delete然后重新pm2 start才会重新读取。我因为这个排查了整整一个下午一直以为是代码里的配置读取逻辑有问题。第二个坑是代码没有真正更新。pm2 restart只是重启进程不会拉取新代码也不会重新执行构建。如果你的部署流程是「git pull 然后 pm2 restart」那对于需要编译的项目TypeScript 编译产物、前端构建产物是不够的编译产物还是旧的。正确顺序是先构建再重启而且要注意构建产物落在的目录和describe里显示的script path必须一致。第三个坑是reload在 fork 模式下等于restart。很多人以为pm2 reload永远是平滑的其实不是。如果进程是以 fork 模式启动的pm2 没有多实例可供轮流切换reload会退化成和restart一样的硬重启。所以想享受平滑重启前提是启动时就用了 cluster 模式也就是-i参数指定了实例数量-i max表示按 CPU 核心数。注意reload的平滑效果依赖进程能正确报告 ready。如果你的应用启动后需要几秒钟初始化数据库连接池但在启动瞬间就开始监听端口那 pm2 会认为它已经就绪并关闭旧实例此时新实例其实还不能正常处理请求用户依然会碰到错误。这种场景下需要在代码里显式调用process.send(ready)并配合wait_ready: true和listen_timeout使用。4. 日志pm2 logs 只是入门日志是排查问题唯一可靠的线索来源但很多人对 pm2 日志的认知停留在「敲pm2 logs看滚动输出」。真到了要定位一个三天前发生的偶发错误时滚动输出帮不上任何忙你得知道日志文件的准确位置、两个文件的区别、以及怎么在几百万行里快速筛出目标。4.1 pm2 logs 的常用参数组合pm2 logs默认会持续流式输出所有进程的 stdout 和 stderr并且会在前面加上进程名和 id 作为前缀。日常我常用的组合有这么几个# 只看某个进程滚动输出 pm2 logs api-gateway # 只看错误输出 pm2 logs api-gateway --err # 只看标准输出 pm2 logs api-gateway --out # 一次性打印最后 500 行然后退出不挂住终端 pm2 logs api-gateway --lines 500 --nostream # 输出纯文本去掉 pm2 自己加的前缀方便重定向到文件 pm2 logs api-gateway --raw --lines 2000 /tmp/dump.log # 输出 JSON交给脚本处理 pm2 logs --json --lines 1000 --nostream--nostream这个参数我要重点强调。默认的pm2 logs是流式的会一直挂在那里很多人通过脚本调用它时会发现脚本永远不返回就是这个原因。加上--nostream后它变成一次性的输出命令可以直接放进脚本里做日志快照。--raw也很有价值。默认输出里每一行都会带上进程名 |这样的前缀还有 pm2 自己加的换行处理。当你需要把日志丢给grep做精确匹配、或者丢给日志分析工具时这些前缀会干扰结果。--raw会把原始内容原样吐出来。4.2 日志文件在哪为什么会有两个文件日志的默认目录是当前用户的~/.pm2/logs/文件名规则是进程名-out.log和进程名-error.log。所以api-gateway这个进程的日志就是~/.pm2/logs/api-gateway-out.log ~/.pm2/logs/api-gateway-error.log不记得路径的时候pm2 describe api-gateway里的out log path和error log path字段会直接告诉你这比去猜路径靠谱。为什么是两个文件这跟 Unix 的标准流设计有关。进程有 stdout标准输出和 stderr标准错误两个独立的流console.log走前者console.error走后者。pm2 默认把它们分别重定向到两个不同的文件里。这个设计有好处也有坏处好处是你可以单独--err只看错误噪音少坏处是当一个请求的错误链路同时打了普通日志和错误日志时两边的顺序对不上排查时需要来回对照。我的做法是在配置文件里加上merge_logs: true把两个流合并到一个文件里。对于 cluster 模式下的多实例合并日志尤其重要否则不同实例的日志散落在不同文件里你根本拼不出一个请求的完整生命周期。合并之后所有实例的输出都进同一个文件靠行首的进程 id 前缀区分来源。4.3 用 pm2 flush 和 pm2-logrotate 管住磁盘日志这块最典型的故障不是「看不到日志」而是「日志把磁盘写满了」。Node 服务的日志增长速度经常被低估一个每秒处理几百请求的接口如果每个请求打三行日志一天下来轻松上百 GB。磁盘一满数据库写不进去、进程起不来整个服务链崩掉。第一个要会用的命令是pm2 flush# 清空所有进程的日志文件 pm2 flush # 只清空某个进程 pm2 flush api-gateway它的作用是把日志文件内容截断为 0不会删除文件本身。这个操作要谨慎清掉之后就真没了所以我的习惯是先pm2 logs --nostream --lines 5000 /tmp/backup-$(date %F).log备份一份再 flush。真正解决问题的是日志切割。pm2 自身不带轮转功能需要装一个官方模块pm2 install pm2-logrotate装完之后它的默认行为是单文件超过 10MB 就切割、保留 30 份、开启压缩。这几个默认值对生产环境来说都偏保守我一般会改成这样pm2 set pm2-logrotate:max_size 100M pm2 set pm2-logrotate:retain 14 pm2 set pm2-logrotate:compress true pm2 set pm2-logrotate:rotateInterval 0 0 * * * pm2 set pm2-logrotate:dateFormat YYYY-MM-DD_HH-mm-ss pm2 set pm2-logrotate:workerInterval 30参数含义逐个说清楚。max_size是单个日志文件的大小上限超过就切retain是保留的历史文件份数14 份配合每日轮转就是两周的可追溯窗口这个长度对大多数团队够用了毕竟再久之前的日志一般也查不到有效信息compress开启 gzip 压缩能把文本日志压到原来的十分之一左右rotateInterval是按固定时间切割我设成每天零点这样日志文件天然按天分段排查时直接按日期找文件workerInterval是检查频率单位秒30 秒意味着最坏情况下日志会超出上限一点但对磁盘影响可以忽略。注意改完 pm2-logrotate 的配置后别忘了执行一次pm2 reloadLogs。这个命令是让 pm2 重新打开日志文件句柄。切割工具做的是重命名或删除文件但 pm2 持有的还是旧的文件描述符如果不 reload进程会继续往那个已经被移走的文件里写导致「切割生效了但新文件没内容」的诡异现象。5. 开机自启、集群模式和 ecosystem 配置前面讲的都是运行时操作这一节讲怎么把配置固化下来。手工pm2 start出来的进程一旦服务器重启就全没了而且各种参数散落在命令行里过两个月自己都想不起来当时为什么加了某个参数。5.1 startup 和 save 的正确操作顺序开机自启依赖两步而且顺序不能反。# 第一步生成并安装系统服务单元 pm2 startup # 它会输出一条类似下面的命令需要你手动执行提示会带具体用户和路径 # sudo env PATH$PATH:/usr/bin pm2 startup systemd -u deploy --hp /home/deploy # 第二步把当前进程列表存成快照 pm2 savepm2 startup做的是在你的系统里注册一个开机启动项让机器启动时自动拉起 pm2 守护进程。它本身不会记住你有哪些应用所以必须再执行pm2 save把当前内存里的进程列表写到~/.pm2/dump.pm2这个快照文件里。开机时 pm2 读这个文件来恢复所有进程。我踩过的坑是这样先执行了pm2 save之后又加了一个新服务但忘记再save一次结果服务器重启后新服务没起来排查了半天才发现快照是旧的。所以我的习惯是——只要动过进程列表新增、删除、改了名字或启动参数马上补一条pm2 save。还有一点pm2 startup输出的那条命令必须用提示里的完整形式执行尤其是-u和--hp参数。如果直接sudo pm2 startup而不带用户信息开机启动会以 root 身份运行 pm2你的应用也跟着以 root 跑~/.pm2/logs目录会变成 root 所有之后用普通用户操作时会到处都是权限报错。恢复快照也可以手动触发pm2 resurrect这个命令在误删进程列表之后特别有用能从快照文件里把进程定义重新读出来。5.2 cluster 模式与实例数的选择Node 是单线程执行 JavaScript 的一个进程只能用到一个 CPU 核心。现代服务器动辄 8 核 16 核单进程部署等于浪费了绝大部分算力。cluster 模式解决的就是这个问题它基于 Node 内置的 cluster 模块fork 出多个共享同一个监听端口的 worker。# 按 CPU 核心数自动决定实例数 pm2 start app.js -i max # 指定 4 个实例 pm2 start app.js -i 4 # 在配置文件中写死 # instances: 4实例数怎么定max是常见起点但它有个前提你的应用必须是「无状态」或者「状态外置」的。如果应用在内存里维护会话、缓存、定时任务多实例会立刻出问题——用户请求可能落到不同实例上会话丢失定时任务每个实例都跑一遍数据被重复处理。这种情况下要么先用一个实例要么把这些状态挪到外部存储里。另一个需要提醒的点是内存。cluster 模式下内存是按实例累加的pm2 ls里显示的 mem 是单个实例的值。设了max_memory_restart: 500M8 个实例的实际内存上限是 4GB 而不是 500MB。我曾经在 4GB 内存的机器上开了 8 个实例每个限 800M结果触发系统 OOM进程被系统杀掉pm2 又自动重启形成循环。正确的算法是先算总可用内存减去系统和数据库占用再除以实例数留出 30% 的余量。5.3 一份可以直接抄的 ecosystem.config.js配置文件的优势是版本可控、参数集中、多环境切换清晰。我把它放在项目根目录和代码一起提交到仓库。module.exports { apps: [ { name: api-gateway, script: ./dist/server.js, cwd: /opt/apps/api-gateway, instances: 4, exec_mode: cluster, watch: false, max_memory_restart: 600M, autorestart: true, max_restarts: 10, min_uptime: 20s, restart_delay: 3000, kill_timeout: 8000, wait_ready: true, listen_timeout: 15000, merge_logs: true, log_date_format: YYYY-MM-DD HH:mm:ss.SSS, error_file: /data/logs/api-gateway/error.log, out_file: /data/logs/api-gateway/out.log, env: { NODE_ENV: production, PORT: 3000 }, env_staging: { NODE_ENV: staging, PORT: 3100 } } ] }挑几个参数解释为什么这么设。max_restarts配合min_uptime是一道保险如果进程在启动后 20 秒内就崩溃且这种情况连续发生 10 次pm2 会停止重启并把它标记为errored。这比无限重启要好得多因为无限重启会让日志被刷屏真正的第一条报错被淹没同时也消耗大量 CPU。restart_delay是每次重启前的等待时间给外部依赖数据库、缓存一点恢复时间避免雪崩式的重启风暴。kill_timeout是给进程的优雅退出时限如果你的应用在退出前需要关闭连接池、写完缓冲日志这个值要设得比默认的 1600ms 更大一些。启动和切换环境pm2 start ecosystem.config.js pm2 start ecosystem.config.js --env staging pm2 reload ecosystem.config.js --env production用配置文件启动后pm2 reload ecosystem.config.js可以一次性对配置里所有应用做平滑重启比逐个敲命令省事很多。6. 常见故障排查实录这一节是我这几年的问题记录本按现象反查原因。真出事的时候顺着「现象 → 判断依据 → 处置方式」去看比从头读文档快得多。6.1 进程反复重启restart 次数一路飙升最典型的表现就是pm2 ls里某个进程的↺数字不停增长状态在online和errored之间跳。我的排查顺序固定是三步。第一步看 error 日志的末尾。因为重启循环会把日志刷得很快所以要立刻抓一份快照pm2 logs api-gateway --err --lines 300 --nostream第二步看最新一次重启前的完整启动过程。如果应用启动阶段就有语法错误、模块加载失败、端口占用这里会直接暴露pm2 describe api-gateway | grep -A 5 restart time第三步判断是「启动即崩」还是「跑一阵才崩」。pm2 describe里的unstable restarts如果不为 0说明是启动阶段就挂问题在代码或环境如果这个值是 0但重启次数在涨说明是运行一段时间后崩方向转向内存、未捕获异常、或者外部依赖超时。按我的经验重启循环的原因分布大概是这样的端口被占用EADDRINUSE占一成多环境变量缺失占两成未捕获的 Promise 异常占三成内存超限占两成多剩下的是外部依赖连不上。端口占用这个尤其常见于「旧进程没杀干净」的场景此时pm2 delete再加start比restart更彻底。6.2 日志不输出或者输出乱码日志完全不写的第一个检查点是看进程有没有真正在运行。pm2 ls显示online但日志为空通常是应用本身没打日志或者日志级别设成了 warn 以上。用pm2 logs --raw直接看原始输出能排除 pm2 前缀的干扰。第二个检查点是日志文件的权限和目录。如果error_file指向的目录不存在pm2 启动时会报警。自定义日志路径时一定要确认目录已经创建并且当前用户有写权限mkdir -p /data/logs/api-gateway chown deploy:deploy /data/logs/api-gateway至于乱码多数情况是日志内容里混了彩色控制字符比如某些库在检测到终端时输出 ANSI 转义码。解决办法是在生产环境里关掉彩色输出或者用--raw加sed过滤掉转义序列。我在 CI 环境里统一设了FORCE_COLOR0从此再没遇到过。6.3 内存缓慢上涨与 max_memory_restart 的正确用法max_memory_restart经常被当成万能药其实它只是一个兜底。它的机制是pm2 定期检查进程的内存占用一旦超过阈值就把它重启。问题是这个检查有间隔而且重启本身是有代价的——正在处理的请求会被打断用户会收到错误。所以更正确的姿势是把它设成一个「明显异常」的值而不是「稍微偏高就要重启」的值。判断依据可以从历史数据来正常运行时内存稳定在 200MB偶发峰值到 400MB那阈值设 800MB 比较合理只有真的失控了才触发。内存持续上涨的排查我一般会先确认是不是缓存没设上界。很多库默认是无限制缓存比如某些 ORM 的查询缓存、某些 HTTP 客户端的连接池。给它们都设上明确的上限之后内存曲线通常就会从斜线变成平线。这一步不解决的话重启只是把问题推迟几十分钟。6.4 常见问题速查表现象大概率原因处置方式启动即崩unstable restarts大于 0端口占用、环境变量缺失、依赖未安装查 error 日志首屏pm2 delete后重启重启次数持续增长运行一阵才崩未捕获异常、内存超限、外部依赖超时抓 error 日志末段结合内存曲线判断reload后仍有短暂 502实际是 fork 模式或未实现 ready 上报改 cluster 模式代码中调用process.send(ready)改了环境变量但restart后没生效pm2 复用启动时的环境快照pm2 delete后重新start磁盘被日志写满未配置日志轮转安装 pm2-logrotate 并调大 retain日志切割后新文件为空pm2 未重新打开文件句柄执行pm2 reloadLogs服务器重启后服务不全pm2 save快照过期重新pm2 save必要时pm2 resurrect多实例下会话丢失应用持有内存态会话会话外置到共享存储或先退回单实例这张表我建议直接存成便利贴因为绝大多数线上告警都能对到其中一行。剩下那些对不上的基本就是业务代码自己的问题了方向不同排查手段也不同。7. 一些不值得重复踩的经验关于 pm2我最后想分享几个和命令无关、但确实省了我很多时间的做法。其一是给进程起名字要克制。我见过用完整路径当名字的、用中文当名字的、用时间戳当名字的最后都因为名字太长或者不好敲而后悔。统一用「业务域-组件」的短横线格式长度控制在二十个字符内日常操作会舒服非常多。其二是把pm2 save绑进部署脚本。我现在的部署脚本末尾固定有一句pm2 reload ecosystem.config.js pm2 save这样快照永远和实际运行状态一致服务器意外重启时不会出现版本错乱。其三日志目录别放在系统盘。日志的写入量大且不可控一旦撑爆系统盘连登录和排查都成问题。把日志路径指到独立的数据盘上即使那块盘写满也只是服务不可用机器还能进去处理。这个建议听起来很基础但我在生产环境里见过不止一次因为日志写满根分区导致整台机器不可用的案例。其四遇到「重启就好了」的故障一定不要止步于重启。重启只是把现场清掉了问题还在。我的习惯是重启前先执行一次pm2 logs --nostream --lines 5000 /tmp/incident-$(date %s).log把现场保留下来等恢复之后再慢慢分析。这个动作只花两秒钟但经常是唯一能找到根因的机会。
返回列表