Files
workpod/SPECS.md
T

21 KiB
Raw Blame History

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. 架构设计

graph TB
    subgraph "Docker Host"
        DC["docker-compose.yml<br/>基础实例"]
        DCF["docker-compose.flux.yml<br/>Flux 项目"]
        DCA["docker-compose-alpine.yml<br/>备用/测试"]
    end

    subgraph "Images"
        BASE["workpod-alpine:latest<br/>Alpine 3.23 + Node 24<br/>~436MB"]
        FLUX["ws-flux-dev:latest<br/>BASE + JDK17/Maven 挂载"]
    end

    subgraph "Containers"
        W1["workpod-alpine<br/>SSH :2222 / Web :7681<br/>network: workpod-alpine-network"]
        W2["ws-flux-dev<br/>SSH :2201 / Web :7701<br/>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<br/>认证代理 :8080<br/>Basic Auth + Token"]
        NX["nginx (wk.1216.top)<br/>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)

# 多阶段构建
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)

FROM workpod-alpine:latest
# 无额外 RUN 指令
# JDK 通过 docker-compose volume 从宿主机挂载
# JAVA_HOME 由 environment 设置
EXPOSE 22 7681
ENTRYPOINT ["/entrypoint.sh"]

Flux 镜像与基础镜像几乎等大(仅增加 LABEL 元数据),JDK/Maven 不占用镜像空间。

3.3 构建命令

# 基础镜像
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 启动命令

# 基础实例
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 信号处理

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 连接速查

# 基础实例
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 生产部署

# 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 下载脚本

./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" 标签实际仍链接 glibcAlpine openjdk 无法外部挂载;gcompat 缺少符号
  • 参考: 经 ldd 验证解释器为 /lib/ld-musl-x86_64.so.1

DEC-04: npm 包 @latest vs 固定版本

  • 决策: 保持 @latestclaude-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 failedbind mount 源目录删除后容器失效);符合 immutable 镜像最佳实践
  • 触发事件: 2026-04-04 因删除 workpod-alpine 目录导致 workpod 容器 ttyd 子进程全部 crash (exit code 254)

附录 A: 常用运维命令

# 构建
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/                   # 历史版本归档