Skip to content

feat: 设计并实现 Docker Compose 应用管理 #2

Closed

Description

@wuxs

目标

在 devbox 中交付可用于单机开发环境的 Docker Compose 应用管理,与 Kubernetes runtime 并列,并统一复用应用商店、应用列表和管理抽屉。当前范围是:单机、文件原生、持久异步任务驱动、安全受控的 Compose Stack 管理器、原生 1Panel 开源应用商店兼容,以及系统 Compose project 自动发现与安全接管。

Compose 应用管理(已完成)

  • apps.Controller 统一领域 seam,Compose/Kubernetes adapter 隐藏在模块内部。
  • Application/Task/Revision 稳定模型;旧读路径/action 兼容;phase、service replica/health 由后端聚合。
  • Compose CLI v2 写、Docker Engine API 读;SQLite 保存 Application/Revision/Task/Idempotency/Audit;Compose、env、sidecar 与 revision 文件原生落盘。
  • deploy/start/stop/restart/redeploy/remove/purge/definition restore 全部异步返回 Task。
  • per-app 串行、跨 app 有限并发、显式幂等、乐观并发、崩溃恢复和 last observed 校验。
  • 默认卸载保留数据;purge 只删除 managed 数据;external/bind/socket 永不自动删除。
  • 粘贴/上传、预检、结构化参数、Compose 编辑、版本历史、definition restore,以及概览/服务/日志/Compose/环境变量/存储/版本/操作记录 8 个详情 Tab。
  • edge-apiserver storeapps 与通用 devbox/v1 HTTP/Git catalog 进入同一可信安装链路。

原生 1Panel 应用商店(已完成)

直接读取 1Panel 官方仓库格式(根 data.yaml、apps/<key>/data.yml、版本 data.yml/formFields、docker-compose.yml),不要求上游转换为 devbox 私有 catalog。

  • UI 可添加、测试、保存、启停、刷新、删除 1Panel Git 来源;自动探测或显式选择 1panel。
  • 动态来源持久化到现有 apps.db,与只读 YAML 来源合并;token 只写不回显;来源变更与审计同事务。
  • 原生解析应用、版本、参数和 Compose,安装时可信重取并走 Controller.Apply、Task、revision 与风险策略。
  • 处理来源隔离 ID、1panel-network、container_name、不执行脚本、password 默认值、可变镜像、残留插值、sparse clone、last-good cache 和来源 generation。
  • 动态 URL 仅允许解析到公网地址的 HTTPS,拒绝 userinfo/query/fragment、私网/loopback/link-local/metadata。

系统 Compose project 自动发现与安全接管(已完成)

  • 按标准 Compose labels 扫描所有仍有容器记录(包含 stopped)的 project;与受管应用去重后统一展示。
  • 外部 project 使用稳定 ID,展示 project name、services、phase/health、端口和来源路径诊断;明确标记“已发现 · 只读”。
  • 未接管项目只读,禁止 start/stop/restart/redeploy/remove/edit 等写操作,避免误操作系统现有服务。
  • 列表扫描不读取 labels 指向的宿主文件;只有登录用户显式点击“接管并编辑”后才读取并复制定义。
  • Linux 接管使用 openat2 原子打开,拒绝 symlink/path traversal/magic-link/跨文件系统/非普通文件/超限或读取中变化;FIFO 不会阻塞请求。
  • Compose CLI 前阻断 include/build/env_file/extends/configs/secrets 等额外宿主读取;临时副本 0700/0600、空 env、固定子进程环境。
  • 接管后保留原 Compose project name 原地管理,元数据持久化并可重启恢复;named volume 数据不变。
  • 接管前执行 compose config 与现有风险预检;blocked 不可覆盖,confirmation 需显式确认并审计。
  • 接管成功后进入现有 Compose 编辑、revision、Task、日志、生命周期和卸载数据边界;源目录不被 devbox 修改。
  • 完全 down 且容器记录已删除的外部 project 无法从 Docker daemon 自动发现,UI/文档明确说明;可继续通过粘贴/上传导入。

安全边界

  • blocked 永不可 override:privileged、docker.sock、host network/PID/UTS/userns、cap_add ALL、系统关键 bind 等。
  • Secret 只进入权限受限的 .env/临时期望文件,HTTP、revision、Task、audit、CLI error、日志全链路脱敏。
  • 动态 Git 来源通过 Git CLI 获取,URL 校验到 clone 之间仍存在无法 pin DNS 的 rebinding 时间窗;该残余风险已写入研究文档。
  • 自动发现依赖仍保留的容器 metadata;完全 compose down 后不做文件系统全盘搜索。

验证

  • git diff --check
  • make test(Go race;pkg/apps 71.6%)
  • go vet ./...
  • 目标前端 ESLint 0 error;helper tests 7/7;Vite build
  • make build-all,嵌入 UI 与 Linux amd64 二进制同步
  • integration build tag 编译
  • 真实 Docker 发现→接管→编辑/重部署→进程重启恢复,保留原 project name 与 named volume marker;完全 down 后不再发现(48.89s)
  • 1Panel 官方源真实集成测试(目标机直连官方仓库)

明确边界

  • CasaOS:官方 v2 协议已做一手评估,但构建产物、路径模型与参数模型不同,本 PR 不放入半成品 adapter。
  • fnOS / Unraid:本轮不在范围,不作未经核验的开闭源断言。
  • 多主机、数据备份/恢复、GitOps 自动 reconcile、K8s↔Compose 自动转换:不属于当前单机里程碑。

实现与验证见 PR #3。

Activity

  1. wuxs commented on Jul 21, 2026

    @wuxs
    CollaboratorAuthor

    本 PR 进展:阶段 0-3 MVP(端到端跑通,含真实 Docker 验证)

    分支 issue-2-compose-apps(从最新 origin/main 的 worktree),阶段 0-3 已完成并端到端验证。按任务要求仅本地 commit,未 push、未开 PR。

    已实现

    • 阶段 0 领域 seam:稳定 Application/Task/Revision 模型,Controller 是唯一对外 seam,runtimeAdapter(compose/kubernetes)为内部 seam;旧 K8s Manager 迁成 kubernetesRuntime adapter(deploymentToAppInfo→deploymentToApplication,phase 由后端聚合);HTTP JSON 向前兼容旧 useApps/useAppDetail 读路径,前端零改。
    • 阶段 1 Compose 只读:轻量 Docker Engine 客户端(net/http,支持 unix socket 与 tcp:///DOCKER_HOST,刻意不引入 docker SDK),按 com.docker.compose.project=devbox-* 只发现受管 project;Docker 不可用时 capability 清晰且不影响 K8s。
    • 阶段 2 持久化 + 异步任务:SQLite(modernc.org/sqlite,纯 Go 无 cgo)存元数据/任务/revision/幂等/审计;持久 Task worker:per-app 串行、幂等(同 key 同请求→同 task;同 key 异请求→409)、崩溃恢复;start/stop/restart/redeploy/remove,默认保留数据,purge 显式,external volume 永不删。
    • 阶段 3 创建/编辑:inline Compose 创建/更新;compose config 预检;风险策略 blocked/confirmation/warning(阻断 privileged/docker.sock 挂载/根目录 bind/host network/pid:host;latest/main 强警告,本地创建不静默接受);revision 历史 + 乐观并发(expectedRevision 不匹配→409);app ID 防路径穿越、Compose 参数防 shell 注入、secret 仅写 .env(0600) 不回传/不入 revision/审计/任务摘要。
    • HTTP:保留旧读/action 兼容翻译层;新写 API 返回 202+Task;新增 validate/capability/tasks/actions/compose/revisions/restore/operations。
    • UI(最小):新增「Compose 应用」入口 → ComposeManager 页(runtime 筛选 / 生命周期 / 新建预检 / 任务反馈),不重写 Desktop/AppMgmtDrawer。

    真实验证(不掺水)

    • make test(-race)全绿:领域 / 风险策略 / SQLite 持久化 / worker(per-app 串行 + 崩溃恢复 + secret 脱敏)/ HTTP 路由与状态码(202/409/422/404 映射)。
    • 真实 Docker 集成测试(build tag integration,默认不跑):用缓存的 alpine:3 在真实 daemon 上跑通 apply→observe(running,2 services)→stop→start→remove→logs。
    • HTTP 端到端冒烟(真实 binary + 远程 TCP daemon):capability、validate、create(202)、task 轮询→succeeded、list/detail、stop/start 相位转换、revision 冲突 409、幂等(202/202/409)、delete→404 全部通过。

    关键决策

    • 写走 docker compose CLI(exec.CommandContext 参数数组、无 shell、固定 project name devbox-<app-id>);读走 Engine API。
    • Compose 文件落盘为事实源(Dockge 式),元数据/任务入 SQLite(事务/恢复/幂等/审计强于散落 JSON)。
    • 本开发环境的 Docker 为远程 TCP daemon(DOCKER_HOST=tcp://...:2376,实测为 plain HTTP),故 Engine 客户端同时支持 unix/tcp;生产 socket 同样适用。

    剩余(未做,明确边界)

    • 阶段 4 应用商店统一:AppStore 安装仍走旧 edge-apiserver store 协议,未改调 Controller.Apply;商店包渲染 Compose 留后续。
    • 阶段 5 备份与多主机:按 Issue「仅在出现真实第二台主机后启动」,未设计。
    • UI 仅最小入口;AppMgmtDrawer 的 Compose 专属 tab(Compose 编辑器/环境变量/存储/版本/操作记录)未接入(API 已就绪)。

    待 review 关注点

    • 旧 action 兼容层对 Compose 应用同步等待最多 30s(K8s 秒级无影响);新 UI 用异步 /actions。
    • sqliteRepo 单连接写(per-app 串行 + 有限并发读足够),WAL + busy_timeout。

    下一步:review 通过后 push + 开 PR(本会话按要求仅本地 commit 6cbc787)。

  2. wuxs commented on Jul 22, 2026

    @wuxs
    CollaboratorAuthor

    阶段4 补齐:Compose 商店统一(本地 commit,未 push)

    此前阶段4 被错误 defer,现补齐。两个本地 commit(issue-2-compose-apps):09e62a4(实现) + c082998(独立安全审计驱动修复,No BLOCKER)。

    现状

    前端原请求 /store/apps、/store/version、/store/install、/store/deployments,后端只实现 /store/apps。现补齐,复用既有 edge-apiserver storeapps 协议,不新建本地 catalog、不改 edge-apiserver 仓库。

    catalog 包格式(向后兼容,不改 edge-apiserver)

    edge-apiserver 真实 ApplicationVersionSpec 当前只有 workloadConfig/helmConfig/modelConfig/values/valuesSchema/configTemplates,无 compose 字段。devbox 客户端 DTO 向后兼容读取 spec.composeTemplate(首选)/spec.compose(别名);该字段一旦 edge-apiserver 提供,Compose 安装即自动生效。当前真实 catalog 所有包均为 Kubernetes 包(provisioner=workload/helm/model),按 CEO 裁决第2条标 Kubernetes、不可本机 Compose 安装,未做 K8s→Compose 自动翻译。

    已实现

    • GET /store/version?appId=&v=:返回 valuesSchema/defaultValues/runtime/installable;compose 模板永不回传(后端持有)。
    • POST /store/install:后端从可信 catalog 重取版本 → 校验 values(对照 schema:required/type/select,拒绝未知 key)→ 安全渲染 → Controller.Apply → 202+Task。不接受前端 compose 原文冒充 catalog。
    • 安全渲染:text/template missingkey=error + 不注册任何 FuncMap(无任意函数/shell/file);secret/password 只进 .env(0600),不进 compose 正文/revision/task/audit/HTTP;模板输出 ≤1MiB,catalog 响应 ≤4MiB,URL 转义,错误体裁剪脱敏。
    • 同源 catalog app 已装时复用其 ID + expectedRevision,版本切换产生新 revision;Idempotency-Key 默认按 app+version+params+secrets 指纹(避免 secret 轮换静默失效)。
    • 前端:卡片/详情显示 Compose/Kubernetes;K8s 包显示「仅 Kubernetes 环境支持」禁部署;install 用 taskId 经 useTask 轮询;无 catalog 显示清晰空态,不用 mock;/store/deployments 已由 taskId 轮询替代。

    边界(如实)

    • 真实 edge-apiserver 线上验证:未做。本环境未配置 apiserver_url、不应随意连远程集群;故未做线上端到端。完整管道由 httptest fake catalog + stub controller 覆盖(store/render/handler 测试全绿)。
    • Compose 安装仍依赖本机 Docker(Controller.Apply 内 docker compose config strict 预检);Docker 不可用时返回 capability 错误,前端显示 failed。
    • 列表 runtime 按 provisioner label 粗判,权威值以 /store/version 为准(若 label 标 compose 但版本无模板,install 会 422 兜底,仅一次性 UX 不一致)。

    验证

    go test ./... 全绿、go vet 干净;前端 vite build 通过;eslint 较改动前 baseline 不恶化(AppStore.jsx 既有 dead-import debt 为 main 既有,非本次引入)。embed 副本 pkg/console/dist 已 make ui 同步。

  3. wuxs commented on Jul 22, 2026

    @wuxs
    CollaboratorAuthor

    阶段 4 状态更新:Compose 商店提交 09e62a4 与审查修复 c082998 已推送到 PR #3,PR head 与测试环境部署版本均为 c082998。

    • make test(race)通过,pkg/apps 覆盖率 62.6%。
    • make build-all 通过,前端嵌入资源与 Linux amd64 二进制已同步。
    • 已原子替换 192.168.1.2:/opt/devbox/devbox 并重启;服务 active、health 200、Compose runtime enabled、远端 SHA256 与本地一致。
    • 当前真实 edge-apiserver catalog 仍未提供 Compose 模板字段,因此现有包会正确显示为 Kubernetes-only;Compose 商店安装链路由 fake catalog + HTTP/controller 测试覆盖,不虚构线上包验证。

    现等待 PR review,不会自动合并。

  4. wuxs commented on Jul 22, 2026

    @wuxs
    CollaboratorAuthor

    完成性审计与最终部署已完成,PR #3 head 为 046601b。本轮补齐了 CatalogID/StoreID 持久化、崩溃恢复事实源、同 app 提交串行、默认卸载数据安全、卸载后重装、definition restore 参数语义、本机 Docker socket 边界、结构化向导、真实 Task phase、service replica 聚合、预检 network/secret/端口/路径摘要等缺口。最终 make test(race,pkg/apps 73.6%)、vet、前端 test/lint/build、build-all 全部通过;192.168.1.2 上两条真实 Docker E2E 与最终部署验证通过。PR 保持 open,等待人工 review,不会自动 merge。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions