Files
creator-hub/docs/native-browser-change-review.md
T

181 lines
22 KiB
Markdown
Raw 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.
# 浏览器环境去 Docker:变更评审
## 1. 状态与结论
- 日期:2026-09-18。
- 评审基线:已同步的 `main` / `origin/main` / `7e3808cf4ce453b3583079680a3b59ca2ed64ad4`;评审时的旧 Docker 入口仅作为历史对照,不能将本文当作真实平台能力证明。
- 评审阶段的契约已在独立实施 worktree 执行。当前状态以实施计划、验证文档和证据记录为准。
- **方向成立,但不是把 Docker 启动命令换成 Xvfb 命令即可。**必须同时替换进程生命周期、运行代次、Profile 路径、端口分配、代理接入及版本管理。
- 下文保留必要的风险、边界和历史对照;不表示真实平台、真实代理或 LAN 已全部验收。详细实现范围见[实施计划](native-browser-implementation-plan.md),验收见[验证文档](native-browser-verification.md)。
### 已确认的需求
1. 不再用 Docker 创建、启动浏览器环境;每台机器由已有 Python gateway 管理本机浏览器,Xvfb 提供虚拟显示。
2. 保留已有多 gateway 控制方式,账号/环境仍绑定指定机器。
3. 采集浏览器按任务按需启动,任务完成后清理本次创建的临时资源;失败、取消、超时及异常退出也必须处理。
4. 不删除需要保留的账号登录状态、稳定指纹、采集结果与正式素材;重启与普通浏览器升级后仍使用原 Profile、指纹及账号身份,不因采集结束关闭无关账号的长期监听。
5. 用实测比较启动耗时、内存及磁盘占用;不预先承诺节省比例。
### 本次边界
- 移除的是**浏览器运行环境对 Docker 的依赖**。PostgreSQL 或控制面的部署方式不是本次强制重写对象;联调仍优先裸启动 Go、Python、Vite。
- 不增加自动登录、浏览器池、远程桌面新协议、Redis、任务框架、自动跨机迁移或通用资源调度平台。
- 不借此次变更增加认证、网络隔离等安全策略;保留现有身份校验、凭据处理与平台写操作约束。
- 先抖音,再小红书;不能以运行环境改造为由删减 `plan01.md` 的业务范围。
## 2. 现有实现及其影响
以下是代码定位,不是对真实平台能力的背书。实施前须针对最新工作区复核。
| 现有事实/入口 | 对本次变更的影响 |
| --- | --- |
| `internal/environment/store.go``Gateway` 保存名称、Endpoint;环境仅保留 gateway 绑定 | 多机器注册与路由已存在,应复用;浏览器运行时由目标 gateway 本机配置,不新增第二套节点管理 |
| `internal/controlplane/api/environments.go``startBrowserRuntime``createGatewayRuntime``removeGatewayRuntime` 管理启停与清理 | 必须整体改造生命周期,不能仅替换 Python 中一个 Docker 调用 |
| `gatewayCreatePayload` 下发 `creatorhub-profile-{alias}` 卷名、指纹、代理和绑定版本 | 卷改成 gateway 管理的持久目录;控制面不能下发任意宿主机路径 |
| `runtimeCleanupStore` 及启动前清理逻辑已有 cleanup-pending 概念 | 保留“未清理完成不能冒充已停止”的语义,改用本机资源身份,不再依赖容器/网络 ID |
| 历史 `cmd/docker_gateway/gateway.py` 曾初始化 `DockerClient` 并读取 `DOCKER_SOCKET``BROWSER_NETWORK` | 已由 `browser_gateway/server/http.py` 的 native runtime 管理替换;当前浏览器入口不依赖 Docker。该行只保留迁移前事实 |
| 历史 `docker/browser-wrapper/docker-entrypoint.sh` 曾固定容器内 Xvfb/端口/Profile | 浏览器 wrapper 已从生产入口删除;native gateway 为每个 runtime 独立分配 display、端口和 Profile,历史行不构成当前能力证明 |
| 历史 wrapper 曾传入 `--no-sandbox` | native gateway 拒绝该参数并以非 root 用户启动;无法满足 sandbox 时直接失败,不回退旧 wrapper |
| `internal/controlplane/api/creator.go` 的采集读取已有运行账号,没有按任务自动创建 runtime;`internal/controlplane/api/creator_events.go` 独立运行监听 | 需要新增任务使用权;现有 source lease 不是浏览器使用权,直接在采集末尾调用 stop 会误停共享会话 |
| `internal/controlplane/api/creator.go` 的临时下载、`internal/controlplane/api/creator_material.go``audio.wav.tmp` 依赖正常退出删除;素材先写固定路径再提交数据库 token | 控制面本机也需要崩溃清理;旧执行可能覆盖新产物,必须先隔离执行目录与产物发布,不能只增加目录扫描 |
| `internal/creator/source_lease.go` / `content.go` 的来源 lease 为固定十分钟;部分失败收尾仍使用已取消的 context | 长任务可能重复领取,取消后仍显示 running;资源使用权必须覆盖真实执行期并有独立收尾上下文 |
| `internal/environment/store.go` 的 gateway endpoint 可更新,环境按 gateway 名称重新解析地址 | 不能把名称当稳定机器身份;原 owner 和待清理目标必须保留,禁止把仍有绑定资源的 gateway 改指另一机器 |
| `internal/environment/store.go` 的旧 `Image`/浏览器版本管理模型与前端入口 | 浏览器安装、默认运行时和路径映射必须由 gateway 宿主机负责;control-plane 不能留下版本登记、路径下发或升级入口 |
| `scripts/dev-backend.mjs` / `compose*.yaml` 现在只保留 PostgreSQLCompose)和 host-native gateway 注册 | 联调脚本和部署说明已同步;原生模式不启动 Docker browser gateway |
| `requirements-gateway.lock` 当前锁定 `websocket-client` | 优先保留已有 CDP/WebSocket 与平台适配器,不因取消 Docker 就新增 Playwright/Patchright |
## 3. 方案选择
### 3.1 显示与浏览器
| 方案 | 优点 | 缺点 | 结论 |
| --- | --- | --- | --- |
| 每个运行实例独立 Xvfb + 浏览器 | 所有权明确;远程桌面不串窗口;单任务易于关闭和清理 | 比共享 Xvfb 多一个轻量进程,需要分配 display | **推荐首版**,满足任务结束释放资源;实际开销纳入对比 |
| 每个 gateway 共用一个 Xvfb | 减少 X server 数量 | 窗口、远程桌面及故障相互影响;不能随单任务清理 | 无实测瓶颈前不采用 |
| 改成全 headless 或复用浏览器池 | 可能进一步减少开销 | 与本次 Xvfb 要求、人工登录和监听生命周期不一致 | 本次不做 |
Xvfb 只负责显示;gateway 负责启动、就绪确认、终止、回收与恢复。浏览器进程存在不等于能执行采集:至少须完成 CDP 就绪、目标页面可用、代理与实际账号身份确认。
### 3.2 进程归属与异常退出
推荐首版运行在 Linux + systemd:每个 runtime 使用独立的 systemd transient service 管理其 Xvfb、浏览器、可选 x11vnc 及子进程,使用该 runtime 的 cgroup 作为终止与观察边界。Python 通过标准库执行固定的管理命令,不实现通用进程管理平台,也不为每个进程引入第三方库。
- **推荐每个 runtime unit 独立存活,gateway 重启后按持久记录和 unit 身份重新核对。**接近当前容器可独立存活的行为,不默认把 gateway 升级变成所有账号强制断开。
- 短任务使用权必须续租,且有明确最长执行期限;gateway 未及时恢复时,runtime 自身的有界寿命也能终止短任务进程。长期监听不按短任务期限回收,但 gateway/代理中断必须可见,恢复后重新确认事件边界,不能宣称无遗漏。
- gateway、控制面重启及监听重连不得清除自动响应的事件去重记录、基线或已占用 UID 冷却;冷却到期仅允许新事件,旧事件永不重放,迟到事件默认只记录、不自动补发。按 `plan01.md` 的 AC-A9、AC-B1 验证,不能用人工逐次确认的发送测试代替自动策略验收。
- 正常停止先请求浏览器退出,给出有限等待时间,再停止整个 runtime unit;无法停止就报清理失败,不能删除仍在使用的 Profile。
- 磁盘中的运行清单记录 gateway、environment、runtime generation、任务/用途、unit 名及临时目录归属;先登记意图,再创建资源,逐步记录结果。
- gateway 重启先核对自己的 unit、运行记录及代理监听;仅重新识别完整身份匹配的受管实例,不接管任意现存 CDP。恢复失败时明确停止该 runtime 并标记待处理,不能另开同 Profile 浏览器或静默直连。
- 不能只按 PID、进程名或目录年龄清理。PID 会复用,另一台机器可能有同名环境,新一代 runtime 也可能复用端口。
- 更简单的“gateway 服务停止即杀掉全部子进程”会改变现有会话体验,因此不默认采用;若用户明确接受每次 gateway 重启中断全部会话,再缩减为该方案。
- 单纯 `subprocess` + `finally` 无法处理 gateway 被强制终止或宿主机重启;仅靠进程组也必须证明目标浏览器不会留下脱离进程组的子进程。首版使用 OS 原生进程归属,避免依赖此假设。
这是部署前置条件,不承诺本轮支持 Windows/macOS、无 systemd Linux,也不增加多个进程管理后端。实施前需确认目标机器符合这一条件。
### 3.3 多机及 gateway 运行时
- 控制面继续选中账号绑定的 gateway;gateway 只管理本机进程、目录、端口和浏览器安装。控制面自行管理它创建的素材临时文件,不能要求 gateway 清理另一机器的路径。
- gateway 需持久稳定的节点身份;显示名称、Endpoint 与节点身份分开。仍有绑定环境、运行实例或待清理资源时,不允许把 Endpoint 改指另一机器。
- runtime 身份至少包含 `(gateway, environment, generation)`;创建、查询、停止、清理及事件都核对同一身份。
- Profile 留在账号绑定机器。gateway 不可达时显示不可达/结果不明,绝不换到另一机器以空 Profile 重建。
- 首版不做跨机复制 Profile、不做共享文件系统、不做自动故障转移。人工重新绑定机器必须说明要重新登录或另行批准搬迁。
- 浏览器二进制按版本在每台 gateway 预安装一次,启动前核实版本可用;任务不得拉镜像、下载或复制整套浏览器。
- 用“浏览器版本”取代“镜像版本”,控制面保存版本标识,gateway 映射到预安装的绝对路径。路径不是用户可自由下发的执行命令。
- **必须取得现有指纹 Chromium 的原生 Linux 发行物、来源、许可证及参数支持证据。**不能偷偷换成普通 Chromium。若只有 Docker 镜像可用,应先解决发行物来源问题,不把启动时解包镜像作为新方案。
## 4. 生命周期与清理契约
### 4.1 两种资源寿命,不新增两套环境系统
| 对象 | 何时创建 | 何时回收 | 必须保留的内容 |
| --- | --- | --- | --- |
| 账号环境定义、稳定指纹、绑定 gateway | 创建账号/环境 | 用户明确删除相关业务对象 | 不因一次任务结束删除 |
| 账号 Profile | 首次人工登录/启动需要时 | 用户明确删除账号数据;普通停止不删除 | Cookie、Local Storage 等登录与身份必需状态 |
| 任务独占浏览器及 Xvfb | 采集真正开始时 | 本次采集成功、失败、取消、超时后 | 不保留活进程 |
| 匿名采集临时 Profile | 仅明确无需登录的任务需要时 | 随该任务清理 | 无需保留 |
| cache、临时下载、CDP 文件、运行目录、临时截图 | 对应 runtime/任务需要时 | runtime 停止并完成结果提交后 | 正式结果另存,不依赖这些目录 |
| 人工登录会话、持续监听浏览器 | 明确登录/开启监听 | 显式停止、登录期限结束或服务故障 | 长期监听不能被另一个短任务关闭 |
| 作品、评论、线索、审计及用户确认保留的素材 | 业务提交成功时 | 遵循业务删除操作 | 不属于运行时垃圾 |
| 浏览器安装包/共享二进制 | 运维安装版本 | 确认无环境引用后显式卸载 | 不随任务删除,也不每任务复制 |
**一个稳定账号 Profile 同时最多由一个浏览器实例使用。**采集账号与自有账号监听职责分开:优先使用已有指定采集账号,不复制活跃 Profile,不为同一账号另开浏览器。冲突明确排队或拒绝,不悄悄关闭监听。若必须使用已有长期会话,需明确标识为借用:只关闭本任务创建的页面、下载与引用,绝不销毁借入 runtime;不能将该路径伪称为任务独占浏览器已回收。
任务所属的临时资源必须全部归还,不要求把用户早已打开的登录/监听会话一并关闭。排队等待期间不能提前占用 Xvfb、浏览器或临时大文件。
### 4.2 正常与异常终态
1. 获取账号/Profile 使用权与运行代次;落盘创建意图。
2. 创建 runtime 临时目录,分配显示/端口,启动 Xvfb/浏览器,核对代理及身份。
3. 执行业务,先将结果提交到持久存储。大文件写入本次执行独有、不可覆盖的产物路径,再由数据库核对执行 token 并发布引用;旧 token 不能覆盖新文件。数据库提交结果不明时先查引用,不能猜成未发布后删除。
4. 无论业务结果如何,都使用**独立且有时限的清理上下文**释放任务资源,不能沿用已取消的请求上下文。
5. 回收顺序:停止任务及订阅/页面 → 退出浏览器及子进程 → 关闭 Xvfb/可选桌面 → 释放本次代理引用 → 删除已确认属于本代次的临时目录 → 释放锁并记账。
6. 清理失败时保存待清理清单、最后错误及重试信息;禁止只写日志然后报“已回收”。同环境新启动必须先解决旧代次冲突。
7. 重复取消、重复清理、迟到的旧代次请求必须幂等;旧请求不得结束新 runtime。
业务结果与清理结果分别记录:例如“采集成功,资源待清理”。不能因为删除临时目录失败就重复采集,更不能自动重复执行写操作;也不能因为业务失败就跳过清理。
### 4.3 磁盘增长不能只靠删除临时目录
- 尽可能把浏览器磁盘缓存、下载与临时文件指向 runtime 私有目录。`--disk-cache-dir` 不代表浏览器的全部写入都已迁移。
- 持久 Profile 只保留登录/身份相关状态;浏览器版本已验证可重建的缓存,必须等 Profile 解锁后按固定白名单清理。不凭名称猜测并删除未知目录,不删除 Cookie/Local Storage。
- 浏览器日志、截图与诊断材料必须有明确大小/数量/保留期限;不能把垃圾从 runtime 目录转移到日志目录。
- 每台 gateway 启动前检查可用磁盘和运行槽位;不足时明确拒绝/等待,不通过删除登录资料腾空间。
- 未知目录不自动 `rm -rf`。目录归属、路径边界和符号链接校验属于防止误删的必要条件,不是新增安全产品。
- 验收分别统计临时目录、Profile、诊断日志和正式素材;正式采集结果的合理增长不能算泄漏,登录 Profile 的持续无界增长也不能被忽略。
## 5. 不能漏掉的能力替代
| 风险 | 处理要求 | 放行条件 |
| --- | --- | --- |
| 容器内固定 display/5900/9222 在宿主机冲突 | 由 runtime 独占分配,优先使用操作系统/工具提供的空闲分配能力;并发就绪确认,不用“先探测再假定可用” | 同机器两个以上 runtime 不串端口/窗口 |
| 容器 ID / network ID 被业务当运行身份 | 统一改为 runtime generation 与代理绑定版本;不复用 PID 冒充稳定 ID | 迟到请求、重启、并发启动不误删新实例 |
| 代理以前依赖 Docker 网段/容器地址 | 复用 Python 代理转发;浏览器显式连接本机对应代理端点;代次核对继续保留 | A09/B03/D05 按 HTTP/HTTPS/SOCKS4/SOCKS5 及协议支持的认证配置分项记录;实际浏览器出口、认证失败、网络失败均有证据,失败不直连(AC-E3) |
| 原生版本切换重置账号资料 | 停止后执行普通升级,保留原 Profile、稳定指纹与绑定账号,不通过重建环境升级 | A02 核对实际版本已切换、原 Profile/指纹/登录身份保持(AC-E1);不含首次 Docker 切换的数据处置 |
| 把 Docker 网络隔离等价替换成浏览器 sandbox | 明确移除旧隔离描述;sandbox 不能提供原容器的网络/文件系统边界 | 不作隔离等价承诺;如仍要求系统级禁直连,需要另行确认,而不是暗加防火墙 |
| `--no-sandbox` 直接继承 | 非 root 运行,确认内核/浏览器支持正常 sandbox;不能启动就报前置条件不满足,不静默禁用 | 使用实际指纹浏览器证明 sandbox 配置有效 |
| wrapper 有 RFB 服务,但源码未证明网页远程桌面已接通 | 保留已实现的二维码/CDP 页面动作;如要求完整网页远程桌面,单独确认后使用成熟链路,不冒称已有 | 登录、二维码保持;桌面仅在批准的范围内验收 |
| gateway 消失后浏览器仍在运行 | OS 管理完整运行组;短任务有最长寿命;恢复时识别受管实例并清理失去所有者的资源 | 无永久失管进程;存活监听恢复或明确报告中断 |
| 所有采集结束都通用关闭浏览器 | 任务独占与借入长期会话明确区分;共享代理引用计数不误关 | 常驻监听、其他任务、另一机器不受清理影响 |
| 出口探测在控制面执行、gateway 代理 ready 仅证明监听 | 从实际执行 gateway 的浏览器路径验证代理和身份;控制面探测不能代表远端机器可用 | A/B 机器各自出网检查有证据,不能只复用中央检测结果 |
| 素材临时文件和旧执行产物相互覆盖 | 每个执行独有目录/不可变产物引用,发布受 token 约束;清理也校验归属 | 旧执行恢复、清理、超时不破坏新产物 |
| 多机快照串行等待拖慢续租 | 保留既有心跳,按节点隔离失败与时限;只有验证证明需要时增加有界并发,不新建调度系统 | A 不可达时 B 续租、采集和监听不受拖累 |
| UI 显示完成但后台仍占磁盘 | P2 即交付最小接口适配、启停/采集入口及业务与清理双状态,失败有明确错误;P4 完善完整管理界面 | P2 的单节点闭环可从界面操作并查看停止、待清理和失败,不依赖 P4 才能验收;清理失败不会显示为完全成功 |
## 6. 改造评审结论与放行项
**已采用:每台 gateway 管理本机独立 Xvfb/runtime,保留账号 Profile,采集结束回收任务资源,复用现有多机路由及业务层。**单节点代码、自动检查和 native gateway smoke 已完成;真实账号、代理、LAN、多节点和性能验收按验证文档分开记录。
代码实施前须确认:
- [x] 目标机器为满足 systemd、Xvfb、sandbox 条件的 Linux,原生指纹浏览器可合法部署且能力相同;已完成非 root native smoke。
- [x] 已接受删除 Docker 浏览器代码路径、镜像字段/接口及相关部署配置;开发数据可受控重建,用户数据删除仍需逐次授权。
- [x] Profile 不随采集删除、机器不自动切换、任务不得抢占人工登录/长期监听;gateway 重启按独立 runtime 核对恢复。
- [x] 本次不把完整网页远程桌面当作已有能力;若要求补齐,另列明确验收范围。
- [x] 验证文档中的资源预算已固定;性能改善和真实平台能力未以无旧基线的推测代替实测。
以上是已执行的实施放行记录;真实平台、代理、LAN 和破坏性资源用例仍需授权操作者按验证文档补齐,未执行项不视为通过。
## 7. 专项评审处理记录
本轮进行了两项独立只读代码评审:gateway/多机生命周期,以及采集/素材资源清理。它们审查的是实现与改造风险,不是新实现的真实平台证明;自动检查和 native smoke 已记录,真实手工验收仍按验证文档执行。
| 发现 | 处理 |
| --- | --- |
| gateway 和 Docker runtime 寿命不同,直接绑定父子进程会改变重启体验 | 采用独立 runtime unit,核对恢复;短任务有有界寿命 |
| Xvfb wrapper 与实际强制入口不同,远程桌面链路无完整证据 | 不冒称现有能力;原生启动链单独验证,完整桌面另行确认 |
| source lease 不能代表浏览器所有权,短任务会误停长期会话 | 明确任务自建/借入与使用权,P3 验证并发和监听 |
| 控制面也有临时文件,旧素材执行可能覆盖新产物 | 先做执行目录与不可变产物发布,再做残留清理 |
| gateway 名称/Endpoint 可变,旧清理可能指向新机器 | 固定节点身份与资源 owner,阻止有资源时更换机器 |
| 中央出口检查不能证明远端浏览器出网正确 | 从 A/B 实际执行路径分别验证,不复用中央结果冒充 |
P0 的发行物、目标机器条件、契约和资源参数已记录;性能基线以及未执行的真实平台/代理/故障测试仍全部保留为未验证。
## 8. 成熟方案参考与证据边界
- [Xvfb/xvfb-run 手册](https://manpages.debian.org/bookworm/xvfb/xvfb-run.1.en.html):虚拟显示、自动 display 分配与 X server 生命周期;不将它当浏览器业务管理器。
- [systemd.kill](https://www.freedesktop.org/software/systemd/man/latest/systemd.kill.html)、[systemd-run](https://www.freedesktop.org/software/systemd/man/latest/systemd-run.html):使用系统已有的进程归属和终止机制。
- [Playwright persistent context 文档](https://playwright.dev/python/docs/api/class-browsertype#browser-type-launch-persistent-context):持久 user-data-dir 独占与上下文寿命的成熟约定;引用约定不代表引入该依赖。
- [Chromium Linux sandbox](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/linux/sandboxing.md):sandbox 是浏览器防护机制,不等于 Docker 环境隔离。
本轮取得检索结果,但直接抓取上述页面被当前工具的代理地址校验拦截;不将未取得的全文或发行版行为当作已验证证据。实施 P0 须用目标机器的手册、浏览器文档与小规模验证补齐。这里只引用文档,不复用上游代码;若后续复制脚本/发行物,必须记录具体来源、版本和许可证。