Files
server-deploy/docs/2026-09-25-complete-optimization-proposal.md
T

21 KiB
Raw Blame History

新服务器部署与管理工具:完整优化方案

日期:2026-09-25。状态:用户已批准开始实施;选定 Traefik、本地面板、Go 执行器。不代表已实现或经过服务器验收。

1. 范围与决策状态

已确认:为新服务器设计;不做旧服务器迁移或旧脚本兼容;每个工具独立部署;公共环境可共享;Apple 风格界面;claude-dev-stack 不进入新体系。

实施默认仅在本地开发和隔离测试环境进行,不连接生产服务器,不自动提交代码。之后用户已指定新服务器并授权只读初检,记录见 target-server-preflight 文档;这不代表已授权任意写操作。2026-09-25 用户要求跨设备拉取接续,本次据此整理并提交/推送新架构交接成果。此前已删除的 claude-dev-stack/.env 保持现状。

用户在完整方案后明确要求“按照方案,开始”,因此以本方案的 Traefik + 本地面板 + Go 执行器作为实施基线。2026-09-24 的 Caddy 方案保留为历史讨论,不与本方案同时实施。

推荐组合:本地 React/TypeScript 面板 + Node API + SQLite;SSH 调用 Go deployctl;远端 Docker Compose + Traefik + systemd;应用包独立分发。首版面向单管理员、可信主机,不承诺多租户强隔离或集群高可用。

2. 现有项目的结构性问题

依据 2026-09-24 源码审查及本轮目录核对:

  • base 混合函数库和主机安装程序;应用部署可触发整机升级、Docker 配置覆盖与重启。
  • 应用运行单元基本独立,但脚本依赖相邻 base 目录;固定容器名和端口妨碍多实例。
  • 网关、证书、定时任务由多个应用脚本各自维护,资源归属不清。
  • 部分备份失败被忽略,一致性、完成标记、恢复演练与保留策略不足。
  • Gitea 的健康判断、停止写入后恢复、回滚结果判断等问题,不能作为新执行后端继续继承。
  • 版本浮动、文档漂移、实际秘密进入源码或历史,影响可复现与发布安全。

旧脚本只作为功能和故障案例来源,不包装成新管理工具的执行后端。完整重构允许按模块开发验证,但不建立长期双轨兼容系统。

3. 四个明确的职责层

3.1 控制界面

本地面板负责主机连接、应用选择、参数输入、差异展示、任务观察。面板停止不影响应用,也不终止已经提交的远端任务。

3.2 运维执行器

deployctl 是唯一业务执行入口:检查、计划、应用、备份、恢复、升级、卸载、任务核对。CLI 与面板走相同路径,不各写一套脚本。

建议 Go:远端独立二进制,无需安装 Node;进程调用、文件操作和状态机更易测试。代价是维护 Go 与 TypeScript 两套构建链,不以减少语言数量牺牲远端可靠性。

3.3 公共环境

Docker Engine/Compose、Traefik、受限 Docker API 代理、systemd 任务与计时器、必要防火墙规则。环境初始化和维护是独立操作,必须展示整机影响。

3.4 应用实例

各实例独立编排、配置、数据、备份、版本记录。私有数据库属于应用,不提升为跨应用共享环境数据库。

4. 网关、网络与证书

4.1 Traefik 候选的取舍

应用路由从包内声明生成 Compose 标签;Traefik 使用 Docker provider 动态发现。部署工具校验域名归属和标签,但不再另写一份同域名的文件路由。普通容器 HTTP 服务不发布宿主端口。

采用此方案的理由是应用包可携带完整入口声明,而非 Traefik 比 Caddy 更现代。若希望网关不访问 Docker API,则采用此前宿主 Caddy + 回环上游方案更简单。首版只实现其中一种,不做多网关切换平台。

Traefik 是独立环境 Compose 项目,静态配置变化或自身升级作为主机级维护;动态应用路由不要求重启整个网关。主机和网关故障仍可能影响全部应用 HTTP 入口。

