Skip to content

refactor: 全量命名卷替代 /home/node 分散挂载 #23

@hrygo

Description

@hrygo

背景

当前 docker-compose.yml/home/node 目录采用分散挂载策略:

  • ~/.global → named vol openclaw-global
  • ~/.local → named vol openclaw-python-local
  • ~/.openclaw → bind mount ~/.openclaw-in-docker
  • ~/.claude → bind mount ~/.claude
  • ~/.notebooklm → bind mount ~/.notebooklm
  • ~/.agents/skills → bind mount ~/.agents/skills (ro)
  • ~/.cache/ms-playwright → named vol openclaw-playwright-cache
  • ~/.go/pkg/mod → named vol openclaw-go-mod
  • /app/node_modules → named vol openclaw-node-modules

共 9 行挂载配置。

目标

用单个命名卷 openclaw-devkit-home:/home/node 替代上述所有分散挂载,同时通过只读 bind mount 保留从宿主机编辑配置的能力。

优势

  1. 数据完整性 — 镜像构建时写入的所有内容(工具链、CLI、配置)随容器重启全部保留,不从镜像层重新初始化
  2. entrypoint 简化 — 无需针对 bind mount / named vol / root 运行做复杂的权限修复逻辑
  3. Go 生态覆盖完整go/pkg/modgo/bingo/build~/.cache/go-build 全部保留
  4. 运维极简 — 一个卷的备份/清理/迁移
  5. 配置隔离 — 天然与宿主机隔离,不污染原生 ~/.openclaw

新卷挂载方案

volumes:
  openclaw-devkit-home:
    name: openclaw-devkit-home

services:
  openclaw-gateway:
    volumes:
      # ─────────────────────────────────────────────────────────────
      # 1. 全量命名卷(核心)
      #    替代所有分散挂载,包含:.global, .local, .opencode, .openclaw
      #    .claude, .notebooklm, .agents, .cache, go/, /app
      # ─────────────────────────────────────────────────────────────
      - openclaw-devkit-home:/home/node:rw

      # ─────────────────────────────────────────────────────────────
      # 2. 只读挂载:CLI 工具配置文件(容器内可读,宿主机可编辑)
      #    注意:必须只读,避免宿主机编辑器锁文件或权限问题
      # ─────────────────────────────────────────────────────────────
      - ${HOME:-${USERPROFILE}}/.claude:/home/node/.claude:ro
      - ${HOME:-${USERPROFILE}}/.openclaw-in-docker:/home/node/.openclaw:ro
      - ${HOME:-${USERPROFILE}}/.notebooklm:/home/node/.notebooklm:ro
      - ${HOME:-${USERPROFILE}}/.agents/skills:/home/node/.agents/skills:ro

      # ─────────────────────────────────────────────────────────────
      # 3. 读写挂载:初始化脚本和动态工作区
      # ─────────────────────────────────────────────────────────────
      - ./docker-entrypoint.sh:/usr/local/bin/docker-entrypoint.sh:ro
      - ${OPENCLAW_WORKSPACE_DIR}:/home/node/.openclaw/workspace:rw

只读挂载说明

宿主机路径 容器挂载点 模式 用途
~/.claude /home/node/.claude ro Claude Code 配置文件、claude.json、skills、memory
~/.openclaw-in-docker /home/node/.openclaw ro OpenClaw Gateway 配置、会话状态
~/.notebooklm /home/node/.notebooklm ro NotebookLM 认证信息
~/.agents/skills /home/node/.agents/skills ro 共享 Skills 目录

只读理由

  • 避免宿主机编辑器(如 VSCode)在打开文件时创建 .swp 交换文件,导致容器内进程权限问题
  • 避免宿主机用户的 uid/gid 与容器内 node 用户 (uid 1000) 不一致,引发权限冲突
  • 配置修改统一在宿主机端进行,容器内对配置的写入操作(如 openclaw config set)通过 docker exec 或 Gateway UI 完成

写入方式

# 宿主机端直接编辑(只读挂载不会阻止宿主机写入)
vim ~/.claude/claude.json
vim ~/.openclaw-in-docker/openclaw.json

# 容器内命令修改(通过 exec 临时以 node 用户运行)
docker exec -it openclaw-gateway openclaw config set ...
docker exec -it openclaw-gateway claude config set ...

workspace 路径重叠检测

问题:如果 OPENCLAW_WORKSPACE_DIR 设置在 ~/.openclaw-in-docker 内部,bind mount 会遮蔽命名卷中同名路径,导致数据丢失或行为不可预期。

检测机制:已在 docker-entrypoint.sh 增加 _validate_workspace_path() 验证函数(entrypoint 第 0 步执行):

# 检测场景:
# 1. workspace 路径 == ~/.openclaw-in-docker        → ERROR
# 2. workspace 路径是 ~/.openclaw-in-docker 的子目录 → ERROR

# 正确示例:
OPENCLAW_WORKSPACE_DIR=~/projects           # 在 ~/.openclaw-in-docker 外部 ✓
OPENCLAW_WORKSPACE_DIR=~/openclaw-workspace # 在 ~/.openclaw-in-docker 外部 ✓

# 错误示例(会被检测并拒绝启动):
OPENCLAW_WORKSPACE_DIR=~/.openclaw-in-docker/workspace  #
OPENCLAW_WORKSPACE_DIR=~/.openclaw-in-docker           #

待办事项

  • 修改 docker-compose.yml
    • 删除 volumes: 中的 6 个冗余命名卷(openclaw-node-modulesopenclaw-go-modopenclaw-playwright-cacheopenclaw-playwright-binopenclaw-globalopenclaw-python-local
    • 新增 openclaw-devkit-home 命名卷
    • 替换 volumes: 为新方案(命名卷 + 只读挂载 + 读写挂载)
  • 简化 docker-entrypoint.sh 权限修复逻辑:
    • 移除针对不同挂载类型的差异化处理(named vol / bind mount)
    • 移除 TARGET_DIRS 数组遍历逻辑
    • 保留 workspace 路径重叠检测
    • 保留 .openclaw-seed 复制逻辑
    • 保留手术修复(surgery)逻辑
  • 处理空卷初始化:entrypoint 检测到 named volume 为空时,从镜像层复制默认文件
  • 验证工具链可用性:npm / pnpm / bun / uv / opencode / claude / openclaw 全部正常
  • 验证 Playwright 浏览器缓存保留
  • 验证 Go 模块缓存和构建缓存保留
  • 验证 .openclaw 配置持久化(只读挂载模式下宿主机编辑生效)
  • 验证 .claude 会话状态持久化(只读挂载模式下宿主机编辑生效)
  • 验证 .notebooklm 只读挂载正常工作
  • 更新 CLAUDE.md 中的 Makefile 命令注释(make clean / make clean-volumes 说明)
  • 更新 .env.example 或文档说明 OPENCLAW_WORKSPACE_DIR 路径约束

风险

  • 命名卷内数据丢失风险 → 首次切换前需备份旧卷数据
  • 首次启动需初始化逻辑(已有 .openclaw-seed 模式可复用)

参考

  • 当前权限修复逻辑:docker-entrypoint.sh:56-111(重构前)
  • workspace 重叠检测:docker-entrypoint.sh 新增 _validate_workspace_path()

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions