# WorkPod Alpine 技术规格 > 版本:1.1.0 | 更新日期:2026-04-07 | 镜像版本:workpod-alpine:latest (1.1.0) --- ## 1. 项目概述 WorkPod Alpine 是一个**轻量级 Docker 开发环境容器**,基于 Alpine Linux 3.23 构建,提供 Web 终端(ttyd)和 SSH 双入口,支持多实例部署、开发工具自动检测、tmux 多会话管理。 ### 核心特性 | 特性 | 说明 | |------|------| | **镜像大小** | ~436MB(多阶段构建,Ubuntu 版的 1/6) | | **基础镜像** | alpine:3.23 (~7MB) | | **运行时内存** | 80-120MB 基线 + 应用 | | **启动时间** | ~2.5 秒 | | **Web 终端** | ttyd + tmux 会话管理 | | **AI 工具集成** | Claude Code + coding-helper + 智谱 GLM | ### 适用场景 - 远程开发环境(浏览器直接访问,无需本地配置) - 团队成员统一开发环境 - 多项目隔离部署(每个项目独立容器实例) --- ## 2. 架构设计 ```mermaid graph TB subgraph "Docker Host" DC["docker-compose.yml
基础实例"] DCF["docker-compose.flux.yml
Flux 项目"] DCA["docker-compose-alpine.yml
备用/测试"] end subgraph "Images" BASE["workpod-alpine:latest
Alpine 3.23 + Node 24
~436MB"] FLUX["ws-flux-dev:latest
BASE + JDK17/Maven 挂载"] end subgraph "Containers" W1["workpod-alpine
SSH :2222 / Web :7681
network: workpod-alpine-network"] W2["ws-flux-dev
SSH :2201 / Web :7701
network: workpod-flux-network"] end subgraph "Host Mounts" WS["E:/wk-flux → /workspace"] JDK["D:/Java/jdk-musl-17 → /opt/jdk-musl-17 :ro"] MVN["D:/Java/maven-mvnd → /opt/maven-mvnd :ro"] end subgraph "Optional" AP["auth-proxy.js
认证代理 :8080
Basic Auth + Token"] NX["nginx (wk.1216.top)
SSL 终止 + 反向代理"] end DC -->|"build"| BASE DCF -->|"build"| FLUX DCA -->|"image"| W1 BASE --> W1 FLUX --> W2 W2 --> WS W2 --> JDK W2 --> MVN AP -.->|"proxy"| W1 AP -.->|"proxy"| W2 NX -.->|"ssl termination"| AP ``` ### 组件职责 | 组件 | 职责 | 状态 | |------|------|------| | `Dockerfile` | 基础镜像构建(Node + Claude Code + coding-helper) | 活跃 | | `Dockerfile.flux` | Flux 项目扩展(基于基础镜像,JDK 外挂) | 活跃 | | `entrypoint.sh` | 容器入口:信号处理、PATH 检测、服务启停 | 活跃 | | `ttyd-session.sh` | tmux 会话管理(URL 参数 / 交互菜单) | 活跃 | | `docker-compose.yml` | 基础实例编排 | 活跃 | | `docker-compose.flux.yml` | Flux 项目实例编排 | 活跃 | | `docker-compose-alpine.yml` | 备用编排(多端口 /root 挂载) | 归档备用 | | `auth-proxy.js` | 认证代理(Basic Auth + Token + WS 代理) | 可选组件 | | `download-packages.sh` | 离线包下载脚本 | 归档备用 | | `entrypoint-test.sh` | Test 入口(developer 用户 + 全权限 Claude) | 已归档 | --- ## 3. 镜像构建 ### 3.1 基础镜像 (Dockerfile) ```dockerfile # 多阶段构建 FROM alpine:3.23 AS builder # 阶段1: 构建 # → 安装 xz, libstdc++ # → 解压 Node.js v24.14.1 (musl) # → npm install -g claude-code@latest, coding-helper@latest # → npm cache clean FROM alpine:3.23 # 阶段2: 运行时 # → apk add: curl git openssh-server bash ttyd tmux libstdc++ # → ssh-keygen + chpasswd + PermitRootLogin yes # COPY --from=builder /opt/node /usr/local # → npm config set registry npmmirror.com # COPY --chmod=755 entrypoint.sh /entrypoint.sh # COPY --chmod=755 ttyd-session.sh /opt/ttyd-session.sh ``` **构建产物**: | 层 | 内容 | 大小 | |----|------|------| | alpine:3.23 | 基础系统 | ~7MB | | runtime tools | curl/git/ssh/ttyd/tmux/bash | ~80MB | | /usr/local | Node.js + npm 全局包 | ~200MB | | scripts | entrypoint + ttyd-session | <10KB | | **总计** | | **~436MB (压缩后)** | ### 3.2 Flux 扩展镜像 (Dockerfile.flux) ```dockerfile FROM workpod-alpine:latest # 无额外 RUN 指令 # JDK 通过 docker-compose volume 从宿主机挂载 # JAVA_HOME 由 environment 设置 EXPOSE 22 7681 ENTRYPOINT ["/entrypoint.sh"] ``` Flux 镜像与基础镜像几乎等大(仅增加 LABEL 元数据),JDK/Maven 不占用镜像空间。 ### 3.3 构建命令 ```bash # 基础镜像 docker build -t workpod-alpine:latest . # Flux 镜像(依赖基础镜像先构建好) docker build -t ws-flux-dev:latest -f Dockerfile.flux . ``` --- ## 4. 容器编排 ### 4.1 三份 Compose 文件对比 | 配置项 | docker-compose.yml (基础) | docker-compose.flux.yml (Flux) | docker-compose-alpine.yml (备用) | |--------|--------------------------|-------------------------------|--------------------------------| | **Service 名** | workpod-alpine | ws-flux-dev | workpod-alpine | | **Container 名** | workpod-alpine | ws-flux-dev | workpod-alpine | | **SSH 端口** | 2222 → 22 | 2201 → 22 | 2223 → 22 | | **Web 端口** | 7681 → 7681 | 7701 → 7681 | 7683→7681, 7684→7682 | | **工作空间挂载** | ./data/workspace → /workspace | E:/wk-flux → /workspace | data/workpod-alpine/workspace → /workspace | | **JDK 挂载** | 无 | D:/Java/jdk-musl-17:ro → /opt/jdk-musl-17 | 无 | | **Maven 挂载** | 无 | D:/Java/maven-mvnd:ro → /opt/maven-mvnd | 无 | | **智谱 GLM env** | 无 | ANTHROPIC_* 5 项传递 | 无 | | **privileged** | true | false | true | | **内存限制** | 4G limit / 1G reserve | 4G limit / 1G reserve | 无 | | **健康检查** | 有 | 有 | 无 | | **日志轮转** | 10m × 3 | 10m × 3 | 无 | | **网络** | workpod-alpine-network | workpod-flux-network | workpod-network | ### 4.2 启动命令 ```bash # 基础实例 docker compose up -d # Flux 项目实例 docker compose -f docker-compose.flux.yml up -d # 备用实例 docker compose -f docker-compose-alpine.yml up -d ``` --- ## 5. 运行时 ### 5.1 启动流程 (entrypoint.sh) ``` trap SIGTERM/SIGINT/SIGQUIT → cleanup() │ ▼ ┌─ PATH 自动检测 ─────────────────────────┐ │ JAVA_HOME → ${JAVA_HOME}/bin │ │ MAVEN_HOME → ${MAVEN_HOME}/bin │ │ .cargo/bin → Rust (cargo/rustup) │ │ gvm/gos → Go (gvm 版本管理) │ │ .pyenv → Python (pyenv) │ │ go/bin → Go (自定义安装) │ │ .local/bin → 本地工具 │ │ rust-* → Rust 独立安装 │ └──────────────────────────────────────────┘ │ ▼ ┌─ 写入 /etc/profile.d/dev-tools.sh ─────┐│ (新 shell 会话也生效) │ (同样逻辑,确保持久化) │ └──────────────────────────────────────────┘ │ ▼ ┌─ 启动服务 ────────────────────────────────┐ │ /usr/sbin/sshd │ │ ttyd -W -c "${TTYD_CREDENTIALS}" \ │ │ -t fontSize=16 \ │ │ -t theme='{"background":"#1a1a2e"}' │ │ /opt/ttyd-session.sh & │ └──────────────────────────────────────────┘ │ ▼ ┌─ 健康检查 ────────────────────────────────┐ │ kill -0 $TTYD_PID → ttyd 存活? │ │ pidof sshd → sshd 存活? │ │ 任一失败 → exit 1 │ └──────────────────────────────────────────┘ │ ▼ ┌─ 显示环境信息 ──────────────────────────┐ │ Node/npm/Claude/Rust/Go/Python 版本 │ │ 连接方式 (Web URL + SSH 命令) │ └──────────────────────────────────────────┘ │ ▼ exec sleep infinity (PID 1) ``` ### 5.2 信号处理 ```bash cleanup() { kill $TTYD_PID # ttyd 后台进程 kill $SSHD_PID # sshd (注意: 当前 PID 未追踪,实际用 pkill) wait # 等待子进程退出 exit 0 } trap cleanup SIGTERM SIGINT SIGQUIT ``` ### 5.3 ttyd 会话管理 (ttyd-session.sh) ``` 用户访问 http://host:7681 │ ▼ ┌─ URL 带 ?session=xxx ? ──────┐ │ 是 → 解析 session 名称 │ │ ↓ │ │ 过滤非法字符 (仅 a-zA-Z0-9_-) │ │ ↓ │ │ tmux attach (存在) 或 new (不存在)│ │ │ │ 否 → 显示交互式菜单 │ │ ┌─────────────────────────┐ │ │ │ [1] session_a │ │ │ │ [2] session_b │ │ │ │ 输入编号或新名称 │ │ │ └─────────────────────────┘ │ │ ↓ │ │ 数字 → 映射到已有 session │ │ 字符串 → 新建 session │ │ ↓ │ │ exec tmux attach/new │ └─────────────────────────────────────┘ ``` **安全设计**: - Session 名过滤:`tr -cd 'a-zA-Z0-9_-'` 防止注入 - 共享模式:不带 `-d` 参数,多人可同时查看同一 session - 默认值:空输入默认为 `default` --- ## 6. 服务端口与连接方式 ### 6.1 端口分配表 | 实例 | SSH | Web 终端 (ttyd) | 网络 | |------|-----|---------------|------| | **workpod-alpine** (基础) | 2222 | 7681 | workpod-alpine-network | | **ws-flux-dev** (Flux) | 2201 | 7701 | workpod-flux-network | | **workpod-test** (旧测试) | 2223 | 7682 | — | ### 6.2 连接速查 ```bash # 基础实例 Web: http://localhost:7681 # 用户 jc / 密码 1234567 SSH: ssh root@localhost -p 2222 # 密码 workpod123 (或 ROOT_PASSWORD) # Flux 项目实例 Web: http://localhost:7701 # 用户 jc / 密码 1234567 SSH: ssh root@localhost -p 2201 # 密码 workpod123 (或 ROOT_PASSWORD) # 进入容器 (调试用) MSYS_NO_PATHCONV=1 docker exec -it workpod-alpine bash MSYS_NO_PATHCONV=1 docker exec -it ws-flux-dev bash ``` ### 6.3 环境变量速查 | 变量 | 默认值 | 用途 | |------|--------|------| | `ROOT_PASSWORD` | workpod123 | SSH root 密码 | | `TTYD_CREDENTIALS` | jc:1234567 | ttyd Web 终端认证 (格式 `用户:密码`) | | `TZ` | Asia/Shanghai | 时区 | | `JAVA_HOME` | (无) | JDK 路径 (Flux 实例设置) | | `MAVEN_HOME` | (无) | Maven 路径 (Flux 实例设置) | | `ANTHROPIC_AUTH_TOKEN` | (从宿主机继承) | 智谱 API 密钥 | | `ANTHROPIC_BASE_URL` | https://open.bigmodel.cn/api/anthropic | 智谱 API 地址 | | `ANTHROPIC_DEFAULT_SONNET_MODEL` | glm-5v-turbo | Sonnet 模型 | | `ANTHROPIC_DEFAULT_OPUS_MODEL` | glm-5.1 | Opus 模型 | | `ANTHROPIC_DEFAULT_HAIKU_MODEL` | glm-4.5-air | Haiku 模型 | --- ## 7. 开发工具外挂机制 ### 7.1 支持的工具链 entrypoint.sh 启动时自动检测以下路径并加入 PATH: | 工具 | 检测路径 | 环境变量 | |------|---------|---------| | Java (JDK) | `$JAVA_HOME/bin` | JAVA_HOME | | Maven | `$MAVEN_HOME/bin` | MAVEN_HOME | | Rust (rustup) | `/root/.cargo/bin` | RUSTUP_HOME, CARGO_HOME | | Rust (独立) | `/root/rust-*/bin` | — | | Go (gvm) | `/root/gvm/gos/current/bin` | — | | Python (pyenv) | `/root/.pyenv/shims`, `.pyenv/bin` | — | | Go (自定义) | `/root/go/bin` | GOROOT | | 本地工具 | `/root/.local/bin` | — | ### 7.2 PATH 生效范围 - **当前 entrypoint 进程**:立即 export - **后续 shell 登录**:通过 `/etc/profile.d/dev-tools.sh` 自动加载 - **docker exec**:需要 `bash -lc 'command'` 或手动 source profile ### 7.3 使用方式 开发者只需将工具安装到对应路径(通过挂载 /root 或在容器内安装),重启或重新登录即可生效。无需修改 Dockerfile 或 entrypoint。 --- ## 8. 多实例部署策略 ### 8.1 推荐方案 ``` 项目 A → docker-compose.A.yml (端口 220x / 770x) 项目 B → docker-compose.B.yml (端口 221x / 771x) 通用 → docker-compose.yml (端口 2222 / 7681) ``` 每个项目一份 compose 文件,基于同一个基础镜像 `workpod-alpine:latest`,通过 Dockerfile 扩展或 volume 挂载添加项目特定依赖。 ### 8.2 扩展新实例步骤 1. 创建 `Dockerfile.xxx`(如需额外包)或直接用基础镜像 2. 创建 `docker-compose.xxx.yml`(配置端口、挂载、环境变量) 3. `docker compose -f docker-compose.xxx.yml up -d` 4. 确认端口不冲突 ### 8.3 网络隔离 每个 compose 文件创建独立的 bridge network,容器间默认不可互通。如需互通可在同一 compose 中定义多 service。 --- ## 9. 安全模型 ### 9.1 当前状态 | 安全项 | 配置 | 风险等级 | 备注 | |--------|------|---------|------| | **特权模式** | 基础版 `privileged: true` | **高** | Flux 版已移除 | | **运行用户** | 全部 root | 中 | 开发环境可接受 | | **SSH 登录** | PermitRootLogin yes + 密码 | 中 | 建议生产环境禁用 | | **ttyd 认证** | Basic Auth (jc:1234567) | 低-中 | 可配置强密码 | | **密码管理** | 环境变量注入 | 低 | 已修复硬编码 | | **API Key** | docker-compose env 传递 | 低 | 不写入文件系统 | | **镜像源** | alpine:3.23 + npmmirror | 低 | 第三方信任链 | | **npm 包** | @latest (不确定版本) | 低 | 用户决策,接受风险 | ### 9.2 已修复的问题 - ~~密码硬编码在源码中~~ → ROOT_PASSWORD / TTYD_CREDENTIALS 环境变量 - ~~API Key 写入 .bashrc~~ → docker-compose environment 直接传递 - ~~healthcheck CMD 数组管道错误~~ → CMD-SHELL + netstat - ~~entrypoint 外挂 bind mount~~ → COPY --chmod 内置镜像 - ~~构建上下文 ~520MB~~ → .dockerignore 优化到 275 bytes ### 9.3 待改进项 | 优先级 | 改进项 | 建议 | |--------|--------|------| | P0 | 移除 privileged: true | 基础版改用具体 capability 或确认是否真需要 | | P1 | SSH 禁用密码登录 | 改为密钥认证,或限制来源 IP | | P2 | ttyd 加密传输 | 前置 nginx/caddy 做 HTTPS 终止 | | P3 | npm 包固定版本 | 将 @latest 改为具体版本号,构建可重现 | --- ## 10. 认证代理 (auth-proxy.js) > 状态:可选组件,已补充到主目录但未默认启用 ### 10.1 架构 ``` 浏览器 → :8080 auth-proxy.js ├── GET /login → 登录页面 (static/login.html) ├── POST /auth/check → Basic Auth → Token (1h 过期) ├── GET /api/workspaces → 工作空间列表 ├── GET/WS /* → Token 验证 → 代理到 ttyd :7681 └── WebSocket upgrade → 双向管道 (终端 I/O) ``` ### 10.2 配置 | 配置项 | 值 | |--------|-----| | 监听端口 | 8080 | | 认证方式 | Basic Auth (`wk:1234567`, `admin:admin123`) | | Token 格式 | `wk_{用户名}_{时间戳}` | | Token 过期 | 1 小时 (内存存储) | | 工作区路由 | 默认(/) → :7681, hszd → :7682 | ### 10.3 生产部署 ```bash # 1. 修改密码(环境变量或配置文件) # 2. 前置 Nginx 反向代理 (wk.1216.conf) # 3. SSL 证书 (wk.1216.top) # 4. 启动 node auth-proxy.js ``` --- ## 11. 离线包管理 > 状态:归档备用,Dockerfile 直接从 registry 安装 ### 11.1 包清单 | 包名 | 版本 | 用途 | 来源 | |------|------|------|------| | node-v24.14.1-linux-x64-musl.tar.gz | v24.14.1 | Node.js 运行时 | unofficial-builds.nodejs.org | | claude-code-*.tgz | @latest | AI 编程助手 | registry.npmmirror.com | | z_ai-coding-helper-*.tgz | @latest | AI 编程助手 | registry.npmmirror.com | | openclaw-*.tgz | 2026.3.28 | (未使用) | registry.npmmirror.com | | go1.26.1.linux-amd64.tar.gz | 1.26.1 | (外挂预留) | Go 官方 | | rust-1.94.1-x86_64-unknown-linux-musl.tar.xz | 1.94.1 | (外挂预留) | Rust 官方 | ### 11.2 下载脚本 ```bash ./download-packages.sh # 自动下载到 packages/ 目录 # 支持断点续传(文件存在则跳过) # 使用国内镜像加速 ``` --- ## 12. 决策记录 以下是在项目演进过程中做出的关键架构决策及其原因: ### DEC-01: Alpine vs Ubuntu 作为基础镜像 - **决策**: 选择 Alpine 3.23 - **原因**: 镜像小 6 倍(436MB vs 2.6GB)、内存基线低 5 倍、启动快 6 倍 - **代价**: musl libc 兼容性需注意(JDK 必须用 musl 版本) ### DEC-02: JDK/Maven 宿主机挂载 vs 镜像内安装 - **决策**: 选择宿主机目录 ro 挂载(`D:/Java/jdk-musl-17`, `D:/Java/maven-mvnd`) - **原因**: 升级 JDK/Maven 无需 rebuild 镜像;本地多项目可共享同一套工具链 - **代价**: 依赖宿主机路径存在;容器不可脱离该主机单独分发 ### DEC-03: BellSoft Liberica JDK vs 其他 musl JDK - **决策**: BellSoft Liberica JDK 17.0.16+12 (musl) - **原因**: Adoptium "musl" 标签实际仍链接 glibc;Alpine openjdk 无法外部挂载;gcompat 缺少符号 - **参考**: 经 ldd 验证解释器为 `/lib/ld-musl-x86_64.so.1` ### DEC-04: npm 包 @latest vs 固定版本 - **决策**: 保持 @latest(claude-code 和 coding-helper) - **原因**: 用户选择每次构建自动获取最新版本,接受不确定性 - **风险**: 构建结果可能不一致;回归问题排查困难 ### DEC-05: auto-upgrade 功能废弃 - **决策**: 不实现容器启动时自动检测升级 - **原因**: 维护负担 > 收益;增加启动复杂度和网络依赖;set -e 下失败处理困难 - **替代**: 手动 `docker build` 时自然获取最新 ### DEC-06: workpod-alpine 目录合并到 workpod - **决策**: 删除 `E:/wk-lab/workpod-alpine/`,全部内容归入 `E:/wk-lab/workpod/` - **原因**: 单一目录管理;避免两份代码漂移;旧版完整归档于 `_archive/` ### DEC-07: entrypoint/ttyd-session 内置镜像 vs bind mount - **决策**: COPY --chmod=755 内置到镜像 - **原因**: 避免 execvp failed(bind mount 源目录删除后容器失效);符合 immutable 镜像最佳实践 - **触发事件**: 2026-04-04 因删除 workpod-alpine 目录导致 workpod 容器 ttyd 子进程全部 crash (exit code 254) --- ## 附录 A: 常用运维命令 ```bash # 构建 docker build -t workpod-alpine:latest . docker build -t ws-flux-dev:latest -f Dockerfile.flux . # 启动/停止 docker compose up -d docker compose down docker compose -f docker-compose.flux.yml up -d # 查看日志 docker logs -f workpod-alpine docker logs --tail 50 ws-flux-dev # 进入容器 MSYS_NO_PATHCONV=1 docker exec -it workpod-alpine bash # 验证工具链 (需 login shell) MSYS_NO_PATHCONV=1 docker exec -it workpod-alpine bash -lc ' echo "Java: $(java -version 2>&1 | head -1)" echo "Maven: $(mvn -version 2>&1 | head -1)" echo "Node: $(node -v)" echo "Claude: $(claude --version)" ' # 清理 docker system prune -f # 清理悬停资源 docker image prune -f # 清理未使用镜像 ``` ## 附录 B: 文件总览 ``` workpod/ ├── Dockerfile # 基础镜像 (多阶段构建) ├── Dockerfile.flux # Flux 扩展镜像 ├── entrypoint.sh # 容器入口 (信号/PATH/服务/信息) ├── ttyd-session.sh # tmux 会话管理 ├── docker-compose.yml # 基础实例 (:2222/:7681) ├── docker-compose.flux.yml # Flux 实例 (:2201/:7701) ├── docker-compose-alpine.yml # 备用编排 ├── auth-proxy.js # 认证代理 (可选) ├── download-packages.sh # 离线包下载 (归档) ├── entrypoint-test.sh # Test 入口 (归档) ├── wk.1216.conf # Nginx 配置 ├── nginx-map-patch.sh # Nginx WS 补丁 ├── connection_upgrade.map # Nginx map 片段 ├── .dockerignore # 构建忽略 ├── ISSUES.md # 问题记录 ├── SPECS.md # 本文档 ← 你在这里 ├── docs/ │ └── 05-问题处理/ # 5 份审核报告 ├── config/ # 配置模板 ├── static/ # auth-proxy 登录页 ├── data/ # 运行时数据 └── _archive/ # 历史版本归档 ```