写在前面
今天下午,我想给 OpenClaw 安装一个微信公众号发布技能 wechat-article-publisher,把文章一键发布到公众号草稿箱。
技能安装很顺利,但在飞书执行“发布到公众号草稿箱”时,遇到了一系列问题:
ModuleNotFoundError: No module named 'requests'(依赖缺失)Read-only file system(只读权限)Permission denied(权限不足)No usable temporary directory(无临时目录)- 最后还有封面图片写入失败的问题
从下午 1 点折腾到晚上 6 点,整整 5 个小时。写下这篇教程,记录踩过的每一个坑和最终解决方案。
一、问题根源
| 问题 | 原因 |
|---|---|
| 依赖缺失 | 容器没有 pip,也没装 Python 依赖 |
| 只读错误 | read_only: true 导致无法写入 |
| 权限错误 | 非 root 用户无安装权限 |
| apt-get 慢 | 用了国外源 |
| 依赖重启丢失 | 手动装在了容器内,不是镜像里 |
| 临时目录不可写 | /tmp 只读 |
| 封面图片写入失败 | 技能脚本试图写入只读的 skills/assets 目录 |
核心教训:不要在运行时手动装依赖,要把所有依赖打包进 Docker 镜像。
二、最终解决方案
2.1 Dockerfile(依赖打包进镜像)
FROM ghcr.nju.edu.cn/openclaw/openclaw:latest
USER root
# 强制阿里云源(加速 apt-get)
RUN rm -rf /etc/apt/sources.list.d/* 2>/dev/null || true && \
echo "deb http://mirrors.aliyun.com/debian bookworm main" > /etc/apt/sources.list && \
echo "deb http://mirrors.aliyun.com/debian bookworm-updates main" >> /etc/apt/sources.list && \
echo "deb http://mirrors.aliyun.com/debian-security bookworm-security main" >> /etc/apt/sources.list
# 安装 pip
RUN apt-get update && apt-get install -y python3-pip cron && rm -rf /var/lib/apt/lists/*
# 安装 wechat-article-publisher 技能所需的依赖
RUN python3 -m pip install --break-system-packages \
-i https://pypi.tuna.tsinghua.edu.cn/simple \
requests beautifulsoup4 markdown pyyaml Pillow
USER node
2.2 docker-compose.yml(关键配置)
services:
openclaw:
build: .
container_name: openclaw-gateway
restart: unless-stopped
# 安全配置(全部开启)
cap_drop:
- ALL
cap_add:
- DAC_OVERRIDE
read_only: true
tmpfs:
- /tmp # 让 /tmp 可写(解决临时文件问题)
user: "1000:1000"
security_opt:
- no-new-privileges:true
ports:
- "127.0.0.1:37406:18789"
volumes:
- ./config:/home/node/.openclaw:rw
- ./workspace:/home/node/.openclaw/workspace:rw
- ./logs:/home/node/logs:rw
- ./data:/home/node/data:rw
- ./credentials:/home/node/.openclaw/credentials:ro
- ./skills:/home/node/.openclaw/skills:ro # 技能目录保持只读
- ./extensions:/home/node/.openclaw/extensions:ro
- ./agents:/home/node/.openclaw/agents:rw
- ./tmp/openclaw-1000:/tmp/openclaw-1000:rw
- ./npm:/home/node/.npm:rw
environment:
- NODE_ENV=production
- LOG_LEVEL=info
- OPENCLAW_GATEWAY_TOKEN=${OPENCLAW_TOKEN}
# ... 其他环境变量
2.3 修改技能脚本(解决封面图片写入问题)
技能脚本试图写入只读的 skills/assets 目录,需要改为写入 /tmp。
# 修改脚本中的临时目录
sed -i 's|Path(__file__).resolve().parent.parent / "assets" / "generated_cover.jpg"|Path("/tmp") / "generated_cover.jpg"|g' \
skills/wechat-article-publisher-1/scripts/publish_wechat.py
三、构建与验证
3.1 首次构建
cd /opt/openclaw
docker compose build --no-cache
docker compose up -d
3.2 验证依赖
docker exec -it openclaw-gateway python3 -c "import requests, bs4, markdown, yaml, PIL; print('✅ 依赖齐全')"
3.3 配置公众号凭证
在飞书里发送:
这是我的公众号配置,请帮我写入到公众号发布技能中
AppID:你的AppID
AppSecret:你的AppSecret
3.4 测试发布
在飞书里发送:
发布到公众号草稿箱
或先生成一篇文章再发布:
帮我写一篇关于AI的公众号文章,发布到公众号草稿箱
四、以后的操作
日常升级
cd /opt/openclaw
docker compose down
docker compose build --no-cache
docker compose up -d
日常重启
docker compose restart
验证依赖是否正常
docker exec -it openclaw-gateway python3 -c "import requests; print('✅ OK')"
五、踩坑清单与解决方案汇总
| 序号 | 错误现象 | 原因 | 解决方案 |
|---|---|---|---|
| 1 | pip3: command not found | 容器没装 pip | Dockerfile 中安装 python3-pip |
| 2 | Read-only file system | read_only: true | 依赖打包进镜像,不在运行时安装 |
| 3 | Permission denied | 非 root 用户 | 构建时用 root,运行时切回 node |
| 4 | apt-get update 极慢 | 国外源 | 强制换阿里云源 |
| 5 | 依赖重启就丢 | 手动装在容器内 | 依赖打包进镜像 |
| 6 | ModuleNotFoundError | PYTHONPATH 不对 | 依赖装到系统默认路径 |
| 7 | No usable temporary directory | /tmp 只读 | 添加 tmpfs: - /tmp |
| 8 | 封面图片写入失败 | 脚本写入只读的 skills/assets | 修改脚本,改为写入 /tmp |
六、快速排障清单
如果发布失败,按顺序检查:
# 1. 检查依赖
docker exec -it openclaw-gateway python3 -c "import requests; print('OK')"
# 2. 检查技能目录
docker exec -it openclaw-gateway ls -la /home/node/.openclaw/skills/ | grep wechat
# 3. 检查配置
docker exec -it openclaw-gateway cat /home/node/.openclaw/skills/wechat-article-publisher-1/config.json
# 4. 查看错误日志
docker compose logs --tail 50 | grep -i error
# 5. 检查 /tmp 是否可写
docker exec -it openclaw-gateway touch /tmp/test.txt && echo "可写"
七、一句话总结
把依赖打包进 Docker 镜像,装到系统默认路径,用 tmpfs 让 /tmp 可写,修改技能脚本使用 /tmp 作为临时目录,换好国内源——一次构建,永久省心。
八、对比:错误方案 vs 正确方案
| 对比项 | 错误方案 | 正确方案 |
|---|---|---|
| 依赖位置 | 运行时手动装 | 打包进镜像 |
| 重启后 | ❌ 丢失 | ✅ 还在 |
| 重建后 | ❌ 丢失 | ✅ 还在 |
| 迁移服务器 | ❌ 需重新装 | ✅ 直接拉镜像 |
| 手动操作 | ❌ 每次都要 | ✅ 一次搞定 |
| 临时目录 | ❌ 只读报错 | ✅ tmpfs 可写 |
| 封面图片 | ❌ 写入失败 | ✅ 写入 /tmp |
记录我踩过的坑,希望通过这篇教程能帮你省下 5 个小时。🦞