# tiup-web
**Repository Path**: fengshuhui/tiup-web
## Basic Information
- **Project Name**: tiup-web
- **Description**: tidb数据库 tiup-web可视化工具
- **Primary Language**: Python
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-09-17
- **Last Updated**: 2026-09-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# TiScope · TiDB 集群透视仪
把 `tiup`、`pd-ctl`、`cdc` 这三套命令行,变成一张**会动、能解释自己**的集群架构图。
图上每个方块都是真实集群里跑出来的节点,每一段注解都在回答「为什么会是这样」:
Region 怎么切、TiFlash 为什么是 Raft Learner、store limit 为什么是「水龙头」不是开关、
缩容为什么必须先排空再 prune。
更多截图
| PD 运维:store 列表 | TiCDC:同步任务与报错 |
| --- | --- |
|  |  |
| 关于本作品:六段技术解读 | 浅色主题 |
| --- | --- |
|  |  |
## 30 秒看懂
| | |
| ---------------- | ------------------------------------------------------------------------------------------------------------------- |
| **看得见** | 分层拓扑 + 数据流动画:SQL 层 → 调度层 → 存储层,再分叉成**列存副本**与**增量同步**两条链路,一路画到 MySQL / Kafka 下游 |
| **讲得清** | 6 段技术解读(架构 / Multi-Raft / HTAP / 调度 / TiCDC / 扩缩容),每层图旁边就地展开 |
| **用得上** | 不是截图工具。扩缩容、PD 配置与调度限速、TiCDC 同步任务管理都能在图旁边直接下发 |
| **打不开也能看** | 内置 17 节点仿真集群(含 1 个故障节点、1 个异常同步任务),点「演示模式」即可,不需要 TiDB 环境 |
## 启动
```bash
python server.py --port 8686
# 浏览器打开 http://127.0.0.1:8686
```
Windows 双击 `start.bat` 也可以。
> 服务只监听 `127.0.0.1`,请勿直接暴露到公网。
### 演示模式(没有集群也能看)
右上角点「演示模式」,或在首次引导弹窗里选「先看演示数据」:
- 会用一套内置仿真集群接管全部取数:**3 PD / 2 TiDB / 5 TiKV / 2 TiFlash / 2 TiCDC,共 17 节点**
,外加 32000 个 Region、4 个 changefeed(其中 1 个异常、1 个已暂停);
- 拦截发生在**最底层**——只替换「执行命令」「调 PD API」「调 TiCDC API」三个入口,
上层所有页面、解析、渲染逻辑与真实模式完全一致,所以演示里看到的就是真实集群里会看到的;
- 写操作(暂停 / 恢复 / 删除 changefeed、改 store limit)只改内存里的仿真状态,
不会碰任何真实环境;右上角「退出演示」或点「重置」即可还原。
> **演示模式不会动你的真实配置。** 进演示时会先把你的 `cluster / version / PD / TiCDC`
> 快照进 `demo_backup`,退出时原样还原;演示期间在「连接设置」里保存的内容会写进这份
> 快照,退出演示后生效 —— 也就是说演示里看到的 `tidb-demo` 只是临时的显示层,
> 你的生产集群信息一直在。回归测试见 `tests/demo_cfg_check.py`。
```bash
python server.py --port 8686
# 浏览器打开 http://127.0.0.1:8686
```
Windows 双击 `start.bat` 也可以。
> 默认监听 `0.0.0.0`。只想本机访问时加 `--host 127.0.0.1`;
> 放到中控机上请配防火墙或反代,不要直接暴露到公网。
## 首次使用:两个必填项
第一件事是填 **集群名** 和 **数据库版本号**。这两个值决定了所有 `tiup` 命令长什么样:
| 必填项 | 影响 |
| --- | --- |
| 集群名 | 自动代入 `tiup cluster <集群名>`,例如 `display` → `tiup cluster display tidb-prod` |
| 数据库版本号 | 决定 `ctl:<版本>` / `cdc:<版本>`,例如填 `7.5.7` → `tiup ctl:v7.5.7 pd -u ...`,不会再出现「填 7.5.7 却跑 v8.5.0」 |
打开页面时会**自动从 `tiup cluster list` 读出集群名和版本号**并预填,弹窗只是让你确认一下。
如果自动识别不到(tiup 不在 PATH、SSH 连不上等),弹窗就变成必填:
- 缺这两个值时,后端会拦住所有 `cluster` 命令(`list` / `help` 等不需要集群名的除外),
并弹出引导窗口让你补全;
- 补全后原命令会自动重试一次,不用手动再点一遍。
版本号支持 `7.5.7`、`v7.5.7`、`7.5.7-nightly` 等写法,会自动补 `v` 前缀。**兼容范围 v6.0 ~ v8.5**,
超出范围会提示但仍然按你填的保存。
填的版本和集群真实版本不一致时,页面顶部会弹提示条,可以一键「用集群版本覆盖」。
## 连接设置
右上角「连接设置」:
| 配置项 | 说明 |
| --- | --- |
| 执行通道 | `本机执行`:在跑这个工具的机器上直接调用 CLI;`SSH 远程`:通过本机 OpenSSH 连到中控机执行 |
| 集群名称 | `tiup cluster` 里的集群名 |
| PD 地址 | 如 `http://10.0.0.11:2379`。点「从集群拓扑识别」会用 `tiup cluster display` 自动填 |
| **PD 执行方式** | **`PD HTTP API 直连`(默认,推荐):直接打 `/pd/api/v1/*`,不经过 tiup,7.5 / 8.5 通用且最快**;`pd-ctl 命令行`:走 `tiup ctl:<版本>`,组件没装时会先联网下载 |
| TiCDC 服务地址 | 如 `http://10.0.0.12:8300`,`cdc cli` 用它做 `--server`;留空则回退用 PD 发现 |
| CTL 调用方式 | `tiup ctl:` 或直接调用本机二进制 |
| 集群版本 | **必填**。可从 `tiup cluster list` / `display` 自动识别,如 `v7.5.7` / `v8.5.0` |
| 审计范围 | 默认只记录**变更类**操作;勾选后只读查询也一并记录 |
| SSH 参数 | 主机/端口/用户/私钥/额外选项,建议使用免密密钥 |
点「环境探测」可以看到:tiup 版本、已安装组件清单、`pd` / `cdc` 每种命令形态逐一探测的结果
(哪个能用就用哪个)、以及 PD API 是否连通。
### 7.5 与 8.5 的兼容处理
### TiCDC 执行方式:OpenAPI 直连 vs cdc cli
TiCDC 页面顶部有一个「执行方式」下拉,也可以在「连接设置 → TiCDC 执行方式」里改:
| | OpenAPI 直连(默认,推荐) | cdc cli |
|---|---|---|
| 依赖 | 只要能访问 `http://:8300` | 需要 tiup 的 `cdc` 组件 |
| 首次使用 | 不用下载任何东西 | 要先拉约 100~300MB |
| 内网镜像缺版本 | 不受影响 | 装不上,且重试无效 |
直连走 TiCDC 自带的 OpenAPI(`/api/v1/*`,v6.1+ 提供,v6.5 / v7.5 / v8.5 路径一致),
覆盖:changefeed 列表 / 详情 / 创建 / 暂停 / 恢复 / 删除、capture、processor、owner resign。
页面会把界面上的操作翻译成对应的 HTTP 请求,也会把两种通道的返回归一化成同一张表格。
唯一要求是**先填「TiCDC 服务地址」**(连接设置里,形如 `http://10.0.0.12:8300`)。
点集群拓扑页的「自动识别」会顺带把这个地址填上。没填的话页面顶部会红字提示。
### 组件预检:别再等到超时
`tiup` 第一次用到某个组件(比如 `ctl`)时会**先联网下载约 400MB**,页面在这期间只能干等到超时,
且没有任何提示。现在:
1. 页面加载时自动跑 `tiup list --installed`(结果缓存 60 秒),算出 `pd` / `cdc` 各自需要的组件
2. 执行 `pd` / `cdc` 命令**之前**先检查,缺组件直接拦住并弹窗提示
3. 点「安装」走**后台任务**:每秒轮询,实时显示百分比、已下载/总大小、已用时和滚动日志
4. 装完自动重试原命令(重试时跳过预检,不会死循环)
5. 已有**同大版本**的组件(如需要 `ctl:v7.5.0` 但已装 `ctl:v7.5.7`)视为可用,不拦截
6. tiup 不可用(查不出清单)时不拦截,避免误报
### 6.0 ~ 8.5 的其它兼容处理
- **PD 默认走 HTTP API 直连**(`/pd/api/v1/*`),这条路径在 v6.0 / v7.5 / v8.5 上完全一致,
彻底绕开「tiup 首次要联网拉 ctl 组件 → 卡住 → 超时」的问题。PD 页面顶部可随时切回 pd-ctl。
- **PD 写入会尝试多种 body 形态**:老版本 PD 只认 `{"schedule.xxx": v}`,新版也接受
`{"schedule": {...}}` 或裸值,逐个试到成功为止。
- **pd-ctl / cdc 的命令形态会自动探测**:`tiup ctl:<版本> pd`、`tiup ctl pd`、本机 `pd-ctl`、
`tiup cdc:<版本> cli`、`tiup cdc cli`、`cdc cli` 逐个试 `--version`,选中第一个可用的并缓存。
- 万一还是超时,输出区会直接给出两个按钮:**预装 ctl 组件**(执行 `tiup install ctl:<版本>`)
和 **PD 改用 API 直连**。
## 功能模块
- **集群拓扑**(默认首页,本作品的主视觉):
- **分层架构图**:`① SQL 层 → ② 调度层 → ③ 存储层 → ④ 列存副本 / ⑤ 增量同步 → ⑥ 下游系统`,
层间用流动虚线画出数据流向,节点按角色着色、带状态圆点,异常节点整块变红
- **版式随节点数自适应**:画布宽度按「一行摆得下」算,1 个 TiFlash + 5 个 TiCDC 时
自动加宽而不是把最右边的节点切掉;节点再多就折行,层带长高而不是缩字号。
同一层的两条分支(列存 / 增量)说明**合成一行**,不会两个标签叠在一起;
地址太长时打省略号收进卡片,鼠标悬停显示完整值
- **一台机器只画一个芯片**:节点来自四个数据源(PD `members` / PD `stores` /
TiCDC `captures` / `tiup cluster display`),同一台 TiCDC 天然会被报到两次。
聚合时按 `(角色, 主机)` 合并,但**端口不同不合并** —— 一台机器跑两个 TiKV 实例是合法的;
合并结果与数据源先后顺序无关,且保留 `Owner / Capture` 这种信息量更大的标注
(节点已经 Down 时才改口成 `Down`,不会让停掉的 Capture 还顶着 Owner 的帽子)
- **同步链路**:changefeed 不是集群节点,但画出来才讲得清终点,所以单独一层渲染
MySQL / Kafka 下游(密码段自动脱敏)
- **Region 分布**:每个 TiKV 的 Region 数横条对比,一眼看出数据是否倾斜
- **副本与角色分布**:TiKV 有 Leader、TiFlash 恒为 0 —— 这张表就是 Raft Learner 的证据
- **技术解读**:6 段可折叠注解就地解释「为什么是这样」
- 数据来源 `/api/topo`:真实模式聚合 PD `members` / `stores`、TiCDC `captures`、`changefeeds`
与 `tiup cluster display`;演示模式直接返回仿真数据,字段完全一致
- **集群概览**:`tiup cluster list` + `display`,组件数量、状态分布、版本信息,可开自动刷新
- **数据量统计**(看体征 · 之一):回答「我有多少数据」和「磁盘占了多少」
- **逻辑数据量 vs 物理占用**:PD 报的 `used_size` 是**单个 store 上那一份副本**的大小,
所有 store 加起来是 N 份副本的总和。所以逻辑量要按 `TiKV 用量 ÷ max-replicas + TiFlash 用量`
折算(TiFlash 是 Raft Learner,不在副本数内,必须单算),物理占用才是磁盘上真实占的量
- **Region 总量**:优先用 PD `regions/count`;老版本没有这个接口时,退回「所有 store 的
peer 数 ÷ 副本数」估算,并明确标注来源,不让读者误以为二者等价
- **分布倾斜**:Region 副本(管容量均衡)与 Leader(管读写压力)分开看。
**Region 均衡但 Leader 集中**是线上最常见的隐性热点,只看容量是发现不了的。
均值样本只取 TiKV —— 混进 TiFlash(只有几十个 Region)会把每台 TiKV 都误判成失衡
- **热点 Top N**:拉取 PD 读写热点 Region,落到具体 store 上
- **按库 / 按表的数据量**:回答「到底是哪一张表在膨胀」—— 这是上面那些集群级数字
答不了的问题,而"磁盘涨得莫名其妙"绝大多数时候就是某一张表或它的某个二级索引
- 读 `information_schema.tables`,**按大小倒序**,膨胀的那张一定在最上面;
数据与索引分开列,索引比数据还大时一眼看得出来
- 先按库汇总(条数与数据量同屏),再看某个库里最大的 20 张表
- **行数标了「估算」**:`TABLE_ROWS` 是 InnoDB 的**采样统计**(analyze 之后才更新,
常见偏差 ±20%),拿它去对账会得出"数据丢了"的结论。每张表都能点**「精算」**
跑一次 `COUNT(*)`,并把估算值、精确值、偏差百分比一起显示出来
- 系统库(`information_schema` / `performance_schema` / `metrics_schema` / `mysql` / `sys`)
不计入业务数据量
- 走独立的 SQL 通道,**它连不上不会把整页拖成空白**:面板原地给「去配置数据库连接」,
PD 那份容量与 Region 数据照常显示
- **增长趋势**:每次采样写 `data-history.json`(同一数值不重复落盘,上限 400 条,
采样同时带健康分),画出 Region 数与物理占用的长期曲线,回答「这个集群在怎么长」
- **磁盘水位预测**:对历史采样做最小二乘线性外推,回答「照当前增速还有**几天**到
80% 水位线(附预计日期)」—— 磁盘满是慢性病,扩容+排空要好几天,等红了才动手就晚了。
样本 <3 个或跨度 <1 天时**明说无法外推**而不是硬给一个数字;<7 天到水位会标红提醒。
顺带点名已越过 80% 的个别 store(集群整体水位未到不代表每台都健康)
- 数据来源 `/api/insight`,两个视图共用一次拉取,15 秒内切换不重复打 PD
- **节点健康度**(看体征 · 之二):把「通不通」升级成「为什么不好」
- **5 类检查项**:副本状态(Up / Disconnected / Down / **Tombstone 不算故障**)、磁盘余量、
版本一致性(滚动升级中途会被认出来)、进程探活、组件自身状态
- **版本一致性先归一化再比**:同一个版本在三个地方有三种写法 —— PD 写 `v7.5.7`、
store 里写 `7.5.7`、TiDB 的 `/status` 写 `8.0.11-TiDB-v7.5.7`(前面那段是 MySQL 兼容版本)。
不归一化直接比字符串,版本整齐的集群会被报成一堆「滚动升级未完成」。
监控组件(Grafana / Prometheus)用的是各自上游的版本号,不参与集群版本比较
- **`/health` 两种返回形状都认**:裸数组 `[{member_id, health}]` 与
`{"healths": [...], "total_count": 1}` 都见过;成员匹配同时支持 `member_id` 与 `host:port`,
不依赖单一字段
- **100 分制**:`notice -8`、`warn -22`、`critical` 直接归零,**`unknown` 不扣分** ——
SSH 通道下本工具常常访问不到集群内网,探不到不等于有故障
- **探活降级**:若所有节点都探不通(`net_blind`),探活类检查一律降级成 `unknown`
而非告警。宁可承认不知道,也不制造满屏假红让人从此不信这个页面
- **「未探明项」单列一份**:卡片给条数,下面逐条写出**是哪一项、为什么拿不到**。
只给一个问号等于把问题原样丢回给用户
- **集群总分 = 均值 × 0.4 + 最低分 × 0.6**:纯取平均会让「一个节点死了」被稀释,
而分布式集群恰恰是最弱一环决定可用性;纯取最低又会让「一个次要组件挂了」拖垮整体
- **「待处理问题」只统计 `warn` / `critical`**,页面提示栏写明了这一点 ——
卡片的数字和提示的口径对不上,用户就会开始怀疑整页的数据
- 每个节点可展开看到**每一条检查项的判定依据**,不是只给一个分数
- **参数调优**(知道要改什么 → 改成多少 → 怎么改,三步都在一页):
- **三层参数放一起**:PD 配置(集群怎么调度与容灾)、组件运行参数(TiKV / PD / TiFlash
单机怎么分配资源)、TiDB 系统变量(一条 SQL 怎么执行)。线上调参几乎从来不是只改一层,
拆成三个页反而对不上
- **集群画像 + 档位**:从 TiKV / PD / TiDB / TiFlash 台数、逻辑数据量、Region 总数、
副本数、磁盘水位自动判档(tiny / small / medium / large),
**两个维度取更高的一档** —— 3 台 TiKV 装了 20 TiB 的集群,压力来自数据量而不是节点数,
只按台数判会把它判成小型,调度并发就给小了。场景档(业务高峰 / 扩容追赶 / 故障恢复)
推不出来,只能手选
- **每条建议都带理由**:建议值不是「最优解」,是与该档位相称的起点。
理由写清楚是为了让你判断它适不适合你 —— 磁盘是 NVMe、有明确低峰窗口,就按自己的判断覆盖它
- **TiDB 系统变量**:内置 195 个变量的中文说明(作用、默认值、作用域、风险等级),
再叠加集群里实时读到的当前值。**字典没收录的变量也会列出来**(真实集群有 600+ 个,
只覆盖字典那部分等于没列全)。可一键 `SET GLOBAL`,也可只复制 SQL 自己去执行
- **组件运行参数**:`SHOW CONFIG` 按组件聚合并标出**实例间取值是否一致**
(一个 5 节点集群里 2 台块缓存是 8GiB、1 台是 10GiB,这种不一致必须看得见),
支持 `SET CONFIG` 在线改并持久化到 PD
- **PD 全量配置**:所有配置项都能改,改动过的行会高亮,应用前可先生成等价的
`pd-ctl config set` 命令核对;**逐项下发**而不是合成一个大请求 ——
某一项被这个版本的 PD 拒绝时,只会有那一项失败并告诉你 PD 回了什么,不会连累其它项
- **改不了的会明说**:会话级变量不给代改(改了只影响工具自己的连接,等于骗人),
PD 的启动参数(`name` / `data-dir` / `client-urls` / 证书路径)在线改会被 PD 忽略,
这里直接拦下来并提示「改 tiup 拓扑后滚动重启」,而不是假装成功
- **SQL 通道**:系统变量与组件参数走 `mysql` 客户端,跟随现有的「本机 / SSH」执行通道。
密码**只保存在服务进程内存里,不写 `config.json`**,也不用 `-p密码`(会出现在 `ps` 里),
而是走 `MYSQL_PWD`;想免密就配 `~/.my.cnf`。连不上也不影响查说明、生成 SQL
- **连不上时不再甩英文报错**:这是真机上最常撞到的一堵墙 —— 页面只弹一行
`ERROR 1045 (28000): Access denied for user 'root'@'127.0.0.1' (using password NO)`,
用户既分不清是「密码没填」还是「语句写错」,也不知道下一步干什么。
现在把 mysql 的错误码归一成 11 类(没带密码 / 密码不对 / 库名不存在 / 端口不通 /
socket 不存在 / 来源地址被拒 / 超时 / 语法错 / 版本不支持 …),换成中文结论,
并弹一个框直接给出口:**「未配置 → 是否配置数据库连接?」**
- 能靠改连接解决的才引导去改;语法错误不会被误导去改连接(改了也没用)
- 原始报错折在弹框里可展开核对 —— 结论给人看,证据留给要核对的人
- SQL 地址还指着 `127.0.0.1` 而集群在别处时,会**从 PD 地址推出一个更可能对的地址**
并给出「一键改成 x.x.x.x:4000」。这个坑看起来像密码问题,其实是连错了机器
—— `127.0.0.1` 指的是工具自己,不是 TiDB
- 顶部状态徽标同步改口为「SQL 连接失败」,不再一边失败一边显示绿色「已连接」
- 进页面自动弹,但**同一条错误只弹一次**(每刷一次弹一次的话,用户只会条件反射关掉它)
- **一键巡检报告**(健康页「导出巡检报告」):把健康度结论、待处理问题逐条、
数据量与水位预测、近 10 次采样的分数趋势拼成一份 Markdown 下载。
老板/客户问「集群最近怎么样」时,不再截一堆 Grafana 图拼邮件。
纯前端拼装(数据全部来自刚拉的体检结果),不新增后端接口 = 不新增出错面
- **健康度告警**(连接设置 → 告警推送):服务端后台线程盯健康度,配了
企业微信/钉钉群机器人 Webhook 就会把「变坏 / 升级 / 恢复」推到群里
- 只在**状态迁移**时推:变坏(ok→warn)、升级(warn→critical)、恢复(warn→ok)
各一条;持续保持坏状态不重复推 —— 刷屏的告警很快就会被人忽略
- **三种情况明确不推**:演示模式(demo 里埋的故障是演的)、PD 探不到
(探不到 ≠ 集群挂了,网络问题该修网络而不是吓人)、等级 unknown
(既不算坏也不算恢复)
- 发送失败保留旧状态自动重试;改配置即时生效,不用重启服务
- **集群概览**:`tiup cluster list` + `display`,组件数量、状态分布、版本信息,可开自动刷新
- **关于本作品**:一屏讲清它是什么、怎么读数的、安全边界在哪,以及完整的技术解读索引
- **集群与启停**:拓扑表格(节点/角色/端口/状态/目录),集群级与节点级 `start / stop / restart / reload`、`show-config`、`audit`
- **扩容 / 缩容 / 清理**(集群页顶部工具栏):
- `扩容`:填 `scale-out.yaml`(内置 TiKV / TiFlash 模板),一键写到**执行 tiup 的那台机器**上
(SSH 通道走 base64 写远端文件),确认后执行 `tiup cluster scale-out <集群名> `
- `缩容`:从最新拓扑里勾选节点,可勾 `--force`,确认后 `tiup cluster scale-in <集群名> -N host:port`
- `清理残留`:缩容后节点会变成 Tombstone,Grafana / Prometheus 里还挂着。执行
`tiup cluster prune <集群名>` 清掉,缩容成功后页面会自动提示你执行
- **PD 运维**(默认 HTTP API 直连,可切 pd-ctl):
- **Store**:容量 / 可用 / Region / Leader 统计,可查看单个 store、下线 store
- **标出 TiKV 还是 TiFlash**:TiKV 和 TiFlash **混在同一份 store 列表里**,
PD 不会直接给类型(只能靠 `labels` 里的 `engine` 或端口判断),而这个判断在前端做不了
—— 表格会把嵌套对象拍平成 `[object Object]`,所以由服务端在 `normalize_pd` 里标注。
不标出来的话,"某台 Leaders 恒为 0" 会被当成故障,其实那只是 TiFlash:
它是**列存副本(Raft Learner),只同步不投票**,Leaders 天然是 0,容量也是单独一块盘。
徽标上鼠标悬停有这句解释
- **可用比例不会再算出负数**:PD 返回的 `capacity` / `available` 是**带单位的字符串**
(`"1.719TiB"` 对 `"786.8GiB"`),直接 `parseFloat` 会得到 `1.719` 和 `786.8`
两个不同量纲的数,一相减就是 `-45671%`。现在先按单位统一折算成 GiB 再算,
徽标显示的是**「已用 N%」**(≥80% 红、≥60% 黄,到 80% PD 会开始拒绝往这台机器调度),
悬停能看到 `已用 x / y GiB` 的具体口径
- 表脚写了这两列该怎么读、两种引擎的区别
- 调度器:查看、新增、移除、按秒暂停
- Region:按 ID 查询、`topread/topwrite/topconfver/...`、副本缺失检查
- **热点**:`hot read / write / store`。PD 的输出不是一张表,这里做成了人能读的样子:
- **热点 Store**:PD 给的是「指标 → store → 速率」的**嵌套结构**,直接拍平渲染会让列名
变成 `bytes-write-rate` 这种 key。现在**转置成一行一台 store**,列名换成中文
(写入流量 / 读取流量 / 写入 Key 速率 / 读取 Key 速率),速率带单位(`MiB/s`、`keys/s`),
按首个指标倒序并配流量条 —— 谁最高一眼就见
- **热点 Region**:PD 7.x 的形态是 `as_peer` / `as_leader` 两个视角下面按 store 聚合、
明细藏在 `statistics` 数组里。这里按 store 汇总成行(热点 Region 数 / 流量合计 /
Key 速率 / Query 速率),**Region 明细折叠进 ``**,展开只挑人看得懂的列
(Region、角色 Leader/Follower/Learner、热度、流量、Key 速率),不再把 JSON 塞进单元格
- 同一个 Region 在 `as_peer` 和 `as_leader` 里都会出现,结果**按 region_id 去重**(保留流量更大的那条)
- **写热点只看 `as_leader`**(客户端写请求只发给 leader),读热点两个视角都要看
(Follower Read 会把读分到 peer 上)—— 这句区别写在面板底部
- `{"regions": [...]}` 这种老形态(PD 5.x / pd-ctl)同样认,不会在新代码里失效
- **配置与调度速度**(扩容后最常用的就在这里):
- 顶部显示副本数(`max-replicas`)等副本信息
- **调度速度预设**:停止 / 缓慢 / 标准 / 快速,一次确认整体下发
(对应 `leader-schedule-limit`、`region-schedule-limit`、`replica-schedule-limit`、
`merge-schedule-limit`、`hot-region-schedule-limit`、`max-snapshot-count`、
`max-pending-peer-count`、`store-balance-rate`)
- **全量配置编辑器**:不再写死 8 个参数,而是把 PD `/pd/api/v1/config` 返回的
**全部配置项**(演示集群实测 55 项)按分区列出,每项带中文名、说明、默认值、生效性,
可单独改、可一键填入建议值、可批量应用。
与「参数调优 → PD 全量配置」是同一个编辑器实例,两处看到的完全一致
- **Store Limit**:查看全部或单个 store 的限流值,直接设置 `rate`
(等价于 `pd-ctl store limit `)
- 底部可展开查看 PD 完整配置
- **TiCDC**(默认 OpenAPI 直连,可切 cdc cli):changefeed 列表(含 state 与 checkpoint)、
详情、暂停、恢复、删除;表单化创建;capture 与 processor 列表
- **命令控制台**:白名单内直接敲命令,支持自动/表格/JSON/文本四种输出解析;
命令类型多了 `PD API 直连` 和 `TiCDC API 直连`(直接填 `GET stores` / `GET changefeeds` 这种)
- **操作审计**:本工具所有执行记录(命令、通道、退出码、耗时),落盘到 `audit.log`;
**默认只记录变更类操作**,只读查询不入审计(可在「连接设置 → 审计范围」打开)。
集群页的「操作记录」是 `tiup cluster audit` 的历史(列表 + 点开看详情 + 关键字过滤)
## 输出配色规则
- **退出码 0 时**,`stderr` 一律按**黄色提示**渲染 —— tiup / pd-ctl 常把「新版本提醒」
「等待中」之类信息写进 stderr,这不是错误
- **退出码非 0**(含超时 -2、连不上 -1)才标红为错误,并在超时场景下给出自救按钮
## 安全设计
1. **命令白名单**:可执行程序只允许 `tiup`、`pd-ctl`、`cdc`;`tiup cluster` 与 `pd-ctl`/`cdc cli` 的子命令也各自白名单
2. **参数校验**:`shell=False` + 参数数组传递;额外拦截 `; & | $ \` < > ( )` 与换行,防止拼接注入(SSH 场景会经远程 shell)
3. **危险操作二次确认**:`start/stop/restart/reload/scale-in/scale-out/prune`、`store delete`、`scheduler add/remove/pause`、`config set`、PD 配置写入、`changefeed create/pause/resume/remove` 等会弹窗要求输入目标名(集群名 / store id / changefeed id)才执行;后端在未带 `confirm` 时直接拒绝,前端弹窗只是第一道
4. **地址规范化**:PD 地址会自动补 `http://` 与默认端口 `2379`,并校验主机名合法性,
避免出现 `-u http://0.0.0.2379` 这种无效地址
5. **只监听回环地址**,不开放外部访问(`--host` 可改,详见部署一节)
6. **审计**:变更类操作写入 `audit.log`
7. **SQL 通道只放行读与 SET**:`SHOW` / `SELECT` / `SET` / `EXPLAIN` / `DESC` / `USE`
之外的动词一律拒绝;一次只能一条语句(引号内的分号不算分隔);禁止注释
(`--` / `/*` / `#` —— 这是最常见的绕行手段);`SET` 必须是
`GLOBAL` / `SESSION` / `CONFIG` / `@@` 形态。
**这个页面的职责是调参数,不是给人一个随手能 `DROP` 表的入口** —— 想跑 DDL/DML 请去命令控制台
8. **`SET PASSWORD` 点名拦掉**:它语法上完全合法(`SET x = v`),但效果是改掉数据库账号的密码,
不属于调参数。这类"看着不像危险"的语句不能靠形态判断,只能点名
9. **不保存任何密码**:SQL 通道的密码只存在于服务进程内存,重启即失效,不写 `config.json`;
传给客户端走 `MYSQL_PWD` 环境变量而不是 `-p密码`(后者会出现在 `ps` 输出里)
## 已知边界
- `upgrade` / `destroy` / `rename` 等复杂命令建议走「命令控制台」
- SSH 通道依赖本机 `ssh` 客户端,不支持交互式密码登录
- 扩容的 `scale-out.yaml` 需要自己填(内置 TiKV / TiFlash 模板);新节点的目录、端口
要和目标机器现状一致,tiup 会在执行时校验
- PD HTTP API 直连、TiCDC OpenAPI 直连都是从**本工具所在机器**发起的;SSH 通道下若这台机器
访问不到集群内网,请把「PD 执行方式」切成 pd-ctl、「TiCDC 执行方式」切成 cdc cli
- OpenAPI 只支持 TiCDC v6.1+;更老的版本请切回 cdc cli
- 通过 OpenAPI 创建 changefeed 时,cdc cli 专属的 `--config` 文件与 GC 开关不生效
(OpenAPI 只接受 `changefeed_id` / `sink_uri` / `start_ts`),要带配置文件请用 cdc cli 模式
- 集群首次 `display` 解析依赖 tiup 的等宽表格输出;如遇格式差异会退回按空白切分
## 排错
**「unknown api: /api/probe」**
说明浏览器加载的是新版前端、但后端 `server.py` 还是旧的(两者要一起更新)。
`/api/probe`(环境检测)在新版里同时注册了 GET 和 POST,旧后端只有 POST,
所以「环境检测」按钮会 404。重新解压完整包、`./restart.sh` 重启即可。
**一直弹「安装 tiup 组件」,点了却怎么都装不成功**
先确认报的是哪一种错:
- `version v7.5.7 ... for component cdc not found: unknown version`
→ **不是网络问题,也不是命令拼错**:你用的 tiup 仓库/镜像里没有 `cdc` 的这个补丁版本。
很多内网镜像只同步了部分版本(`v7.5.6` 有、`v7.5.7` 没有很常见),重试多少次结果都一样。
现在工具会在**开下载任务之前**先 `tiup list --all cdc` 问一遍仓库,版本不存在就直接
告诉你「仓库里有哪些版本」并给出两个按钮:**改装建议版本**(如 `cdc:v7.5.6`)、
**改用 OpenAPI 直连**(对 TiCDC 来说后者完全不用下载,推荐)。
- 一直在下载、进度条不动 → 才是真的网络/镜像慢,看进度条和已用时判断。
- `command not found: tiup` → 连接设置里把 `tiup` 改成完整路径(如 `/root/.tiup/bin/tiup`)。
组件名是 `cdc` 不是 `ticdc`(`tiup list --all ticdc` 查不到是正常的)。
**页面显示「首次使用请先填写集群名和数据库版本号」**
说明后端配置里 `cluster` / `version` 是空的。正常情况打开页面就会自动识别填上;
识别不到时点弹窗里的「保存并开始」,或在「连接设置」里手填这两个值。
## 部署到 Linux 中控机
```bash
unzip -P '你的密码' tiup-web.zip # 若报 unsupported compression method 99,见下方说明
cd tiup-web
chmod +x restart.sh start.sh
./restart.sh # 默认动作 = 重启,监听 0.0.0.0:8686
```
`restart.sh` 用法(后台常驻,日志写 `server.log`,PID 写 `tiup-web.pid`):
```bash
./restart.sh # 重启(默认)
./restart.sh start # 启动
./restart.sh stop # 停止
./restart.sh status # 查看运行状态
./restart.sh log # tail -f 跟踪日志
PORT=9000 HOST=127.0.0.1 ./restart.sh start # 改端口/监听地址
```
**监听地址**:默认 `0.0.0.0`,方便直接从办公机浏览器打开。如果端口暴露面较大,建议改回
`127.0.0.1` 并走 SSH 隧道,安全性更好:
```bash
HOST=127.0.0.1 ./restart.sh start
# 本地再开一条隧道:ssh -L 8686:127.0.0.1:8686 user@中控机
```
**Python 版本**:最低 Python 3.6(CentOS 7 自带的 3.6.8 可用)。3.7+ 用 `ThreadingHTTPServer`,
3.6 会自动回退到 `socketserver.ThreadingMixIn + HTTPServer`。另外 SSH 通道在 CentOS 7 的
OpenSSH 7.4 下会自动把 `StrictHostKeyChecking=accept-new` 降级为 `no`(7.4 不认识该选项)。
> **关于压缩包加密**:打包请用 ZipCrypto(传统 PKWARE 加密)而非 WinZip AES。
> Linux 自带 `unzip` 不支持 AES,会报 `unsupported compression method 99`。
> 7-Zip 打包时加 `-mem=ZipCrypto`;或用本项目同目录的 `pack.py`(见下一节)。
> 若机器上装了 `p7zip`,也可以 `7z x -p密码 tiup-web.zip` 解 AES 包。
## 文件
```
server.py 后端:配置、执行器、白名单、解析、审计、/api/topo 拓扑聚合
demo.py 演示模式:内置 17 节点仿真集群,拦截执行/取数入口
restart.sh Linux 后台启停/重启脚本(start|stop|restart|status|log)
start.sh Linux 前台启动脚本(调试用)
start.bat Windows 启动脚本
catalog.py 参数知识库:195 个 TiDB 系统变量 + 79 项 PD 配置的中文字典、
4 个规模档 + 4 个场景档的建议值与理由、按集群画像自动判档(纯数据 + 纯函数)
public/ 前端页面(index.html / app.js / style.css)
docs/ 投稿材料:21 张截图 + demo.mp4 演示视频
+ COMMUNITY_POST.md 社区帖子全文
+ GITHUB_RELEASE.md 发版清单(含脱敏自检)
tests/ 开发与校验脚本,不进部署包
pack.py 打包脚本(ZipCrypto,Linux unzip 可直接解)
.github/workflows/ CI:后端 6 套回归(Python 3.8 / 3.12)+ 前端语法检查与 jsdom 冒烟
config.example.json 配置模板(复制为 config.json 后按需修改;config.json 含真实环境信息,已 gitignore)
config.json 连接配置(首次保存后生成,不入库)
audit.log 操作审计(自动生成)
data-history.json 数据量采样历史,「增长趋势」折线的数据源(自动生成,演示模式不写入)
```
### tests 目录说明
| 脚本 | 用途 |
| --- | --- |
| `check_ids.py` | HTML id ↔ JS 引用交叉校验、CSS class 是否定义(纯静态分析,秒级) |
| `demo_check.py` | 演示模式 39 项回归:7 个 pd-ctl 子命令 / cdc cli / PD API / TiCDC API |
| `demo_cfg_check.py` | **演示模式不许碰真实配置** 42 项回归:进演示快照 / 退出还原 / 演示中改配置 / 脏输入 / 重启安全 |
| `topo_check.py` | **真实模式** 75 项回归:mock PD / TiCDC / `tiup cluster display` 跑 `build_topo`,不需要真集群 |
| `insight_check.py` | **数据体检** 158 项回归:演示↔真实字段对称对齐、副本/逻辑量换算、评分模型、5 类检查项判定、探活降级、倾斜均值、历史落盘、演示数据自洽性、磁盘水位外推、告警判定与 webhook 发送(本机回环)、PD /health 两种返回形状、版本号归一化 |
| `tune_check.py` | **参数调优** 102 项回归:变量/PD 字典数据完整性、建议值指向真实参数、档位自动识别、SQL 白名单与注入拦截、演示模式 PD 改动真的落盘、类型强转(uint64 / bool)、启动参数被拦 |
| `smoke.js` | jsdom 无浏览器冒烟:148 项(渲染项、视图切换、主题、演示开关与配置隔离、参数调优三个页签、PD 全量编辑器、连接失败弹框与恢复、部分数据失败提示、**PD Store 表的容量换算与 TiKV/TiFlash 区分、热点 Store / 热点 Region 的可读化、按库按表数据量、库表通道失败时不影响 PD 数据**) |
| `shots.js` | 用本机 Edge 出投稿截图(含封面与各功能页) |
| `record.js` | 用本机 Edge 录制演示视频,自动加字幕条与鼠标光圈,输出 mp4 |
| `inspect.js` | 切到每个视图 dump 高度 / 元素数,用来确认页面不是空壳 |
| `shot_warn.js` | 拍「部分数据拿不到」时的页面,验证提示条出现且不遮挡内容 |
| `shot_demo_cfg.js` | 真浏览器验证「演示模式不动真实配置」:开演示 → 看表单 → 退出 → 比对还原,并出图 |
| `shot_insight.js` | 真浏览器端到端验证两个新建视图(数据量统计 / 节点健康度),检查渲染项与残留空态,出两张图 |
| `shot_pd.js` | 真浏览器验证 PD 里那两张最容易看错的表:Store 表(引擎标注、可用比例无负数、不横向溢出)与热点表(列名不是 metric key、无 `[object Object]`、有 leader/peer 双视角),出两张图 |
| `audit_all.js` | **全站低级错误横扫**:真浏览器遍历 11 个视图 + 12 个子面板,自动抓 `[object Object]` / `NaN` / `undefined` / 负百分比 / 四位以上百分比 / metric key 当表头 / 13 位以上裸数字 / 表格横向溢出 / 填了内容却还挂着「暂无数据」样式。用户连续反馈的都是同一类问题,修一处不够,得有脚本兜底 |
| `shot_tune.js` | 真浏览器**量**参数调优页的列宽:说明块必须跨满整行(≥400px)且不被竖排,出四张图 |
| `shot_sqlerr.js` | 真浏览器验证「连接失败 → 弹框问是否配置」:乱码检查、按钮文案、跳转设置框并带上上次报错,出两张图 |
| `topo_fit.js` | 真浏览器**量**拓扑图版式:层说明之间不许重叠、不许被卡片压住、卡片不许越出画布、卡片之间不许互相压、卡片里的字不许戳出边框。跑三种形状(演示 / 用户报障形状 / 12 TiKV 压力),重叠与越界这类问题 jsdom 断言不出来 |
| `shot_topo.js` | 拍拓扑图三张(暗色 / 亮色 / 1 TiFlash + 5 TiCDC),并断言每个层说明各占一行基线 |
| `fake_topo_dup.py` | 造一个**真实模式**的假集群:`build_topo` 保持原样不替换,只换掉 PD / TiCDC / tiup 三个下游入口。3 台 TiCDC 在 captures 与 display 里各出现一次 —— 用户报「只有 3 个却列了 6 个」的形状 |
| `shot_topo_dup.js` | 真浏览器验证去重:接口与页面各数一遍 TiCDC 芯片,断言 3 个、地址不带端口、标注仍是 Owner/Capture、无重复芯片,出图 |
| `video_cards.html` | 演示视频的 5 张标题卡(片头 / 痛点 / 做法 / 上手 / 片尾),与产品同一套暗色科技风 |
| `shot_cards.js` | 用本机 Edge 把上面 5 张卡渲染成 1280×720 PNG,供 ffmpeg 合成 |
| `make_video.py` | 视频合成:5 张卡 + 8 张产品截图 = 13 段,统一缩放到 1280×720 / 25fps,`drawtext`(`msyh.ttc`)+ `zoompan` 做缓慢推近,段间 `xfade` 叠化,输出 `docs/demo.mp4`。Windows 下 `drawtext` 的字幕文件路径要盘符后转正斜杠、冒号转义成 `\:`,否则报 `Unable to open` |
| `comic_panel.html` | 四格漫画的排版模板:暗色科技风 + 编号徽标 + 中文文案 + 页脚。AI 画的插画里一个汉字都不放(画中文必乱码),文案全交给它排 |
| `comic_panel.js` | 把 4 张 AI 插画拼成一张带文案的四格漫画,输出 `docs/comic-4panel.png`。**关键一步是去水印** —— 图像生成工具会在右下角烙「AI生成 / WORKBUDDY」,而活动规则要求剔除水印。水印离边缘只剩 4px 裁不掉,改用 ffmpeg `delogo` 对框内做像素插值(周围是干净的暗色渐变,填回去看不出来)。另外会自动跳过体积 <100KB 的图 —— 生成失败时模型会给一张近乎全黑的图,实测 13KB vs 正常 1.1MB |
| `secret_scan.py` | **发布前脱敏自检**:扫整个仓库,找漏出去的真实集群名 / PD 地址 / 明文密码 / SSH 私钥路径。签名不写死在脚本里,而是运行时从 `config.json` 读(CI 上没有该文件就只跑通用规则)—— 写死的话,批量替换脱敏串时会连检测器的特征一起换掉,检测器就废了 |
## 自检
```bash
python tests/check_ids.py # HTML id 与 JS 引用是否对得上
python tests/demo_check.py # 演示模式 31 项回归(不需要起服务)
python tests/demo_cfg_check.py # 演示模式 ↔ 真实配置隔离 42 项(不需要起服务)
python tests/topo_check.py # 真实模式 75 项回归(mock 集群数据,不需要真集群)
python tests/insight_check.py # 数据体检 158 项回归(含水位外推、告警、版本归一化、热点新形态解析)
python tests/tune_check.py # 参数调优 102 项回归(字典、SQL 白名单、报错归类、演示链路)
python -m py_compile server.py demo.py catalog.py pack.py # 语法检查(CentOS 7 用 python3.6 跑)
# 前端冒烟:起服务后用 jsdom 真跑一遍页面,检查渲染项 + 11 个视图切换 + 主题/演示开关
SMOKE_PORT=8686 node tests/smoke.js
# 截图 / 录屏(需要 playwright-core + 本机 Edge)
node tests/shots.js 8686
node tests/shot_insight.js 8686 # 只拍数据体检这两页
node tests/shot_tune.js 8686 # 拍参数调优三页签,并量出说明块的列宽
python tests/fake_sql_fail.py 8688 & # 造一个"SQL 一定失败"的服务
node tests/shot_sqlerr.js 8688 # 拍连接失败弹框,并验证跳转与文案
node tests/topo_fit.js 8686 # 量拓扑图版式(重叠 / 越界 / 文字戳出边框 / 芯片互压)
node tests/shot_topo.js 8686 # 拍拓扑图三张(暗色 / 亮色 / 5 个 TiCDC)
python tests/fake_topo_dup.py 8689 & # 造一个真实模式的假集群(3 台 TiCDC 两路各报一次)
node tests/shot_topo_dup.js 8689 # 验证去重:接口与页面都得是 3 个 TiCDC
node tests/record.js 8686
```
> 演示模式走的是 `demo.py` 里的假数据,测不到真实链路的解析问题;
> `topo_check.py` 专门补这一块 —— 它把 `call_pd_api` / `call_cdc_api` / `run_command`
> 换掉,直接测 `build_topo` 的聚合逻辑,PD 挂了、display 挂了、返回脏数据都覆盖了。
> 里面 `_CDC_ROWS` 那组假数据是有来历的:**tiup cluster display 的表里本来就有 TiCDC 行**,
> 早期夹具漏了这一行,于是"同一台 TiCDC 被两路各报一次"这个 bug 一直测不出来 ——
> 线上表现就是 3 台 TiCDC 在拓扑图上画成 6 个。
> `demo_cfg_check.py` 把 `CONFIG_PATH` 指到临时目录,所以随便跑它都不会动你的
> `config.json`。它守的是一条硬规矩:**演示模式只换数据源,绝不改用户的真实配置。**
> `insight_check.py` 守的是另一条:**演示数据必须与真实聚合链路逐字段对齐**(连顶层
> `unreachable` 这种字段也做对称差,只查一个方向是不行的)。演示能看、一接真实集群就报错,对这类小工具来说是最致命的事故 —— 而对黑客松作品,那是直接归零。
> `record.js` 第一次跑会提示缺 playwright 的 ffmpeg。
> 解决办法:`npm i ffmpeg-static`,再把 `node_modules/ffmpeg-static/ffmpeg.exe`
> 复制到 `%LOCALAPPDATA%\ms-playwright\ffmpeg-<版本号>\ffmpeg-win64.exe`。
## 技术栈
- **后端**:Python 标准库(无第三方依赖),兼容 Python 3.6+
- **前端**:原生 HTML / CSS / JavaScript,无构建步骤、无框架、无 CDN 依赖
- **配色**:CSS 变量 + `html[data-theme]`,深浅两套主题共用同一组语义变量
> 零依赖不是炫技。这工具的部署目标是**生产集群的中控机** —— 那种机器上装个包要
> 走审批、pip 源可能不通、Python 版本可能是 3.6。能 `python server.py` 直接跑起来,
> 比用什么框架重要得多。
## English
TiScope visualizes the `tiup` / `pd-ctl` / `cdc` CLIs as a live, self-explaining
cluster topology. Zero dependencies — Python standard library only, no build step,
no framework, no CDN.
```bash
python server.py --port 8686 # then open http://127.0.0.1:8686
# No TiDB cluster at hand? Toggle 演示模式 (Demo Mode) — a built-in 17-node
# simulated cluster, including one failed node and one failing changefeed.
```
Requires Python 3.6+ (tested on 3.8 / 3.12). Backend regressions live in `tests/*.py`;
the frontend has a browser-free jsdom smoke test (`node tests/smoke.js`) and a
real-browser audit that sweeps every view for rendering mistakes
(`node tests/audit_all.js `). See [`CHANGELOG.md`](CHANGELOG.md).
## 许可
[Apache License 2.0](LICENSE) —— 与 TiDB 生态保持一致。
> 部署到生产中控机时请只监听内网(`--host 127.0.0.1` 或配合防火墙)。
> 本工具能下发扩缩容、PD 配置、TiCDC 任务等变更操作,虽然内置了危险命令拦截
> 与二次确认,也不应暴露到公网。