☰
OpenClaw + Docker 私有爬虫管理平台部署实战
2026/10/2 14:30:07 网站建设 项目流程

前阵子帮一个初创团队收拾爬虫摊子,发现他们十几台服务器上躺着上百个无人认领的 Python 脚本,全是crontab手动挂的,有的跑一半就死了,有的重复抓了三遍,还有的连数据落在哪儿都不知道。排查了半天之后,我给他们搭了一套基于 OpenClaw 的私有爬虫管理平台,用 Docker 一把梭部署完事,从此任务编排、去重、调度、结果回传都统一走平台,再也没出现"半夜三点某台机器脚本崩了但没人发现"的尴尬。

如果你也遇到类似情况——爬虫脚本散落、依赖环境各搞一套、定时任务全靠手写、根本没有任务面板和状态追踪,那这篇文章就是给你写的。我会把 OpenClaw 用 Docker 从零部署这套私有爬虫管理平台的完整过程拆开讲:包含环境准备、Compose 编排、跑通第一个爬虫任务、踩坑实录、以及进阶玩法。不整花活,全程实操视角。

1. 部署前先想明白:OpenClaw 解决的是什么问题

1.1 爬虫项目失控的典型症状

先对号入座。爬虫项目刚开始总是很爽:写一个脚本,requests.get()拿数据,pandas洗一洗,存个 CSV。但到第二个、第三个、第十个爬虫上线之后,痛点会集中爆发。

最典型的问题有几个。一是任务没有统一调度,crontab写得到处都是,改一次服务器就要重新理一遍;二是去重靠脚本里自己维护一个 set 或者查数据库,任务一多就乱,我见过一个团队因为忘记去重,写了 800 万条重复数据进去,把数据库直接搞死;三是没有失败重试和告警,爬虫半夜崩了,第二天早上业务方才发现数据没更新;四是任务状态不透明,老板问"现在有几个爬虫在跑、跑到哪一步了",没人能立刻答上来。

这些问题单独看都不大,但合在一起就是灾难。你需要的不是再写一个爬虫,而是一个能管所有爬虫的平台:能注册任务、排优先级、派发到 worker、自动重试、记录结果、提供 Web 控制台。

1.2 OpenClaw 的核心模块拆解

我理解的 OpenClaw,是一个面向私有部署的爬虫任务管理平台。它把爬虫这件事拆成了几层,每一层都有明确职责。

  • Web 控制台:用来创建任务、查看运行状态、浏览抓取结果。不需要每次操作都碰命令行。
  • 调度器:把cron表达式或手动触发的任务转换成可执行的消息,推送到队列。相当于整个平台的"大脑",只负责派单,不负责干活。
  • 消息队列:任务的等待区。OpenClaw 依赖 Redis 做队列存储,这样任务高峰时不会直接把数据库打死。
  • Worker 节点:真正执行爬虫脚本的部分。它可以和调度器在同一个容器里,也可以独立部署多副本。一个 worker 消费一个任务消息,拿到要抓的 URL 和解析规则,跑完把结构化结果回传给平台。
  • 持久化存储:任务元数据、抓取结果、去重记录,放在 PostgreSQL/MySQL 这类关系型数据库里。文件类的结果(图片、PDF)可以落到对象存储或本地卷。

所以你在部署的时候,并不是只跑一个 OpenClaw 容器就行,而是一整套依赖:应用本身 + Redis + 数据库。这也是为什么要用 Docker Compose 而不是docker run逐个启动。

1.3 为什么选 Docker 部署而不是裸机

很多人觉得裸机跑更简单,但私有部署的本质是"可复制、可迁移、可回滚"。我实际对比过十二次以上:裸机部署虽然省了镜像构建这一步,但后续升级、换机器、加 worker 节点、统一依赖版本,每一步都是坑。Docker 把运行时依赖(Node.js 版本、Python 环境、系统库、时区配置)全部打进镜像,换一台机器docker compose up -d就恢复,不用再对着文档装环境。

另外,爬虫平台往往要跑大量自定义脚本。你用裸机跑,脚本的依赖装在系统里,过一阵子系统升级可能就挂了;用 Docker,每个 worker 的依赖都是独立的,互相不污染。这也是我在这类场景里坚持容器化的核心理由。

1.4 先明确边界:不是让你抛弃商用方案

这里我先把丑话说在前面:OpenClaw 这类私有平台,和八爪鱼、后羿采集器、云采集 API 这类商用方案,定位完全不同。商用方案胜在开箱即用,零代码采集常见网站,但灵活性有限,数据要先落到对方平台,涉及敏感数据时很难过合规审计。OpenClaw 则强调可控和可编程:规则自己写,存储在自己服务器上,爬虫行为全掌握。