4.2 首版网络边界

  • 一张受控共享入口网络,仅连接 Traefik 与需要公开 HTTP 的应用组件。
  • 每个应用另有私有后端网络,数据库只连接本应用后端网络、不发布宿主端口。
  • 一张专用 Docker API 管理网络,仅连接 Traefik 与 socket proxy,不发布宿主端口。
  • Traefik 设置 exposedByDefault=false,并过滤受管应用标签;显式指定上游端口和入口网络。
  • 路由、服务名称包含实例 ID,域名绑定有唯一性校验,避免跨应用覆盖。

共享入口网络上的应用可以相互连接:这是首版同一管理员、同一信任域的明确取舍,不是强隔离。若未来运行不可信应用,应使用独立主机或专门设计的隔离网络,不把 Compose 项目名当安全边界。

socket proxy 必须按实际需要限制 Docker API 路径和方法,验收确认拒绝容器创建、exec、删除等写操作。只读挂载 socket 不是 API 授权。允许读取的容器元数据仍可能泄露秘密,因此凭据尽量使用文件挂载而非标签或环境变量。

代理本身仍持有高权限 socket,必须固定版本、保护网络、审查供应链;不能描述成彻底消除了 Docker 权限风险。

4.3 证书所有权

Traefik 内置 ACME 管理入口证书:自动申请、续期和加载。默认普通子域名证书,用户预先配置 A/AAAA;部署前检查解析及验证端口,不默认索取 DNS API 密钥。

泛域名或不能使用公网端口验证时,单独启用 DNS challenge;凭据采用支持的最小权限策略。DNS 验证 TXT 与网站 A/AAAA 解析是两回事。

acme.json 放在独立持久目录、严格权限保护、加密备份;不进入 Git/日志/普通导出。单实例管理该存储,不能靠多个 Traefik 共享该文件实现高可用。

Certd 可单独部署处理额外证书用途,但不同时管理 Traefik 的证书。应用删除只移除其路由,不随意编辑共享 acme.json。

网关管理接口默认不对公网开放,通过 SSH 隧道访问。证书监控检查实际 TLS 端点而不仅是本地文件时间。

4.4 非 HTTP 入口

Gitea SSH、RustDesk TCP/UDP、Xray 独立声明端口和防火墙要求,不强制套进 HTTP 代理。Xray 保持 systemd,默认单独主机或非冲突端口;同一 IP 的 TCP 443 冲突必须拒绝,不默认设计复杂协议复用。

5. 应用包与实例模型

AppDefinition 表示应用种类,PackageRelease 表示部署包版本,Instance 表示某主机上的实际安装,不能混为一个“服务”。

应用包包含 manifest、参数 schema、Compose/systemd 模板、上游版本锁、健康规则、数据位置、一致性与升级策略声明、文件摘要及可信发布信息。

规则:

  • 不使用固定 container_name;Compose 项目名、资源名和路径由稳定实例 ID 生成。
  • 包版本、程序版本、数据库版本、镜像 digest 分别记录;发布时确认目标 CPU 架构支持。
  • 禁止 latest 作为可复现部署依据;升级前解析并锁定具体目标。
  • 数据目录不在包目录内;应用程序更新不覆盖持久数据。
  • UI 字段和可用动作从 schema/能力声明生成,不维护第二套应用清单。
  • 常规应用新增包即可接入;复杂备份或迁移需要内置适配器及测试,不允许包声明任意 root Shell 工作流。
  • 包只从受信来源发布;摘要是完整性检查,不是身份认证。
  • 面板、执行器、包分别发布,显式校验协议兼容,不隐式互相升级。

准备好公共环境后,独立应用包 + deployctl 即可通过 CLI 部署,不依赖整个仓库或面板。

6. 现有工具的目标归属

  • Gitea:应用 + 私有 MySQL 一个部署单元;数据库大版本升级单列维护计划。
  • Joplin:应用 + 私有 PostgreSQL 一个部署单元。
  • Vaultwarden:独立应用,数据库类型在包中明确;SQLite 不可直接在线复制当作可靠备份。
  • SiYuan:独立应用,数据目录与客户端写入的一致性需要专门验证。
  • RustDesk:hbbs/hbbr 同一应用单元,保存身份密钥,验证直连/中继链路。
  • Certd:可选证书工具,不是公共入口证书的第二管理者。
  • Portainer:可选高权限管理入口;其人工操作会造成漂移,不能与本工具并发修改同一实例。
  • Xray:systemd 应用适配器;内核调优、整机网络修改属于环境维护。
  • claude:开发机辅助脚本,归类到 extras/workstation,不混进服务器应用目录。
  • claude-dev-stack:排除,不恢复、不保留适配器。

