多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

OpenClaw排障手册:分布式爬虫故障排查路径

OpenClaw排障手册:分布式爬虫故障排查路径 简介《OpenClaw常见问题排查手册》面向具备Node.js、Docker和Linux基础的研发、运维人员聚焦OpenClaw安装、启动、Dashboard连接、内网/远程访问及模型调用等环节的故障排查。手册按紧急修复、安装、启动等章节组织覆盖环境配置、权限错误、内存溢出、Docker部署、反向代理、Token配对、网络访问控制等场景针对npm安装缓慢、SSH密钥错误、配置文件异常、网关未运行、远程访问受限、模型无响应等问题并提供了详细的命令示例和分步排错流程图。资源包内共包含1个PDF文档压缩包整体大小467KB目前已有161人学习浏览。读者可按章节对照实际环境查阅优先使用淘宝镜像源、named volume挂载等推荐方案借助openclaw doctor与日志工具快速定位根因覆盖本地、Docker、NAS等多环境有效提升OpenClaw平台部署稳定性与排错效率。1. OpenClaw 排查手册一次抓取事故后我们沉淀的排障路径OpenClaw 是一套基于 Node.js 的分布式爬虫管理平台前端负责配置、后端负责调度采集器用 Docker 编排。前阵子夜间抓取突然停摆调度表里所有任务都停在 Pending前端却一直显示后端健康。我们查了三天最后定位到日志分区 inode 耗尽和 Node 堆内存上限的双重问题才发现故障根本不在业务代码里。从那天起我把这类高频故障整理成一份可执行的 OpenClaw 排查手册这次分享的就是这份资源。它适合用 Node.js 写爬虫、用 Docker 部署后端、又要维护任务调度的开发者。新手能照着逐步定位熟手可以直接对号入座关键参数和边界条件。2. OpenClaw 的运行时模型进程、端口与数据链路2.1 三个进程各司其职主进程、采集器、定时器在碰问题之前先把 OpenClaw 的系统边界看清。它不是单文件应用而是由三个 Node.js 进程协同的分布式系统。调度主进程负责接收前端提交的任务配置写入数据库按 cron 表达式把任务指令投递到 Redis 队列采集器进程负责实际抓取、解析和入库每个进程默认最大并发 5 个任务定时器进程专门处理周期任务这里最常见的坑是时区设置错误。常见部署方式是每个进程独立跑一个容器用 Docker Compose 编排。主进程和采集器之间通过 Redis 的 list 队列通信这样采集器崩溃时任务消息不会丢只会在队列里积压。所以排查时第一步要看 Redis 队列长度而不是直接翻 Node 日志。下面这段命令可以快速确认积压情况redis-cli llen openclaw:task:queue # 正常应该接近 0或者长期在个位数波动 # 如果持续增长说明采集器消费速度跟不上生产速度llen是 Redis 获取列表长度的命令openclaw:task:queue是配置里给任务队列起的键名。我一般还会用redis-cli --scan --pattern openclaw:*看所有相关键确认是任务生产过多还是消费停滞。如果队列长度不变但任务就是不动那问题大概率不在队列而在下游的数据库或代理池。为什么选 Redis 而不是直接用 HTTP 通知采集器因为 HTTP 是点对点如果采集器正在处理上一个任务新的任务请求会直接丢失Redis 队列天然支持削峰和重放采集器挂了重启后会继续消费未确认的消息。这个选型是 OpenClaw 能稳定跑夜班任务的前提。2.2 前端到后端的链路配置保存在哪一步前端用 React 实现提交任务配置时走 REST API。整个链路是前端表单 → POST /api/tasks → 主进程校验 → 写入 MySQL → 插入 Redis 队列 → 采集器轮询。最容易断的三个环节是跨域拦截、后端校验失败、数据库行锁冲突。先看前后端是否同域。OpenClaw 默认前端跑 3000后端跑 8080浏览器会先发 OPTIONS 预检请求。如果前端能打开但一提交配置就报CORS policy去后端环境变量里加一行OPENCLAW_CORS_ORIGINhttp://localhost:3000这个值不能用*因为任务配置里可能带身份认证信息浏览器不允许携带凭证的请求使用通配符。我们之前在测试环境用*一切正常一旦加了credentials: true跨域立刻失效。所以排查时要同时看浏览器请求头里有没有Access-Control-Allow-Credentials。数据库行锁是另一个隐蔽点。主进程写任务表时用了事务采集器回调更新状态如果抛异常事务没提交MySQL 行锁会一直持有。用如下 SQL 查当前事务SELECT * FROM information_schema.INNODB_TRX\G如果看到trx_stateRUNNING且持续时间异常长基本可以判定是事务未提交。常见原因是采集器回调接口没有全局异常捕获可以在入口处加上try/catch同时给 MySQL 设置合理的innodb_lock_wait_timeout防止行锁永续。前端配置本身也要注意类型边界。任务名称限制 64 字符超出的字符串后端会直接返回 400但前端表单如果不做 maxLength 校验用户看到的错误提示就是笼统的“保存失败”。我在前端加了一层 yup 校验错误具体到字段这样至少能区分是后端拒绝还是网络问题。2.3 Docker 部署端口映射与持久化卷OpenClaw 的 Compose 模板里有三个 serviceapi、worker、redis。如果你自己改过编排最容易出问题的是固定端口和卷挂载。我的习惯是每个 service 用固定 IP 和端口而不是随机映射。因为调度器需要回调采集器随机端口会导致容器重启后采集器地址变化所有回调全部失败。在docker-compose.yml里显式声明api: ports: - 8080:8080 volumes: - ./data/logs:/var/log/openclaw healthcheck: test: [CMD, curl, -f, http://localhost:8080/healthz] interval: 10s timeout: 5s retries: 3 worker: ports: - 9000:9000 volumes: - ./data/cache:/var/cache/openclaw./data/logs:/var/log/openclaw是把宿主机日志目录挂进容器容器崩溃后日志不丢。我见过有人把挂载点写反宿主机/var/log被容器覆盖连系统日志都没了。所以每次改 Compose 前先跑docker compose config检查解析结果再执行docker compose up -d --force-recreate。healthcheck 里的接口/healthz如果依赖数据库那么数据库未就绪时容器会一直 unhealthy。这不是容器本身的问题而是上下游依赖顺序的问题。建议在 api 启动脚本里先等待 MySQL再做健康检查。整体上运行时模型的排查顺序应该是Redis 队列 → 数据库锁 → 跨域配置 → 端口映射。把这套链路理清后面的任务运行问题才有排查基础。我把这个顺序写在 OpenClaw 排查手册的第一张流程图里实际用下来能省一半时间。3. npm 依赖与 Docker 启动阶段从安装到跑的常见问题3.1 npm install 卡住或 ETARGET镜像源与版本锁定拿到 OpenClaw 源码后第一件事是装依赖。Node.js 的依赖树很深npm install 卡住是常态。如果项目里有package-lock.json优先用npm ci它按锁文件精确恢复版本速度比npm install快三倍以上也不会擅自改依赖。没有锁文件才用npm install。如果你用的是官方源网络波动时经常超时。我习惯在项目根目录放一个.npmrcregistryhttps://registry.npmmirror.com fetch-retry-maxtimeout60000 fetch-timeout60000fetch-timeout是单个包的请求超时默认 30 秒网络抖动时拉到 60 秒能显著降低 ETIMEDOUT。换镜像解决的是连接问题但版本漂移不是镜像能解决的。有同事把锁文件删了重新装结果anymatch被升到不兼容版本Glob 的私有依赖冲突运行时直接抛TypeError。所以不要轻易删锁文件也不要用npm update去修 bug。常见的错误处理是遇到npm install报错就删node_modules重来。这只能修复文件损坏解决不了依赖树冲突。应该先跑npm ls看冲突的具体链条再决定是升级父依赖还是锁定子版本。如果只是想快速恢复环境用npm ci --prefer-offline配合之前下载过的缓存能减少网络请求。3.2 Docker 容器启动后 healthcheck 一直失败依赖未就绪OpenClaw 的 api 容器启动时如果 MySQL 还没就绪进程会反复退出重试。Docker 的depends_on只控制启动顺序不保证服务可用。所以 api 容器先启动连接 MySQL 失败报错退出然后重启看起来像 healthcheck 失败实际上是缺少等待依赖。解决方法是给 api 容器加一个 entrypoint 脚本先探测 MySQL再启动主进程#!/bin/bash until mysqladmin ping -h mysql -u root -proot --silent; do echo waiting for mysql sleep 2 done node src/index.js注意这个脚本里密码直接写在参数里生产环境不建议这样。我会把密码放在环境变量文件里用--env-file加载或者使用 Docker secret。healthcheck 本身只是一种信号真正的原因是连接超时或连接池耗尽需要在脚本里处理。另一个容易忽略的点是 MySQL 初始化时间。如果你挂载了初始化 SQLMySQL 首次启动要执行脚本可能耗时 30 秒以上。这时候 api 容器如果等待时间不够照样起不来。所以等待脚本里循环次数要设大我的做法是attempt0 while [ $attempt -lt 30 ]; do if mysqladmin ping -h mysql --silent; then break fi attempt$((attempt1)) sleep 2 done超过 60 秒还没就绪就放弃方便排查是 MySQL 配置问题还是网络问题。3.3 前后端分离跨域四个边界条件刚才提了跨域展开讲。OpenClaw 前端用 axios默认带X-Requested-With头后端只配置了 origin 但没允许请求头OPTIONS 预检就会失败。需要在后端加一个全局过滤器处理 OPTIONS 请求并允许凭证app.use((req, res, next) { if (req.method OPTIONS) { res.setHeader(Access-Control-Max-Age, 86400); res.setHeader(Access-Control-Allow-Headers, Content-Type, Authorization, X-Requested-With); res.setHeader(Access-Control-Allow-Methods, GET, POST, PUT, DELETE, OPTIONS); res.status(204).end(); } else { next(); } });这段中间件让预检结果在浏览器缓存 24 小时减少重复请求。我遇到过排查两小时没结果最后发现是Authorization头没加到 Allow-Headers 里导致带 token 的请求一直被拦。这里再强调一次Access-Control-Allow-Origin不能是*除非你把credentials关掉。还容易出错的是 cookie 的SameSite属性。如果前端页面和后端接口不在同一域名且后端种 cookie 时没有设置SameSiteNone; Secure浏览器会忽略 cookie表现为“登录后马上又登出”。这个属于跨域链路的隐藏条件前面三个常见问题都在手册里标注了优先级。3.4 Node 版本不匹配既有报错又定位难OpenClaw 要求 Node.js 版本在 16 到 18 之间但很多环境默认安装了 Node 20。如果版本过高node-gyp在编译某些原生依赖时会报 Python 找不不到或者头文件不匹配。安装过程中最容易出现的是node-pre-gyp报错但实际上只是版本太新。我建议项目内统一用nvm并且在.nvmrc里写死版本echo 18 .nvmrc nvm use然后执行npm ci --registry...。注意 Node 20 的npm版本对应npm10而锁文件是npm9生成的运行时可能有 lockfileVersion 警告。这时候不是大问题但如果你用了overrides字段最好重新生成锁文件否则版本约束可能失效。这个细节在排查手册的第 3 节有单独一段因为它太容易被忽略。4. 任务运行阶段排查套路调度、代理与内存4.1 定时任务不触发时区与 cron 表达式的双重坑OpenClaw 的定时任务配置在 MySQL 的task_schedule表里cron_expression字段默认按服务器时区解释。如果前端传的是“每天 08:00”北京时间而后端容器时区是 UTC任务会跑到 16:00 执行。这是让业务方最崩溃的问题任务没丢只是时区偏移。在采集器容器里先确认时区date如果输出是 UTC而业务要求的是 Asia/Shanghai就在 Compose 里给所有 service 加环境变量environment: - TZAsia/Shanghai - CRON_TZAsia/Shanghai但 Node.js 的cron-parser不是所有版本都认CRON_TZ需要显式设置process.env.TZ。另一个坑是 cron 字段位数OpenClaw 用的是 6 段 cron前缀带秒。比如0 0 8 * * *表示每天 8 点 0 分 0 秒。如果你从外部拷贝了 5 段 cron0 8 * * *解析器会静默忽略因为位数不对不报错。我吃过两次亏现在每次配置都会先执行node -e const crequire(cron-parser); console.log(c.parseExpression(process.env.CRON).next().toString())如果这段命令输出空说明表达式有问题先修表达式再谈时区。4.2 抓取请求反复超时连接池参数与代理池质量采集器默认用 axios连接池最大连接数是 10。当目标网站响应变慢时连接被长时间占用后续请求排队表现为任务一个个超时。这是连接池耗尽不是代理失效。我在采集器代码里调整了参数const HttpAgent require(agentkeepalive).HttpAgent; const HttpsAgent require(agentkeepalive).HttpsAgent; const client Axios.create({ timeout: 15000, httpAgent: new HttpAgent({ keepAlive: true, maxSockets: 20, maxFreeSockets: 5, timeout: 20000 }), httpsAgent: new HttpsAgent({ keepAlive: true, maxSockets: 20, maxFreeSockets: 5, timeout: 20000 }) });maxSockets: 20表示一个进程同时最多 20 个请求超过就排队。如果目标有反爬限制并发过高会封 IP所以这个值不是越大越好。我会做一个动态调整连续失败三次就把该站点并发降到 2超时改为 8 秒。代理池的坑集中在代理持续时间和质量。短效代理过期后采集器还拿着旧列表请求大量ECONNRESET。排查顺序先看指标面板里的proxy_fail_rate超过 20% 优先怀疑代理而不是代码。还有个细节代理验证要用目标 URL 的详情页而不是首页因为很多代理对特定路径的出口不同。4.3 内存泄漏与 Node 堆限制先定位再止血长时间跑的采集器最怕内存缓慢增长。OpenClaw 的 worker 默认没设堆上限Node 默认约 4GB。当内存接近上限时 GC 频繁CPU 飙高但任务不一定失败。定位泄漏不能只看top要用 inspectornode --inspect0.0.0.0:9229 src/worker.js然后用 Chrome DevTools 连接ip:9229做两次堆快照对比看哪些对象持续增长。常见泄漏点是 Redis 订阅回调里监听了未清理的事件或者 cache 对象没设容量上限。如果临时找不到根因先加堆上限止损node --max-old-space-size2048 src/worker.js再配合--exit让进程在内存耗尽时退出由 Docker 重启。这样能保证单次任务不被打爆但治标不治本。真正修复后要调回阈值否则频繁重启会导致任务中断。我一般会在 worker 的 process 对象上监听 heap 使用率超过 70% 就主动 dump 快照留作事后分析。5. 避坑清单四个最常见的 OpenClaw 故障记录5.1 现象数据库连接被拒 Connection refused现象部署完毕api 容器日志持续报Cant connect to MySQL server on mysql (111)容器无限重启。原因depends_on只保证启动顺序不保证 MySQL 可及。另一个原因是 MySQL 首次初始化耗时 30 秒以上而 api 容器在 5 秒内就开始连接。解决写一个等待脚本用mysqladmin ping轮询并设置超时上限同时确认 Compose 网络里数据库地址用服务名mysql而不是127.0.0.1。如果仍然失败检查 MySQL 是否挂载了初始化 SQL首次启动时会有延迟。5.2 现象日志文件不滚动磁盘被撑满现象运行一周后宿主机磁盘告警data/logs/worker.log达到 27GB。原因OpenClaw 默认的 winston 传输层没有配置maxsize容器日志驱动json-file只处理 stdout不影响内部文件。解决在 winston transport 里加入maxsize: 10485760和maxFiles: 5重启 worker。注意任何手动写的fs.writeFileSync都不会自动轮转要统一走 logger。调整后还要定期清理宿主机上的旧日志目录否则 Docker 卷不受文件系统 logrotate 管理。5.3 现象前端页面白屏控制台报 chunk not found现象发布新版本后部分路由白屏刷新几秒后恢复。原因Webpack 打包的 chunk 文件名带 hash旧的 html 引用了旧 chunk但 CDN 已删除旧文件。这是缓存策略问题不是代码 bug。解决nginx 对 html 设置Cache-Control: no-cache对带 hash 的 js/css 设置public, max-age31536000, immutable。这样浏览器每次重新拉 html内容变更时加载新 chunk。也可以把output.chunkFilename去掉 hash 做快速验证但生产环境不要这么干。5.4 现象定时任务重复执行多次现象同一任务在第二天凌晨被执行了两次抓取数据翻倍。原因部署了多个 worker 实例每个实例都注册了同一个 cron 表达式任务分发到所有实例时没有加锁导致重复执行。解决在任务执行前用 Redis 分布式锁SET NX EX 60只有成功拿到锁的实例才允许执行执行完或者过期后释放。锁的过期时间要大于任务最长执行时间否则任务还没跑完另一个实例就拿到锁造成并发覆盖。这个坑在水平扩展采集器后特别常见排查手册里把它单列为高优先级。6. 调试 OpenClaw 的进阶习惯日志采样、快照对比与千次跑测6.1 从 DEBUG 日志到现场不用重启就能开日志OpenClaw 内置 loglevel 机制默认 info。如果要查一次失败请求的上下文不要改代码重启。可以给运行中的进程发信号kill -USR1 worker_pidNode 会动态把日志级别切到 debug持续一段时间后自动回落。我习惯同时加上NODE_DEBUGhttp和NODE_DEBUGnet这样能看到底层 socket 的错误码比业务日志更接近真实现场。这个技巧在处理偶发超时特别有用不需要重启就能抓到现场。6.2 快照对比定位配置漂移排查配置被改的问题我会把所有配置文件纳入版本管理并在发布前生成快照。用docker compose config输出的哈希做基线如果发布后行为异常快速对比容器内的环境变量与快照差异docker inspect --format {{range .Config.Env}}{{println .}}{{end}} openclaw_api actual.env diff expected.env actual.env很多“神秘错误”其实是有人在服务器上手动 export 了变量或者改错了 .env 文件。这个对比习惯救了我很多次比查代码快得多。6.3 千次跑测用脚本验证修复稳定性排查手册最终要能快速验证一个问题是否修复。我会写一个简单脚本循环提交 1000 次任务请求统计成功率和响应分布同时抓取进程 CPU、内存for i in $(seq 1 1000); do code$(curl -s -o /dev/null -w %{http_code} -X POST http://localhost:8080/api/tasks -d {name:test}) echo $code $i done | awk {print $1} | sort | uniq -c这个脚本的意义不在于压测而在于复现偶发故障概率。跑完直接看非 200 的数量再对应时间戳和 worker 日志。如果修复后跑测结果稳定才敢把改动发布到生产。从那以后我每次改 OpenClaw 的配置都强制自己先做一轮千次跑测再收集日志、快照和指标三件套。很多看起来诡异的问题最后都落在参数边界和缓存策略上。希望这份排查手册能帮你少走几个弯路让抓取任务稳定跑得更久。本文还有配套的精品资源点击获取
返回列表