所以选择的逻辑很清楚:如果抓取规模大、规则多变、数据要入库做自家业务,或者对数据主权有要求,私有部署更合适。如果只是临时抓几个公开资讯页,商用工具就够了,没必要自己折腾一套平台。别为了技术上的爽感过度自建。

2. 环境准备:Windows 翻车现场与 Linux/ARM 设备的正确打开方式

2.1 Windows 本地跑 Docker:先过 WSL2 这一关

很多人在自己电脑上先试跑,Windows 10/11 上装 Docker Desktop 是必经之路。我遇到过的问题中,最高频的是这个:Docker Desktop 启动时报错 "virtualization support not detected",或者弹窗提示 WSL 存在问题。

这个报错的实际含义是,Docker Desktop 依赖的虚拟化层没起来。排查链路不复杂,按顺序来。

第一步,确认 Windows 的虚拟化功能有没有开启。打开 PowerShell(管理员权限),运行:

systeminfo | findstr /i "Hyper-V"

如果输出里显示Virtualization Enabled In Firmware: Yes,说明主板和系统层面的虚拟化没问题;如果显示 No,需要去 BIOS/UEFI 里把 Intel VT-x 或 AMD-V 打开。惠普、戴尔、联想的 BIOS 入口不一样,但一般都在Advanced或Security菜单下,开启后重启。

第二步,确认 WSL 功能正常。热词里提到的那句"请在 PowerShell 中运行 wsl -- status"其实是个笔误,真正的命令是wsl --status。运行之后看输出里 WSL 版本是不是 2,默认版本对不对。我见过一种情况:WSL 内核太旧,Docker Desktop 不认,解决办法是更新 WSL 内核:

wsl --update wsl --set-default-version 2

第三步,检查 Hyper-V 和"虚拟机平台"两个 Windows 功能是否启用。在控制面板的"启用或关闭 Windows 功能"里,找到 Hyper-V 和虚拟机平台(Windows Hypervisor Platform)勾上,重启。Docker Desktop 新版还要求开启"内核隔离"(如果系统支持的话),但多数情况下开启 Hyper-V 就够了。

这些做完,Docker Desktop 一般就能正常启动。如果还不行,打开任务管理器 -> 性能 -> CPU,看左下角虚拟化状态是否为"已启用"。如果这里显示已禁用,那问题基本锁死在 BIOS 层面,Windows 里怎么折腾都没用。

2.2 Linux 服务器部署:Ubuntu / CentOS 的关键差异

生产环境我强烈建议直接用 Linux 服务器,别在 Windows 上长时间挂机跑爬虫。一是资源占用更少,二是少一层 Docker Desktop 的虚拟化开销,三是重启自愈更容易配置。至于选 Ubuntu 还是 CentOS,个人经验是Ubuntu 优先。原因很简单:官方 Docker 仓库对 Ubuntu 的适配最激进,内核新,装 Docker 几乎不踩坑,遇到问题搜到的文档也最多。

Ubuntu 上安装 Docker 的完整链路:

# 1. 卸载可能残留的旧版本 sudo apt-get remove docker docker-engine docker.io containerd runc # 2. 安装依赖和 GPG 密钥 sudo apt-get update sudo apt-get install ca-certificates curl gnupg lsb-release sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg # 3. 添加官方软件源并安装 echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin # 4. 启动并设置开机自启 sudo systemctl start docker sudo systemctl enable docker

注意一点,Debian 系和 RPM 系的软件源格式完全不同,别把 Ubuntu 的命令直接搬到 CentOS 上跑。CentOS 如果非用不可,用yum install -y yum-utils,然后yum-config-manager --add-repo加 Docker 官方源,再yum install docker-ce。CentOS 7 上遇到过 Docker 依赖的iptables版本过旧导致 NAT 失效的问题,升级内核或调整防火墙策略都是常见解法,但这很折腾,所以我的结论还是优先 Ubuntu。

2.3 ARM 设备:RK3588 跑 OpenClaw 可行,但要注意镜像架构

为什么提 ARM 设备?因为我自己真的在一台 RK3588 的开发板上跑过这套平台,当低功耗采集节点用。很多人的需求是在 NAS、开发板、ARM 云主机上部署,成本和功耗都比 x86 云服务器低得多。

RK3588 属于 ARM64 架构,部署的核心注意点只有一个:镜像必须用 arm64 版本。好在主流镜像都支持多架构,拉取时会自动选择对应平台。如果你自己写 Dockerfile,注意基础镜像要选node:20-alpine这种带 arm64 的 tag,别用node:20里某些特殊平台版本。

在 ARM 设备上还要留意内存。RK3588 开发板常配 4GB 或 8GB 内存,OpenClaw 全家桶(应用 + Redis + PostgreSQL)跑起来大概占 1.5-2GB,再叠加爬虫 worker 的 Python 进程,8GB 板子比较从容,4GB 会紧张。建议在 Compose 里给每个服务设置mem_limit,防止某个服务吃满内存拖垮整个系统。我自己的做法是这样的:

services: openclaw-server: mem_limit: 768m openclaw-worker: mem_limit: 1g redis: mem_limit: 256m postgres: mem_limit: 512m

这个配置在 RK3588 上跑起来很稳,8GB 板子剩下的内存足够跑两三个轻量采集任务。

2.4 环境自检清单

部署前花三分钟做个自检,能省后面一大半排错时间。我每次给团队搭环境都会发这样一张表:

检查项命令预期结果
Docker 版本docker --versionv20.10 以上
Compose 插件docker compose versionv2.x 以上
当前架构uname -mx86_64 或 aarch64
可用内存free -h总计 2GB 以上
磁盘剩余df -h /剩余 10GB 以上
虚拟化状态systeminfo或 BIOS已启用
服务自启systemctl is-enabled dockerenabled

每条都过一遍,再开始部署。有经验的人都知道,环境问题占部署问题的一半以上,前期多花三分钟,后期少踩无数坑。

3. 用 Docker Compose 把 OpenClaw 全家桶跑起来:目录规划与容器编排

3.1 先想好目录结构,再动手写文件

部署这类多服务项目,最忌讳的是把文件乱扔。我推荐的目录结构是这样:

/opt/openclaw/ ├── .env # 环境变量,密钥集中存放 ├── docker-compose.yml # 容器编排入口 ├── data/ │ ├── postgres/ # 数据库数据卷挂载点 │ ├── redis/ # Redis 持久化目录 │ └── results/ # 爬虫结果落盘目录 ├── logs/ # 容器日志的宿主机映射 └── scripts/ ├── backup.sh # 数据库备份脚本 └── restore.sh # 恢复脚本

把数据文件、日志、脚本和编排文件分开,不只是为了整洁——数据目录独立挂载后,容器随便删、镜像随便更新,数据不丢。我见过有人图省事把数据直接写在容器里,结果docker rm一下,几个月采集的数据全没了,心态直接崩。

3.2 Compose 文件逐段解析

下面是核心的docker-compose.yml。这个文件覆盖了 OpenClaw 服务端、Worker、Redis、PostgreSQL 四个服务的完整编排。我加注释,方便你理解每个字段为什么存在。

version: "3.8" services: # OpenClaw 主服务:Web 控制台 + 调度器 openclaw-server: image: openclaw/server:latest container_name: openclaw-server restart: unless-stopped env_file: .env environment: - TZ=Asia/Shanghai - DATABASE_URL=postgresql://openclaw:${POSTGRES_PASSWORD}@postgres:5432/openclaw - REDIS_URL=redis://redis:6379/0 ports: - "8080:8080" depends_on: postgres: condition: service_healthy redis: condition: service_healthy healthcheck: test: ["CMD", "node", "-e", "fetch('http://localhost:8080/health').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"] interval: 30s timeout: 5s retries: 3 start_period: 20s volumes: - ./data/results:/app/data/results - ./logs:/app/logs networks: - openclaw-net mem_limit: 768m # Worker 节点:执行具体爬虫任务的容器 openclaw-worker: image: openclaw/worker:latest container_name: openclaw-worker restart: unless-stopped env_file: .env environment: - REDIS_URL=redis://redis:6379/1 - DATABASE_URL=postgresql://openclaw:${POSTGRES_PASSWORD}@postgres:5432/openclaw depends_on: - openclaw-server volumes: - ./data/results:/app/data/results - ./scripts:/app/scripts:ro networks: - openclaw-net mem_limit: 1g # 消息队列:Redis redis: image: redis:7-alpine container_name: openclaw-redis restart: unless-stopped command: ["redis-server", "--appendonly", "yes", "--requirepass", "${REDIS_PASSWORD}"] volumes: - ./data/redis:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 3s retries: 5 networks: - openclaw-net mem_limit: 256m # 元数据与结果存储:PostgreSQL postgres: image: postgres:16-alpine container_name: openclaw-postgres restart: unless-stopped environment: - POSTGRES_USER=openclaw - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_DB=openclaw - TZ=Asia/Shanghai volumes: - ./data/postgres:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U openclaw"] interval: 10s timeout: 3s retries: 5 networks: - openclaw-net mem_limit: 512m networks: openclaw-net: driver: bridge

几个字段的用意展开说。

restart: unless-stopped是我在所有服务上的默认值。它保证宿主机重启后容器能自动拉起,而且手动docker stop过的容器不会被强制拉起,很贴合运维直觉。