支持部署不自动等于支持安全升级与恢复;每项能力完成验收后才在面板开放。

7. 操作协议与任务状态

核心流程:inspect → plan → 确认 → 锁内复核 → apply → verify。

计划绑定主机身份、实例 ID、当前状态摘要、目标包/digest、端口和域名、停机与数据影响、备份要求、失败处置、有效期和计划哈希。只读检查不修改配置或秘密。

apply 拒绝过期或发生漂移的计划。operation ID / 幂等键在远端去重,SSH 断开后先查询原任务,不自动重做升级或恢复。

任务由 systemd 服务运行,不依附 SSH 会话;记录固定执行器版本、事件序号、阶段检查点和最终结果。重启后先核对现场,禁止从任意数据库迁移步骤盲目续跑。

首版同一主机写操作串行,跨主机可并行。自动备份、CLI 和 UI 使用同一把锁。外部人工操作无法被锁约束,必须做现场复核和漂移提示。

状态至少包括排队、执行中、成功、失败已恢复、失败需处理、结果待核对、已取消。取消是受控请求,仅在安全检查点停止;不能在恢复数据库时强杀进程然后宣告“取消成功”。

SSH 启动命令使用固定入口,JSON 经 stdin/受保护文件传递,不把用户参数拼进远程 shell。子进程使用参数数组;目录校验包括归属、规范化路径和符号链接逃逸。

远端写状态是权威,本地 SQLite 是连接档案、任务索引与视图缓存。协议含版本号,日志统一脱敏,不记录秘密请求体。

8. 数据保护、备份和恢复

8.1 备份契约

先确认源实例和数据范围、可用空间与恢复所需版本;按适配器冻结全部写入方,或使用明确可证明的一致性机制。

  • Gitea:统一考虑数据库、Git 仓库、LFS、附件、配置与秘密;Git SSH/后台任务同样是写入方。仅做数据库 dump 不算完整备份。
  • Joplin:数据库及配置;如支持额外外部存储,必须纳入范围。
  • Vaultwarden:SQLite 使用受支持备份方式或停机备份,同时覆盖附件等文件。
  • SiYuan、RustDesk、Certd、Portainer:按各自数据格式设计停写和导出策略,不能套一个在线 tar。

临时输出 → 检查导出退出码和内容 → 生成 manifest/摘要 → 原子标记完成。失败备份不可选为恢复点,不触发成功备份清理。

manifest 记录源身份、时间、包与应用/数据库版本、数据范围、一致性方式、工具版本和文件摘要。区分“生成成功”“完整性检查通过”“实际恢复验证通过”。

8.2 保留与异地

建议初始策略:每日一次、保留 7 个日备份和 4 个周备份;按数据规模、变更频率与停机窗口调整。每日一次意味着潜在丢失接近一天的数据,不等于零数据损失。

至少有一份加密异机/对象存储副本;本机备份不防磁盘损坏和主机丢失。备份目标和凭据由用户配置,禁止自动采购或上传到未指定外部服务。

在确认可用替代恢复点前不得清除最后可恢复备份;升级恢复点、正在恢复或被引用备份禁止自动清理。加密密钥应有独立安全副本,否则备份可能无法恢复。

定时任务运行在远端,不依赖面板常开。远端通知使用用户配置的通道;没有通道时不能声称面板关闭仍会主动提醒。

8.3 恢复契约

默认先恢复到隔离实例验证,避免直接覆盖当前数据。原地恢复必须明确展示会覆盖备份时间之后的新增数据,并生成当前状态救援备份。

恢复前确认全部写入方确已停止;停止失败立即中止。完整恢复进入干净目标,禁止用合并解压假装精确还原。校验权限、秘密、版本兼容后启动并执行功能验证。

