| 1 | # Docker |
| 2 | |
| 3 | > 英文原文:[DOCKER.md](../DOCKER.md)。 |
| 4 | > 最后与英文同步日期(last synced with English revision):2026-09-26。 |
| 5 | |
| 6 | Codewhale 每次发布都会把一个多架构的 Linux 镜像推到 GitHub Container |
| 7 | Registry。 |
| 8 | |
| 9 | ```bash |
| 10 | docker pull ghcr.io/codewhale-hq/codewhale:latest |
| 11 | ``` |
| 12 | |
| 13 | ## 快速开始 |
| 14 | |
| 15 | 用 Docker 管理的数据卷运行已发布的镜像: |
| 16 | |
| 17 | ```bash |
| 18 | docker volume create codewhale-home |
| 19 | |
| 20 | docker run --rm -it \ |
| 21 | -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \ |
| 22 | -v codewhale-home:/home/codewhale/.codewhale \ |
| 23 | -v "$PWD:/workspace" \ |
| 24 | -w /workspace \ |
| 25 | ghcr.io/codewhale-hq/codewhale:latest |
| 26 | ``` |
| 27 | |
| 28 | 想获得可复现的安装,请用固定的发布标签: |
| 29 | |
| 30 | ```bash |
| 31 | docker run --rm -it \ |
| 32 | -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \ |
| 33 | -v codewhale-home:/home/codewhale/.codewhale \ |
| 34 | -v "$PWD:/workspace" \ |
| 35 | -w /workspace \ |
| 36 | ghcr.io/codewhale-hq/codewhale:vX.Y.Z |
| 37 | ``` |
| 38 | |
| 39 | 把 `vX.Y.Z` 换成 |
| 40 | [GitHub Releases](https://github.com/codewhale-hq/CodeWhale/releases) 里的标签。 |
| 41 | |
| 42 | ## 默认镜像约定 |
| 43 | |
| 44 | `ghcr.io/codewhale-hq/codewhale:latest` 和各个 semver 标签都是保守的运行时镜像: |
| 45 | |
| 46 | - 容器以非 root 的 `codewhale` 用户运行,UID/GID 为 `1000:1000` |
| 47 | - 镜像不授予免密 `sudo` |
| 48 | - 镜像的用途是让 Codewhale 跑在挂载进来的工作区上,而不是在运行时改动基础操作系统 |
| 49 | - 用户状态应放在挂载到 `/home/codewhale/.codewhale` 的数据卷里 |
| 50 | |
| 51 | 这个默认值是有意为之。想保持最小信任边界,就继续用它。如果某个项目需要 |
| 52 | `apt-get`、编译工具链、Node/Python 包管理器、自定义 CA 证书,或者 Docker 里 |
| 53 | 其他类似主机的环境,请另外构建一个显式的工具箱镜像,不要改动默认镜像的约定。 |
| 54 | |
| 55 | ## 可选:工具箱/自定义镜像 |
| 56 | |
| 57 | 仓库里有一个示例 |
| 58 | [`docs/examples/Dockerfile.toolbox`](../examples/Dockerfile.toolbox),它在官方 |
| 59 | 镜像上加了免密 `sudo` 和常用开发软件包。想要可复现的项目环境时,就用固定的 |
| 60 | Codewhale 标签构建它: |
| 61 | |
| 62 | ```bash |
| 63 | docker build -f docs/examples/Dockerfile.toolbox \ |
| 64 | --build-arg CODEWHALE_IMAGE=ghcr.io/codewhale-hq/codewhale:vX.Y.Z \ |
| 65 | --build-arg TOOLBOX_PACKAGES="git openssh-client curl build-essential pkg-config python3 python3-pip nodejs npm" \ |
| 66 | -t codewhale-toolbox:my-project . |
| 67 | ``` |
| 68 | |
| 69 | `latest` 只用于一次性测试。共享项目请把 `CODEWHALE_IMAGE` 固定住;新增软件包 |
| 70 | 要走审阅,就像改动其他开发环境一样。 |
| 71 | |
| 72 | 用同一套工作区和状态挂载运行工具箱镜像: |
| 73 | |
| 74 | ```bash |
| 75 | docker volume create codewhale-my-project-home |
| 76 | |
| 77 | docker run --rm -it \ |
| 78 | -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \ |
| 79 | -v codewhale-my-project-home:/home/codewhale/.codewhale \ |
| 80 | -v "$PWD:/workspace" \ |
| 81 | -w /workspace \ |
| 82 | codewhale-toolbox:my-project |
| 83 | ``` |
| 84 | |
| 85 | 在这个可选镜像里,Codewhale 可以用 `sudo apt-get update`、 |
| 86 | `sudo apt-get install -y <package>` 这类命令。想要可重复的容器,就把这些包固化进 |
| 87 | 工具箱 Dockerfile,别让长期运行的容器慢慢漂移。 |
| 88 | |
| 89 | 不要把 API 密钥、SSH 私钥或其他密钥固化进自定义镜像。API 密钥在运行时传入; |
| 90 | SSH 材料要显式挂载,最好只读,并且只给真正需要它的项目。 |
| 91 | |
| 92 | ### Compose 工具箱模板 |
| 93 | |
| 94 | 如果你更想要一个可重复的 `docker compose` 入口,请用 |
| 95 | [`docs/examples/compose.toolbox.yml`](../examples/compose.toolbox.yml)。它会从 |
| 96 | [`docs/examples/Dockerfile.toolbox`](../examples/Dockerfile.toolbox) 构建工具箱 |
| 97 | 镜像,并把项目状态卷显式写出来: |
| 98 | |
| 99 | ```bash |
| 100 | CODEWHALE_IMAGE=ghcr.io/codewhale-hq/codewhale:vX.Y.Z \ |
| 101 | CODEWHALE_TOOLBOX_IMAGE=codewhale-toolbox:my-project \ |
| 102 | CODEWHALE_HOME_VOLUME=codewhale-my-project-home \ |
| 103 | CODEWHALE_WORKSPACE="$PWD" \ |
| 104 | docker compose -f docs/examples/compose.toolbox.yml run --rm codewhale |
| 105 | ``` |
| 106 | |
| 107 | 每个需要独立工具链或独立 `.codewhale` 状态的项目,都该用不同的 |
| 108 | `CODEWHALE_TOOLBOX_IMAGE` 和 `CODEWHALE_HOME_VOLUME`。Compose 文件里还给出了 |
| 109 | SSH 材料和本地 CA 证书的可选只读挂载;除非项目确实需要,否则让它们保持注释状态。 |
| 110 | |
| 111 | ## 多个独立项目 |
| 112 | |
| 113 | 每个项目用一个具名状态卷,这样会话、配置、技能、记忆和离线队列就不会跨工作区 |
| 114 | 互相污染: |
| 115 | |
| 116 | ```bash |
| 117 | project="$(basename "$PWD")" |
| 118 | image="codewhale-toolbox:${project}" |
| 119 | docker volume create "codewhale-${project}-home" |
| 120 | |
| 121 | docker run --rm -it \ |
| 122 | --name "codewhale-${project}" \ |
| 123 | -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \ |
| 124 | -v "codewhale-${project}-home:/home/codewhale/.codewhale" \ |
| 125 | -v "$PWD:/workspace" \ |
| 126 | -w /workspace \ |
| 127 | "$image" |
| 128 | ``` |
| 129 | |
| 130 | 工具链不同的项目,就构建不同的工具箱标签,例如 |
| 131 | `codewhale-toolbox:frontend` 和 `codewhale-toolbox:backend`。issue #2217 里 |
| 132 | 讨论的独立启动器可以建立在这套约定之上,但它有意留在核心 Docker 镜像之外。 |
| 133 | |
| 134 | ## 项目引导脚本 |
| 135 | |
| 136 | Codewhale 不会自动执行 `.codewhale/setup.sh`,也不会自动执行旧版的 |
| 137 | `.deepseek/setup.sh`。如果你留着这类文件当本地的项目配方,就显式运行它。 |
| 138 | 团队共用的环境,优先用提交进仓库的项目脚本或工具箱 Dockerfile,这样环境 |
| 139 | 可以被审阅和重建。 |
| 140 | |
| 141 | 例如,在启动 Codewhale 之前运行一个已提交的引导脚本: |
| 142 | |
| 143 | ```bash |
| 144 | docker run --rm -it \ |
| 145 | -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \ |
| 146 | -v codewhale-my-project-home:/home/codewhale/.codewhale \ |
| 147 | -v "$PWD:/workspace" \ |
| 148 | -w /workspace \ |
| 149 | --entrypoint bash \ |
| 150 | codewhale-toolbox:my-project \ |
| 151 | -lc './scripts/bootstrap-dev.sh && exec codewhale' |
| 152 | ``` |
| 153 | |
| 154 | 需要 `sudo` 的引导脚本请用工具箱镜像。默认镜像不会提权。 |
| 155 | |
| 156 | ## 自定义 CA 证书与代理 |
| 157 | |
| 158 | 企业代理、dev-sidecar、自签名内部服务这类场景,最好把受信任的 CA 证书固化进 |
| 159 | 自定义工具箱镜像: |
| 160 | |
| 161 | ```dockerfile |
| 162 | USER root |
| 163 | COPY docker/certs/*.crt /usr/local/share/ca-certificates/ |
| 164 | RUN update-ca-certificates |
| 165 | USER codewhale |
| 166 | ``` |
| 167 | |
| 168 | 复制到 `/usr/local/share/ca-certificates/` 的文件必须用 `.crt` 扩展名。私有 CA |
| 169 | 材料不要放进公开镜像。 |
| 170 | |
| 171 | 只在本地运行时,把证书以只读方式挂载,并在容器启动时更新信任库: |
| 172 | |
| 173 | ```bash |
| 174 | docker run --rm -it \ |
| 175 | -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \ |
| 176 | -v codewhale-my-project-home:/home/codewhale/.codewhale \ |
| 177 | -v "$PWD:/workspace" \ |
| 178 | -v "$PWD/docker/certs:/usr/local/share/ca-certificates/local:ro" \ |
| 179 | -w /workspace \ |
| 180 | --entrypoint bash \ |
| 181 | codewhale-toolbox:my-project \ |
| 182 | -lc 'sudo update-ca-certificates && exec codewhale' |
| 183 | ``` |
| 184 | |
| 185 | 这套 CA 流程需要那个可选的工具箱镜像,因为默认镜像里没有免密 `sudo`。 |
| 186 | |
| 187 | ## 本地构建 |
| 188 | |
| 189 | 在本地检出目录里构建镜像: |
| 190 | |
| 191 | ```bash |
| 192 | docker build -t codewhale . |
| 193 | ``` |
| 194 | |
| 195 | 然后用同一个由 Docker 管理的数据卷运行它: |
| 196 | |
| 197 | ```bash |
| 198 | docker run --rm -it \ |
| 199 | -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \ |
| 200 | -v codewhale-home:/home/codewhale/.codewhale \ |
| 201 | -v "$PWD:/workspace" \ |
| 202 | -w /workspace \ |
| 203 | codewhale |
| 204 | ``` |
| 205 | |
| 206 | 没有配置 Docker Hub 发布;GHCR 是受支持的预构建镜像仓库。 |
| 207 | |
| 208 | ## 环境变量 |
| 209 | |
| 210 | | 变量 | 是否必需 | 说明 | |
| 211 | |-----------------------|----------|--------------------------------------------------| |
| 212 | | `DEEPSEEK_API_KEY` | 是 | DeepSeek API 密钥 | |
| 213 | | `DEEPSEEK_BASE_URL` | 否 | 自定义 API base URL(例如 `https://api.deepseek.com`) | |
| 214 | | `DEEPSEEK_NO_COLOR` | 否 | 设为 `1` 可关闭终端彩色输出 | |
| 215 | |
| 216 | ## 数据卷 |
| 217 | |
| 218 | 挂载 `/home/codewhale/.codewhale`,让会话、配置、技能、记忆和离线队列在容器 |
| 219 | 重启后依然保留。镜像里也保留 `/home/codewhale/.deepseek` 以兼容旧版本。 |
| 220 | Docker 管理的具名卷是最稳妥的默认选择,因为 Docker 创建它时,会把属主设成 |
| 221 | 容器能写入的那个用户: |
| 222 | |
| 223 | ```bash |
| 224 | -v codewhale-home:/home/codewhale/.codewhale |
| 225 | ``` |
| 226 | |
| 227 | 不挂载这个卷,容器每次启动都是全新的。 |
| 228 | |
| 229 | 如果改为绑定挂载主机上已有的目录,镜像会以非 root 的 `codewhale` 用户运行, |
| 230 | UID/GID 为 `1000:1000`。挂载的目录必须对该用户可写,否则启动时创建 |
| 231 | `.codewhale/tasks` 下的运行时目录可能会失败。在 Linux 主机上,要么用上面的具名卷, |
| 232 | 要么显式准备好绑定挂载: |
| 233 | |
| 234 | ```bash |
| 235 | mkdir -p ~/.codewhale |
| 236 | sudo chown -R 1000:1000 ~/.codewhale |
| 237 | |
| 238 | docker run --rm -it \ |
| 239 | -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \ |
| 240 | -v ~/.codewhale:/home/codewhale/.codewhale \ |
| 241 | ghcr.io/codewhale-hq/codewhale:latest |
| 242 | ``` |
| 243 | |
| 244 | 这条 `chown` 会改变主机上 `~/.codewhale` 目录的属主。如果不想让容器里的 UID |
| 245 | 拥有你的本地配置,就跳过它,改用具名卷。 |
| 246 | |
| 247 | ## 非交互 / 流水线用法 |
| 248 | |
| 249 | stdin 不是 TTY 时,`codewhale` 会退到调度器的一次性模式(`codewhale -c "…"`)。 |
| 250 | 把提示词用 stdin 传进去: |
| 251 | |
| 252 | ```bash |
| 253 | echo "Explain the Cargo.toml in structured English." | \ |
| 254 | docker run --rm -i -e DEEPSEEK_API_KEY ghcr.io/codewhale-hq/codewhale:latest |
| 255 | ``` |
| 256 | |
| 257 | ## 在本地构建 |
| 258 | |
| 259 | ```bash |
| 260 | # Single platform (your host architecture) |
| 261 | docker build -t codewhale . |
| 262 | |
| 263 | # Multi-platform (requires a builder with emulation) |
| 264 | docker buildx create --use |
| 265 | docker buildx build --platform linux/amd64,linux/arm64 -t codewhale . |
| 266 | ``` |
| 267 | |
| 268 | ## 开发容器 |
| 269 | |
| 270 | 仓库里有一个给 VS Code / GitHub Codespaces 用的 |
| 271 | [`.devcontainer/devcontainer.json`](../../.devcontainer/devcontainer.json) 配置。 |
| 272 | 它会构建一个专用的开发镜像,里面有 Rust 工具链、Git、`pkg-config`,以及工作区 |
| 273 | 需要的 DBus 开发头文件。首次打开会运行 `cargo build --locked`,并安装 |
| 274 | rust-analyzer 和其他编辑器扩展。 |
| 275 | |
| 276 | 源码检出仍从主机挂载。Codewhale 状态和 Cargo 构建产物改用 Docker 具名卷。 |
| 277 | 这样一来,就算 VS Code 提供不了 POSIX 风格的 `HOME` 变量(Windows 上尤其 |
| 278 | 如此),这套配置照样能用,构建也不会经由 Windows 绑定挂载写入数千个小 |
| 279 | 文件。改动 Dev Container 配置后要重建容器。 |
| 280 | |
| 281 | ## 发布状态 |
| 282 | |
| 283 | Docker 镜像发布是发布门禁的一部分。镜像会以 semver 标签加 `latest` 的形式 |
| 284 | 发布到 GHCR,覆盖 `linux/amd64` 和 `linux/arm64`。 |
| 285 |