TiDB 集群 local-path 存储下,如何安全维护 Kubernetes 节点(PD + TiKV 踩坑实录)
环境:TiDB Operator + TidbCluster(v7.5.6),StorageClass 为 local-path(本地存储不可自动迁移),PD / TiKV 均为 3 副本
参考:本文的整体思路来自 PingCAP 官方文档《维护 TiDB 集群所在的 Kubernetes 节点》,在此基础上补充了本地存储场景下的实操细节和踩坑记录(含 PD 和 TiKV 两条完整链路)。
一、问题背景
集群使用 local-path 作为 StorageClass,PV 与节点是强绑定的,也就是官方文档里说的”节点存储不可自动迁移“的场景。这种场景下,无论是 TiKV 还是 PD,只删除 Pod 是没用的——PVC 跟着节点走,Pod 重建后大概率还会调度回同一个节点,甚至因为本地盘丢失而起不来。
本文以把调度到磁盘空间较小节点(ai-node)上的 tidb-standalone-tikv-1 迁移走为主线,同时补充了 PD Pod 的对应处理方式,因为线上运维中这两者往往是同一个节点下线场景下需要一起处理的。
二、整体思路:先判断存储能否自动迁移
官方文档把节点维护分成两大类:
| 存储类型 | TiKV 处理方式 | PD 处理方式 |
|---|---|---|
| 存储可自动迁移(如 EBS) | 只需迁移 Region Leader,再删 Pod 即可,Store 不用下线 | 只需迁移 Leader,再删 Pod 即可,Member 不用下线 |
| 存储不可自动迁移(如本地盘,本文场景) | 需要完整下线 Store(Tombstone)后再删 PVC + Pod | 需要下线 Member(pd-ctl member delete)后再删 PVC + Pod |
我们用的是 local-path,属于第二类,所以 PD 和 TiKV 都要走”先下线、等状态收敛、再解绑存储”的完整流程。
三、前置检查
在下线 TiKV 之前,必须先确认:
下线后剩余的 TiKV 数量,必须 ≥ PD 的
max-replicas(默认 3)
本次场景中集群只有 3 个 TiKV,直接下线会变成 2 个,不满足条件,常见两种处理方式:
- 先扩容到 4 个,再下线目标节点(最稳妥,生产环境推荐,也是官方文档给出的方式)
- 临时把
max-replicas改成 2(本次实际采用的方式,操作完成后必须改回来)
1 | |
四、标记节点不可调度
1 | |
先看一下这个节点上到底有哪些 PD / TiKV Pod(这也是官方文档推荐的排查方式,用 grep 过滤节点名):
1 | |
五、TiKV 迁移流程(本地存储,需完整下线 Store)
1. 驱逐 Leader
给 TiKV Pod 打上 tidb.pingcap.com/evict-leader annotation,触发 Region Leader 迁移:
1 | |
确认 leaderCount 降为 0。这里有个小坑:status.tikv.stores 在 TidbCluster CRD 里是一个以 store ID 为 key 的 map,不是数组,用 kubectl jsonpath 遍历时要用 .* 而不是 [*];官方文档里用的是 jq,两种写法都贴一下:
1 | |
2. 下线指定 Store
先查 store-id:
1 | |
确认后下线(本例 store-id 为 1):
1 | |
此时 store 状态变为 Offline,PD 开始把上面的 Region 迁走。
3. 加速 Region 迁移(可选)
默认 store limit 较低时迁移会很慢,可临时调高:
1 | |
4. 等待 Store 变为 Tombstone
1 | |
重点关注 state_name 是否变成 Tombstone、region_count 是否降为 0。
🔑 这是全流程最关键的一步:千万不要提前删除 PVC。
5. 解绑 PVC 并重建 Pod
1 | |
等新 Pod 状态变为 Up,新的 store-id 会自动生成,Region Leader 也会逐步调度回来。
6. 清理 evict-leader-scheduler(容易被遗漏的一步)
官方文档特别提到,迁移完成后要记得清掉之前自动生成的 evict-leader-scheduler,否则可能残留调度策略:
1 | |
六、PD 迁移流程(本地存储,需下线 Member)
如果同一个节点上还有 PD Pod,处理逻辑和 TiKV 类似,但下线的对象是 PD Member 而不是 TiKV Store:
1 | |
注意几个和 TiKV 流程不一样的地方:
- PD 下线用的是
member delete,不是store delete;也没有 Tombstone 这种中间态,member delete之后基本立即从pd-ctl member列表消失。 - PD Leader 迁移用的是
member leader transfer,不是 TiKV 的 evict-leader annotation。 - 如果待下线 Pod 本身就是 Leader,一定要先 transfer 走,否则下线过程中可能触发一次不必要的 Leader 选举抖动。
七、踩坑记录:提前删除 PVC 导致的问题(TiKV 场景)
本次操作中,在 TiKV store 还处于 Offline、region_count 尚未降为 0 时就删除了 PVC,导致新 Pod 启动失败,报错:
1 | |
原因:旧 store(id:1)的地址仍然注册在 PD 中,新 TiKV 用相同地址注册时被 PD 拒绝。
解决方法:
1 | |
⚠️
unsafe remove-failed-stores属于强制操作,会直接丢弃尚未迁移完成的副本。本次因为已将max-replicas临时调整为 2,且集群中仍有其他健康 TiKV,风险相对可控,但生产环境使用前务必再三确认数据安全性——这一步官方文档里没有提到,是本地存储 + 提前删 PVC 组合出的额外坑,供大家避雷。
八、后续清理
1 | |
九、总结与最佳实践
| 场景 | 推荐做法 |
|---|---|
| 只有 3 个 TiKV,需要下线一个 | 优先扩容到 4 再下线;或临时改 max-replicas=2 |
| local-path 存储的 TiKV 迁移 | 必须走”驱逐 Leader → store delete → 等 Tombstone → 删 PVC → 删 Pod → 清理 evict-leader-scheduler” |
| local-path 存储的 PD 迁移 | 必须走”迁移 Leader → member delete → 确认 member 列表 → 删 PVC → 删 Pod” |
| Region 迁移过慢 | 调高 store limit(add-peer / remove-peer) |
提前删 PVC 导致 duplicated store address |
用 unsafe remove-failed-stores 强制处理,注意数据风险 |
用 jsonpath / jq 查询 status.tikv.stores |
它是 map 不是数组:jsonpath 用 .*,或直接用官方推荐的 jq |
核心原则:本地存储场景下,TiKV 靠 Store 的 Tombstone 状态、PD 靠 Member 是否从列表中消失来判断”是否可以安全删 PVC”,两者都不能提前动手;同时建议提前对目标节点执行 kubectl cordon,防止新 Pod 又被调度回去。