恢复后的邮件、Webhook、定时任务和公网入口在演练环境默认禁用,防止恢复测试触发真实外部操作。

归档摘要通过只能证明文件未变化;数据库行数不能证明业务完整。RTO 通过实际恢复计时测量后给出,不提前承诺。

9. 升级、回退与卸载

升级前检查受支持升级路径和迁移要求,下载并验证目标包/镜像,预估空间与停机时间,再备份、迁移、启动、验证。不能先停业务才发现镜像不可下载。

健康判定包含进程/容器状态、应用就绪、预期 HTTP 状态与必要功能探测;500 不是健康,200 登录页面也不等于所有功能正常。

Gitea 验收需覆盖登录、读取仓库、clone、受控测试仓库 push、附件/LFS(启用时)及重启后数据。其他应用按自身业务定义探测,不复用单一 HTTP 判断。

镜像回退、配置还原、数据恢复是三个独立能力。发生不可逆数据库迁移后不能自动降级旧镜像;自动补偿仅适用于适配器明确支持的步骤。无法证明可恢复时保持诊断并请求人工处理,不输出“已回滚成功”。

默认卸载只停止并移除实例运行资源与自有路由,保留数据和备份。清除数据是独立高风险动作,要求目标实例确认、路径归属验证及备份检查;不删除共享网关、其他实例、宿主证书或全局卷。

10. 管理工具架构与 UI

模块化单体,不拆微服务。React/TypeScript + Vite 前端,Node 本地 API,SQLite 元数据;具体运行时/依赖版本在实施时选定并锁定,完成 Windows/macOS 便携包验证。

交付形式为本地服务配合浏览器页面,不是 Electron/Tauri 桌面安装 App,也不是公网管理网站。 便携目标为解压后启动、随包携带运行时;启动器、端口/数据目录和退出行为尚待实现,不能当成现有能力。

模块边界:

  • domain:Host、AppDefinition、PackageRelease、Instance、Plan、Operation、Backup、RouteBinding。
  • application:连接、计划确认、任务核对、备份查询等用例,不执行任意 shell。
  • infrastructure:SSH/SFTP、SQLite、包仓库、凭据引用。
  • http:会话、验证、API、事件传输,不包含部署策略。
  • web:页面、表单、任务状态和设计系统。

SSH 主机指纹首次信任需确认,变化阻止连接;优先 Agent 或密钥文件引用,不保存私钥内容。API 只监听回环,校验 Host/Origin、会话与 CSRF;本地服务也不是无认证裸 API。

参考 docker-ops-panel 的连接验证、会话保护、远程任务追踪和便携分发方式,不复制其业务固定服务列表、单项目连接模型或巨型操作模块。

Apple 风格信息架构:全局服务器/任务/设置;服务器内应用/环境/入口与证书;实例内概览/配置/日志/备份/版本。添加应用是目录与参数流程,而不是额外复杂全局导航。

视觉以系统字体、留白、克制色彩、统一间距和圆角、浅深色、键盘焦点为主;日志使用高可读密度,不做满屏玻璃效果。不可计算的任务不伪造百分比;离线显示最后核对时间。

部署和恢复展示真实目标、差异、停机与数据影响。危险确认只用于真正有风险的动作;不靠频繁弹窗代替清楚的权限和资源归属。

11. 源码与服务器目录

源码:

apps/ops-panel/          本地管理产品
cmd/deployctl/           Go CLI
internal/
  planner/              计划、前提与差异
  executor/             状态机、锁、补偿
  state/                远端登记、事件与恢复核对
  runtime/              Compose、systemd、网关适配
  applications/         应用专用备份、升级和验证
  security/             路径、秘密、包验证
catalog/<app>/          清单、模板、版本锁与应用测试资料
platform/               环境定义和模板
protocol/               JSON schema 和协议兼容测试
extras/workstation/     非服务器辅助工具
scripts/                构建发布;不放第二套运维逻辑
tests/                  集成、故障注入、恢复与端到端测试
docs/                   架构决策、操作手册、恢复手册

远端:

/usr/local/bin/deployctl
/opt/server-deploy/packages/<app>/<version>/
/etc/server-deploy/instances/<id>/
/etc/server-deploy/secrets/<id>/
/var/lib/server-deploy/instances/<id>/
/var/lib/server-deploy/operations/<id>/
/var/lib/server-deploy/platform/traefik/
/var/backups/server-deploy/<instance>/<backup>/

配置、包、秘密、数据、备份、日志分别管理权限和保留策略。不把整个仓库上传到服务器。应用进程不应可修改执行器、包信任配置或操作记录。

12. 主机安全与供应链

首版只承诺一套经过端到端验收的 Linux LTS 系统和 CPU 架构,其他组合通过兼容矩阵扩展,不声称支持所有发行版。

bootstrap 是经过审查的显式管理员操作,安装可信、固定版本的执行器和环境。不把任意用户可写路径的程序加入无限 sudo;若后续实现受限运维账户,需要验证包、路径和模板不能绕过权限边界。

整机升级、Docker 重启、sysctl 和 SSH/防火墙修改单独计划。防火墙操作保留现有 SSH 通路;验证云安全组与 Docker 实际端口暴露,不能只看主机防火墙配置。

秘密不进 Git/前端存储/标签/日志;历史已泄露的有效凭据需要轮换,删除文件不等于消除泄露。密钥轮换和历史清理另行授权执行。

发布采用白名单打包、依赖锁定、构建校验、秘密扫描、受信签名或分发通道。容器尽可能降权、限制资源并配置日志轮转;具体只读根目录/能力限制按应用实测,不盲目统一启用。

13. 实施工作包与交付顺序

这是完整新架构的工程顺序,不是存量渐进迁移:

  1. 冻结三项关键选择,确定协议、清单、实例和备份契约;补全威胁模型及验收用例。
  2. 构建 deployctl、任务存储、锁、计划复核和可信分发;验证断线、重启和幂等。
  3. 实现公共环境及选定网关;验证网络权限、解析、证书续期与入口隔离。
  4. 用 Gitea 完成 CLI 部署—备份—升级—隔离恢复闭环,用 Joplin 验证第二种数据库适配。
  5. 建立本地面板与 Apple 风格设计系统,将已验证操作接入,不把 UI 作为执行逻辑试验场。
  6. 接入其余应用和 Xray,能力按实际验收逐项开放。
  7. 完成便携打包、灾难恢复、文档和全新服务器安装验收;删除旧部署实现并切换根 README,不保留旧兼容入口。

每个工作包可单独测试和审查,协议约束贯穿始终。不是先把所有旧脚本复制进新目录再统一包装。

14. 发布验收门槛

  • 单个应用包在干净主机上可独立安装;不依赖面板或相邻仓库目录。
  • 同应用两个实例并存,名称、端口、数据、域名、备份不冲突。
  • 应用 A 升级不改应用 B 数据和编排;共同环境维护明确报告共享影响。
  • 无公网数据库端口、Docker API 或未保护的网关管理接口;socket proxy 写请求确实被拒绝。
  • ACME 测试环境验证申请/续期路径,实际 TLS 握手验证加载结果;不靠频繁生产签发做测试。
  • 注入磁盘不足、备份失败、停止失败、镜像拉取失败、数据库迁移失败、恢复失败,结果真实且可核对。
  • SSH 中断/面板关闭/主机重启后不会重复执行升级,任务能重新关联或明确标记待处理。
  • 每种声明支持恢复的应用都有真实恢复演练记录,检查业务数据和身份密钥,不只检查归档。
  • 默认卸载保留数据,清理只涉及明确自有目标;操作记录和安全测试覆盖路径逃逸。
  • 本地界面访问控制、SSH 指纹、日志脱敏、包完整性与来源验证有自动测试。
  • 前端单元/E2E、协议一致性、Go 单元/集成、隔离 Linux 主机验收全部通过,并区分未测项。

不能承诺“绝不丢数据”或“所有升级自动回滚”。可以承诺的工程目标是:危险动作有明确边界、恢复点真实可用、失败不伪装成功、每项能力有可复核验收证据。

15. 官方参考

这些文档支持产品能力说明;本项目的目录、权限、流程和测试门槛是设计建议,不是已经完成的实现。