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

300 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 新服务器部署与管理工具:完整优化方案
日期: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. 源码与服务器目录
源码:
```text
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/ 架构决策、操作手册、恢复手册
```
远端:
```text
/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. 官方参考
- [Traefik Docker provider 与安全边界](https://doc.traefik.io/traefik/reference/install-configuration/providers/docker/)
- [Traefik ACME 与自动续期](https://doc.traefik.io/traefik/reference/install-configuration/tls/certificate-resolvers/acme/)
- [Compose 项目身份](https://docs.docker.com/compose/how-tos/project-name/)
- [Caddy 候选的自动 HTTPS 能力](https://caddyserver.com/docs/automatic-https)
这些文档支持产品能力说明;本项目的目录、权限、流程和测试门槛是设计建议,不是已经完成的实现。