depends_on配合condition: service_healthy控制启动顺序。OpenClaw 服务端依赖数据库和 Redis 就绪再启动,避免应用启动后连不上数据库疯狂打日志。Redis 和 PostgreSQL 的healthcheck分别用redis-cli ping和pg_isready,都是官方推荐的方式,准确且开销极小。

env_file: .env让所有服务共享一套环境变量,密钥不会硬编码进 compose 文件。这是安全底线,后面细说。

volumes把容器内数据目录映射到宿主机。结果是./data/results,数据库是./data/postgres,Redis 是./data/redis。这样即使整个 Compose 项目删掉重建,数据依然还在。

mem_limit是给每容器设的内存上限。之前提过,尤其 ARM 板子内存紧张,这个必须有。宁可让某个容器触发 OOM 重启,也不能让它拖垮宿主机导致所有服务一起死。

3.3 .env 文件:密钥管理别犯低级错误

.env文件长这样:

# ===== OpenClaw 部署环境变量 ===== POSTGRES_PASSWORD=CHANGE_ME_STRONG_PASSWORD REDIS_PASSWORD=CHANGE_ME_ANOTHER_STRONG_PASSWORD OPENCLAW_SECRET_KEY=CHANGE_ME_SESSION_SECRET ADMIN_USERNAME=admin ADMIN_PASSWORD=CHANGE_ME_ADMIN_PASSWORD

三个关键原则我必须反复强调:

  1. 环境变量不要写死在 compose 文件里。明文密钥一旦进了 git 历史就很难彻底清除,你只能改密钥,不能改历史。正确做法是.env文件加入.gitignore,只在服务器上维护一份。
  2. 用强随机密钥。openssl rand -hex 32生成,别自己编一个"password123"。平台部署后要对外提供服务,弱密钥分分钟被爆破。
  3. 容器间通信走内网。compose 文件里服务之间通过服务名(postgres:5432、redis:6379)访问,不需要把数据库端口暴露到宿主机。我刻意没有在 Redis 和 PostgreSQL 服务上写ports,就是不让外部访问。真要远程管理数据库,用 SSH 隧道或别的安全途径,别偷懒裸开 5432 端口。

3.4 首次启动:从拉镜像到登录控制台

文件准备好之后,首次启动按这个顺序来:

cd /opt/openclaw docker compose config # 先校验配置,有语法错误会直接报出来 docker compose pull # 拉取所有镜像 docker compose up -d # 后台启动全部服务 docker compose ps # 查看状态,确认没有 restart 循环 docker compose logs -f openclaw-server # 跟踪主服务日志

等openclaw-server日志出现类似Server started on port 8080的输出后,浏览器打开http://服务器IP:8080,用.env里配置的ADMIN_USERNAME/ADMIN_PASSWORD登录。

第一次登录后建议立刻做两件事:改一遍管理员密码,然后在设置页里把控制台从 HTTP 切到 HTTPS(或者在前面挂一层 Nginx/Caddy 做 TLS 终结)。爬虫管理平台控制台里全是任务配置和数据结果,裸跑 HTTP 等于把训练数据送给走廊里的抓包者。

3.5 镜像 tag 策略:升级不翻车

Compose 文件里我写的是latesttag,方便演示,但实际长期跑不建议直接追 latest。镜像更新是不可控的,今天docker compose pull拉的新版本可能改了配置项格式,明天平台就起不来了。我的习惯是:

  • 第一次部署时固定一个明确版本 tag,例如openclaw/server:0.5.1,然后手动确认版本号。
  • 升级时改 tag,先docker compose pull,再docker compose up -d。如果起不来,改回旧 tag 重拉即可。
  • 升级前一定先把数据库备份做掉。数据真的比代码金贵。

另外,别忘了docker compose pull之后旧的悬空镜像会堆积,定期清理一下:

docker image prune -f

省磁盘的事不要等它自己发生。

4. 跑通第一个爬虫任务:任务定义、调度去重与结果回传

4.1 先分清控制台操作和 API 接入

OpenClaw 支持两种方式管理任务。一是直接在 Web 控制台里手动创建任务,适合临时抓取、调试规则;二是通过 API 接入,把任务注册集成到你的发布流程里,适合批量、程序化操作。

我实际使用时,日常调试走控制台,正式运行的任务全部走 API。原因很简单:可审计。API 接入意味着每个任务的创建人、创建时间、参数都有记录,出问题能回溯。

任务的核心数据结构大概长这样:

{ "task_id": "task_20240621_001", "name": "每日新闻头条采集", "url": "https://example-news.com/top", "schedule": "0 7 * * *", "priority": 5, "dedup_key": "news_top_20240621", "parser": "news_parser", "max_retries": 3, "timeout": 30, "callback": "http://openclaw-worker:8080/tasks/task_20240621_001/result" }

字段含义不复杂:schedule是 cron 表达式,dedup_key是去重键,parser指定用哪个解析器,max_retries控制失败重试次数。

4.2 调度和执行机制

任务进入平台后,调度器会做三件事。先把 cron 表达式注册进调度循环,到点了检查是否有未执行的任务;然后把任务消息推进 Redis 队列,队列命名按优先级分,比如queue:high、queue:normal;最后 worker 从队列消费消息,执行爬虫脚本。

这里要理解一个关键点:OpenClaw 的 worker 本身不内置爬虫逻辑,它执行的是你注册进去的自定义脚本。也就是说,平台管"什么时候跑、跑哪个任务、结果怎么存",你的 Python 脚本管"怎么抓、怎么解析"。

去重机制依赖dedup_key。同一个 dedup_key 在 Redis 里有 SETNX 的原子操作保证只会被消费一次。比如每天抓新闻头条,dedup_key 带上日期,当天的任务重复提交也不会重复执行。

4.3 一个实际的 Python 爬虫任务示例

下面是一个把公开新闻页面标题列表抓下来并回传结果的完整示例。代码本身用的是最常见的requests+BeautifulSoup,不涉及任何绕过访问控制的手段,只抓公开可访问的页面。

# crawler_jobs/news_parser.py import json import requests from bs4 import BeautifulSoup def parse_news(task_config): # 任务参数从 task_config 里取 url = task_config["url"] timeout = task_config.get("timeout", 30) headers = { "User-Agent": "Mozilla/5.0 (compatible; OpenClawBot/1.0; +https://example.com/bot)" } # 抓取公开页面 resp = requests.get(url, headers=headers, timeout=timeout) resp.raise_for_status() # 解析页面结构 soup = BeautifulSoup(resp.text, "html.parser") items = [] for li in soup.select("ul.news-list li"): a = li.find("a") if a and a.get("href"): items.append({ "title": a.get_text(strip=True), "link": a["href"] }) if len(items) >= 20: break # 把结构化结果回传给平台 return { "code": 0, "data": items, "source_url": url, "fetched_at": int(time.time()) }

Worker 的加载方式是把脚本放进./scripts挂载目录里,任务配置里的parser字段指定文件名和函数名,例如news_parser:parse_news。脚本只要实现"接收 task_config、返回可序列化结果"的约定,平台就自动完成剩下的流程——执行、超时控制、失败重试、结果入库。

我在真实项目里用这个套路跑了两年,踩过的最大坑是:Worker 容器里别装一堆爬虫专用库。以前试过在 worker 镜像里预装 50 多个 Python 包,结果升级一个包导致另一个脚本崩了。最佳实践是给高频任务准备两三个固定镜像(一个偏 requests 系,一个偏 Scrapy 系),按任务类型指定用哪个镜像跑,互不干扰。

4.4 合规意识必须放在第一位

这一步我想多说一点。爬虫管理平台越强大,你越要有数据合规意识。我在给团队做内部培训时反复强调几条红线:

  • 只抓你有权访问的数据。登录后才能看到的内容,用你自己的账号去抓,很可能违反网站服务条款。
  • 尊重robots.txt。虽然技术上它拦不住你,但遵守它是行业的专业底线。OpenClaw 的 worker 配置里可以加一个 robots 检查的中间件,我认为自带判断比事后辩解强。
  • 抓取频率要像人。就算目标站点没反爬,你每秒请求 100 次也是不礼貌的。平台里给任务加个固定间隔参数,例如request_interval: 1(秒),压力小一半,封禁风险低一半。
  • 个人信息和大规模数据抓取,务必先做法律评估。这不是吓唬人,是真实发生过很多案例了。

一句话总结:能力越大,越要克制。平台解决了"能不能跑"的问题,但"该不该跑"永远是人决定的。

5. 让服务稳定过夜:健康检查、日志、备份与安全加固

5.1 健康检查与自动重启

部署完只是开始,真正的考验是让它稳定跑一个月不崩。Compose 文件里的healthcheck和restart: unless-stopped组合是第一步,但还不够。我建议在宿主机再加一层兜底:写一个看门狗脚本,定期检查容器健康状态,发现异常就执行docker compose restart。

看门狗脚本思路很简单:

#!/usr/bin/env bash # scripts/healthcheck.sh unhealthy=$(docker inspect --format='{{.State.Health.Status}}' openclaw-server 2>/dev/null) if [ "$unhealthy" != "healthy" ]; then echo "$(date): openclaw-server unhealthy, restarting..." >> /var/log/openclaw-watchdog.log cd /opt/openclaw && docker compose restart openclaw-server fi

配合 cron 每两分钟执行一次。这比单纯靠 Docker 自带的 restart 策略更主动——restart 策略是容器退出才生效,但健康检查能发现"容器还活着但服务已不可用"的假死状态。

5.2 日志:别等出了事故再看

日志管理被九成小团队忽略。默认情况下,Docker 的 JSON File 日志驱动会把日志写到宿主机,但不做轮转的话,单个容器日志能涨到几个 GB。必须在 Compose 里显式配置日志轮转:

logging: driver: "json-file" options: max-size: "50m" max-file: "5"

这样每个容器日志最多占 250MB,超过自动滚动。再加上第 3 章里挂载的./logs目录输出业务日志,排查问题时grep 关键字 /opt/openclaw/logs/就能定位,不用进容器翻。

关于日志,我的经验是:业务日志要结构化和带 trace_id。每次任务执行都输出一行 JSON 日志,包含 task_id、状态、耗时、错误码。这样从控制台看到"某任务失败",就能直接在日志里grep task_xxx看到完整链路。纯文本日志在任务量上来之后,你会后悔的。

5.3 数据备份:数据库和文件卷一个都不能漏

爬虫管理平台里,最值钱的是任务配置和抓取结果。备份方案我用的是三层:

  1. PostgreSQL 定期逻辑备份:每天凌晨pg_dump,保存最近 7 天。
  2. Redis AOF 持久化:RDB 加 AOF 双保险,防止内存数据丢失。
  3. 结果文件卷快照:data/results目录每日增量同步到异地存储。

最简单的备份脚本长这样:

#!/usr/bin/env bash # scripts/backup.sh BACKUP_DIR=/opt/openclaw/backups/$(date +%Y%m%d) mkdir -p "$BACKUP_DIR" docker exec openclaw-postgres pg_dump -U openclaw openclaw | gzip > "$BACKUP_DIR/openclaw.sql.gz" docker exec openclaw-redis redis-cli -a "$REDIS_PASSWORD" BGSAVE tar czf "$BACKUP_DIR/results.tar.gz" -C /opt/openclaw/data results find /opt/openclaw/backups -type d -mtime +7 -exec rm -rf {} \;

然后用 cron 每天早上 3 点跑一次。这个备份策略成本很低,但能让你在灾难发生时只丢最多一天的数据。不要觉得"平台才跑了两周不用备份",数据做好准备是从第一天就要开始的。

5.4 安全加固:开放端口前先做这几件事

任何面向网络的平台,安全加固都要在开放端口前做完。我的最低安全清单:

  • 控制台必须走 HTTPS。最简单的方式是前面挂 Caddy,自动申请证书,Caddyfile 三行配置搞定,比手工维护 Nginx 配置省心不是一点半点。
  • 容器内禁止 root 运行。前后端进程在 Dockerfile 里通过USER node或单独创建低权限用户执行。PostgreSQL 和 Redis 官方镜像本身就做了非 root 降权,OpenClaw 自定义镜像要自己注意。
  • 依赖漏洞扫描。定期跑docker scout cves openclaw/server:latest看镜像有没有高危 CVE。爬虫平台要处理大量不可信网页内容,解析库的漏洞可能被恶意页面利用,不能忽视。
  • 防火墙只放行必要端口。宿主机上用 ufw 或 firewalld,只开 80(HTTPS 走 Caddy)、22(SSH),其他一律拒绝。数据库和 Redis 端口绝不对公网开放。
  • 不要用默认管理员密码。这个我说了两遍,因为它太常见了,见过太多人部署完不改密码。

这些做完,一般的小团队平台就足够安全了。再往上要接 LDAP、双因素、审计日志,那属于企业版玩法,后面再说。

6. 我踩过的坑:网络不通、OOM 和 Docker 启动失败的完整排查链路

6.1 Docker Desktop 启动失败:从报错到解决的全过程

这个坑我帮人排了很多次。具体场景:新买的 Windows 笔记本,装好 Docker Desktop,启动后弹窗报错 "Docker Desktop failed to start because virtualization support wasn't detected",一晃而过根本来不及截图。

第一步别慌,先在 PowerShell 里跑:

wsl --status

正常输出会显示 "默认版本:2"。如果提示"正在安装",先跑一遍wsl --update和wsl --shutdown。如果提示找不到 WSL,则需要安装 WSL 功能:

wsl --install

装完重启,再启动 Docker Desktop。

如果 WSL 正常但 Docker 仍报错,检查 Windows 功能。PowerShell(管理员)跑:

Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-All Get-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform

两个状态如果不是 Enabled,用下面的命令开启:

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-All -All Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform -All

然后重启。这一套在我接触过的绝大多数机器上都解决了。

最后还有一个容易忽略的点:老旧电脑的 BIOS 虚拟化可能被厂商标记成禁用。开机进 BIOS,找到 "Intel Virtualization Technology" 或 "SVM Mode"(AMD 平台),确认是 Enabled。这一步在系统里无法修改,必须进固件设置。

6.2 容器网络不通:一次 Node 进程连不上 Redis 的排障

容器都起来了,但 OpenClaw 服务端日志疯狂报Redis connection refused。这时按顺序查:

第一步,确认容器状态和网络。执行:

docker compose ps docker network ls

第二步,手动测试容器间连通性。进入 server 容器,ping Redis 容器的 IP 或服务名:

docker exec -it openclaw-server bash ping redis

如果 ping 不通,检查两个容器是否在同一网络。Compose 默认会为项目创建独立网络,但如果你手动docker run过其他容器,可能不在同一网络里。解决办法是自定义 network,显式把服务加进去,就像我在 Compose 文件里写的networks: openclaw-net。

另一种情况是宿主机防火墙拦截。Linux 上 Docker 使用 iptables 做 NAT,如果iptables -F清过规则,容器网络会神秘失效。判断方法很简单:宿主机上iptables -L -n | grep DOCKER,看有没有 DOCKER 链。没有的话,重启 Docker 服务通常能重建规则。

systemctl restart docker

注意:重启 Docker 会重启所有容器,尽量选业务低峰期操作。

6.3 OOM:内存一满,容器全躺

跑采集任务时最怕内存打满。现象是某个容器突然消失,docker ps -a里状态显示Exited (137)。137 退出码的含义就是被 OOM Killer 杀掉了。

排查方式有条不紊:

# 查看容器被杀时的系统日志 journalctl -k | grep -i oom # 查看容器内存使用趋势 docker stats --no-stream # 查看 Compose 里 mem_limit 配置是否合理

解决办法分两层。首先,给任务并发数设置上限。OpenClaw worker 的配置里通常有max_concurrent_tasks,设为 2 或 3,别默认全开。其次,宿主机加一点 swap,它的作用是缓冲突发内存压力,避免 OOM Killer 立刻动手。Ubuntu 上临时加 4GB swap:

sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile # 永久生效需在 /etc/fstab 添加一行

我的最终建议是:爬虫项目,内存配置宁多勿少。Python 解析大 HTML 页面时内存峰值很高,而容器 OOM 重启之后任务重跑又是一轮消耗,得不偿失。

6.4 时区与中文数据编码

这个坑很小但极其常见。容器默认时区是 UTC,如果你在任务里打时间戳、按天做 dedup_key,结果全是 UTC 时间,和业务时间对不上。解决方式是 Compose 里统一设置TZ=Asia/Shanghai,我在第 3 章的配置里已经加上了。

另外一个坑是中文乱码。worker 容器如果没装中文字体或者系统 locale 不对,页面抓下来中文文本进入 PostgreSQL 可能变成????。排查方法:进入容器执行locale,看有没有 UTF-8。最好在基础镜像里提前配置好:

ENV LANG=C.UTF-8 ENV LC_ALL=C.UTF-8

数据库连接串里也显式加?charset=utf8mb4(MySQL)或确认 PostgreSQL 数据库编码是 UTF8。中文数据出问题基本逃不出这两处。

7. 从单机到多节点:接入 Teams / Obsidian / 本地大模型的进阶玩法

7.1 横向扩容:一个 Compose 项目跑多个 Worker

单机跑 OpenClaw 能应对小规模采集需求,但任务量上来后,一个 worker 容器消费消息的速度会成为瓶颈。横向扩容的姿势很简单:在 Compose 里把 worker 服务用deploy.replicas指定多副本。

services: openclaw-worker: image: openclaw/worker:latest deploy: replicas: 3

docker compose up -d就会拉起三个 worker 容器,它们共享同一个 Redis 队列,互不抢任务,天然实现负载均衡。这是 OpenClaw 这类队列架构最大的优势:加节点就能线性提升吞吐,不需要改业务代码。

多节点部署时注意:结果写库没有冲突,因为 PostgreSQL 支持并发写;但结果文件落到本地卷会有并发问题。更好的做法是像前面说的,把文件结果映射到独立的共享存储,或者干脆只存元数据在平台里,文件放到外部对象存储。

7.2 接入 Microsoft Teams 当告警通道

爬虫平台不能只靠人工盯控制台。我的做法是把告警接入 Microsoft Teams,任务失败、调度器异常、worker 掉线,各发一条卡片消息到团队群。

Teams 接入的核心是 Webhook URL。在 Teams 频道里添加 "Incoming Webhook" 连接器,拿到一条https://your-org.webhook.office.com/webhookb2/...的 URL。然后在 OpenClaw 的告警配置里加上这个地址,平台触发告警时 POST 一个 JSON 消息过来。

我在脚本里验证过的最简卡片消息:

{ "@type": "MessageCard", "@context": "http://schema.org/extensions", "summary": "OpenClaw 任务失败告警", "themeColor": "FF0000", "sections": [ { "activityTitle": "任务执行失败", "facts": [ { "name": "任务ID", "value": "task_20240621_001" }, { "name": "失败原因", "value": "Timeout after 30s" }, { "name": "重试次数", "value": "3/3" } ] } ] }

接入之后,团队的响应时间从"第二天早上发现"缩短到"几分钟内处理",这是非常值得的一步。

7.3 在 Obsidian 里查任务结果

这是个小而实用的玩法:OpenClaw 社区有不少人写了 Obsidian 插件,直接在笔记里查平台上的任务状态和结果摘要。我自己的用法是,每天早上起来打开 Obsidian,跑一条命令就能看到"昨晚 7 个任务全部成功,3 个任务抓了 5214 条数据"。

原理就是调 OpenClaw 的 API。插件本质上是个 API 客户端,配置好服务器地址和 token 就能用。这个玩法适合重度 Obsidian 用户,把监控嵌入日常笔记流,比打开单独的控制台更符合习惯。

开放 API 的好处就在这里:不只能和 Teams、Obsidian 对接,理论上也能接企业内部的工作流系统。

7.4 本地大模型辅助解析:Qwen 和 DeepSeek 的正确用法

最近一年,本地大模型和爬虫平台结合是热度很高的方向。热搜词里大量的 DeepSeek 本地部署、Qwen2.5-3B 关联到 OpenClaw,也印证了这一点。这个方向的实际价值在于:让大模型帮我们做页面结构理解和解析规则生成,而不是人工写死 CSS 选择器。

页面改版是爬虫维护最大的痛点。以前是页面一改版,你的解析规则全废,得人工重新看 HTML 写选择器。现在可以让本地大模型干这件事——把页面 HTML 片段和需要提取的字段给模型,让它输出对应的解析规则或者直接输出结构化 JSON。

具体架构是:

爬虫抓取 HTML -> 本地大模型服务(Ollama 跑 Qwen2.5-3B 等) -> 结构化 JSON -> 回传 OpenClaw

我测试过的链路很稳定。Ollama 部署非常简单,一条命令拉起服务,OpenClaw 的 worker 里只需要加一个请求,把 HTML 和字段描述发给localhost:11434的接口。选模型的时候我的建议是:优先 3B-7B 参数量的模型,比如 Qwen2.5-3B。生成解析规则这种任务接近 pattern 提取,不需要 70B 那种大体量,小模型的响应速度在几十毫秒到百毫秒量级,够用且省资源。

这一步把"页面改版导致长期维护工作量"的问题彻底降下来了。我自己项目的实际体感是:以前每次目标站点改版,人工维护解析规则要两个小时,现在交给模型,十分钟内搞定,而且准确率高得多。

需要留意的是,本地大模型对服务器内存和显卡有要求。没有 GPU 的机器,用 CPU 跑 3B 模型也能勉强运行,但延迟会高一些。如果服务器配置紧张,更合理的方案是把解析请求转发到一个独立的推理节点,不要和 OpenClaw 主服务抢资源。

我在实际项目中把 OpenClaw 和 Ollama 分开部署,Ollama 专门跑在一台带 GPU 的机器上,OpenClaw 集群通过内网地址调用它。这样两边互不干扰,升级、重启也互不影响。

最后说几句实在话

这套 OpenClaw Docker 部署方案,目前已经在我手上跑过好几类环境:两核 4G 的小云主机、RK3588 开发板、还有十几台节点的采集集群,都没有出过结构性问题。每次帮团队搭完,我都会留一句话:部署只是开始,运维和规则维护才是长期工作。

你想真正用好爬虫管理平台,最值得投入的不是部署脚本,而是把任务分类、去重策略、失败告警、备份恢复这四条基础能力用起来。先把一两个任务跑稳,再逐步扩大,比一次性把平台搭得花里胡哨但没人用要实在得多。

我这篇里的 Compose 文件和配置,你直接拿走用,环境变量和密码记得换掉。有不清楚的命令,照着敲一遍基本都能跑通。真要遇到怪问题,记住我第 6 章的排查思路:先看日志,先看健康状态,先确认环境,再去动配置。爬虫平台说到底也是个 Web 应用,没有什么神秘问题,只是链路长了一点,排查要有耐心。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询