K8S 生产环境 MariaDB 存储扩容排查实战记录
一、问题背景
今天主要围绕生产环境 MariaDB 集群的存储扩容做了一轮排查和确认。当前环境中的 MariaDB 部署在 K8S 中,使用 StatefulSet 方式,每个副本都有独立的 PVC,底层存储通过 tceinf-csi-loopdevice 提供。
本次排查的直接原因主要有两个:
- 数据库当前 PVC 容量偏小,需要为后续上线预留更多空间。
- 本次上线涉及血缘关系相关需求,预计会批量写入大量数据,担心业务数据、索引以及 binlog 快速增长,导致磁盘被打满。
此外,历史上在 UAT 环境跑全量数据时,已经出现过磁盘被写满的情况,因此这次生产扩容必须提前做足准备。
二、最开始的误区:节点磁盘有空间,不代表 PVC 能扩
刚开始看节点磁盘时,发现单个节点上的磁盘使用率并不高,例如:
/data1还有大量剩余空间/data也还有很多可用空间
但从 K8S 存储池角度看,PVC 却已经接近无法继续分配。这里出现了一个典型的认知差异。
1. df -h 看到的是节点文件系统剩余空间
这是操作系统层面的真实剩余空间。
2. kubectl get lsc -A 看到的是 CSI 存储池可分配空间
这是 K8S 存储类当前还能给 PVC 分配多少容量。
也就是说:
节点磁盘还有空闲,不代表 StorageClass 对外还能继续扩容。
这类问题在本地盘、loopdevice、LVM 池化类存储里特别常见。底层盘看着很空,但因为存储池只认某个路径,比如 data1,或者现有 PVC 已经按申请容量把池子占住了,所以可分配空间仍然不足。
三、PVC 扩容前真正该看的几个关键点
这次排查过程里,最终整理出来一个比较实用的检查链路:
Pod -> PVC -> PV -> StorageClass -> 存储池可分配空间 -> 节点底层磁盘
如果要判断数据库能不能扩容,建议按下面几个层次检查。
1. 查看数据库 Pod 分布在哪些节点
先看 StatefulSet 各副本落在哪些节点:
kubectl get pod -n sso -owide | grep mariadb
重点看:
Pod 是否正常
Running每个副本在哪个节点上
三个副本是否各自绑定独立节点
因为 MariaDB 这种有状态服务,每个副本背后通常都是自己的 PVC,扩容时必须保证每个 Pod 所在节点都满足存储条件。
2. 查看 PVC 当前大小
kubectl get pvc -n sso | grep mariadb
重点确认:
当前是否
Bound容量是多少
StorageClass 是什么
例如最开始 MariaDB 每个 PVC 是 50Gi。
3. 查看 StorageClass 是否支持扩容
kubectl get sc tceinf-csi-loopdevice -o yaml
要重点确认是否有:
allowVolumeExpansion: true
如果没有这个能力,即使底层节点空间够了,PVC 也不能直接在线扩容。
4. 查看存储池剩余可分配容量
这一步是本次排查里最关键的一步:
kubectl get lsc -A
这里能看到各节点在当前存储类下,还剩多少可继续分配的空间。
最开始看到的是:
某些节点只剩十几 Gi
某些节点二十几 Gi
最多的也不到 50Gi
而目标是把每个 PVC 从 50Gi 扩到 100Gi,意味着每个节点至少还要再提供 50Gi 可分配空间。所以初始状态下显然不满足条件。
5. 查看节点底层磁盘
如果要确认客户底层是否已经扩盘,可以到节点上看:
df -TH
例如查看 /data1 挂载点容量是否已经变大。
但是这里必须明确:
底层磁盘扩好了,只是前置条件之一。
最终能不能扩 PVC,还是要回到 kubectl get lsc -A,看存储池是否同步上涨。
四、本次处理思路:先让客户扩 data1,再谈 PVC
在排查清楚之后,结论其实比较明确:
当前问题不在 K8S YAML 本身
也不在 Pod 是否正常
核心问题是
data1对应的存储池可分配容量不够
因此这次给客户的建议也比较直接:
先对三个节点的
data1分别扩容,再考虑 PVC 扩容。
这是一个很稳妥的推进方式,因为它把问题边界切得很清楚:
客户负责底层磁盘扩容
我们只关注 K8S 存储层是否具备扩容条件
后面客户完成扩容后,再看:
kubectl get lsc -A
最终三个目标节点的 AVAILABLE 都明显提升到了 120Gi+ / 140Gi+,这时才说明:
从存储池可分配空间角度,MariaDB PVC 已经具备扩容条件。
五、为什么最终把 PVC 目标容量从 100G 调整到了 120G
最开始的扩容目标是 100G,但结合本次上线内容,后面又把目标改成了 120G。
这个调整并不是拍脑袋,而是基于几个现实判断。
1. 本次上线涉及血缘关系需求
血缘类需求往往会带来大量关系数据写入,尤其是初始化阶段、批量构建阶段、批量刷关系阶段,数据增长可能是瞬时放大的。
2. 数据库写入增长不只是业务表
磁盘占用不只是业务数据本身,还包括:
表数据
索引
临时文件
binlog
relay log
慢日志 / 错误日志
更新、删除带来的额外空间膨胀
3. 已经有过 UAT 全量数据跑满磁盘的历史
这一点非常关键。既然 UAT 已经踩过坑,那生产就不能只按“理论最小值”来配。
所以把 PVC 从 100G 提高到 120G,本质上是在给上线阶段预留更大的 buffer,防止因为批量写入和 binlog 膨胀导致磁盘再次被打满。
六、MariaDB binlog 风险为什么要重点盯
这次讨论里,一个重点担忧就是:
binlog 会不会把磁盘刷满。
这个担忧是合理的,尤其是在批量数据写入场景下。
虽然当前 binlog 只保留 3 天回滚,但这并不意味着绝对安全。因为 binlog 是否会打满磁盘,取决于两个因素:
保留时间有多长
这 3 天内写入量到底有多大
如果上线当天写入非常集中,比如:
批量插入血缘关系数据
批量更新和回灌
反复补跑任务
大事务集中提交
那么即使只保留 3 天,binlog 增长也可能非常快。特别是在数据文件和 binlog 都落在同一块 PVC 上时,更容易一起把磁盘顶满。
而前面已经提到:
UAT 跑全量数据时,磁盘就是被干满的。
这其实已经说明风险是真实存在的,而不是假设。
所以这次扩容到 120G,是合理的防御性动作。当然,从长期看,仅靠扩盘不是根治,后续仍然建议结合以下几个方面一起控制风险:
binlog 保留策略
上线窗口期间磁盘监控
大批量任务执行节奏
必要时分批写入
七、进入 MariaDB Pod 的一个细节:不是“集群方式”,而是“指定 galera 容器”
今天还有一个容易混淆的点,就是进入 Pod 时执行:
kubectl exec -it -n sso mariadb-ape-mariadb-si-ss-0 -- sh
执行时发现不太对,怀疑是不是要“通过 Galera 方式进入”。
实际上这里要区分两个概念。
1. Pod 内可能有多个容器
MariaDB Galera 这类 Pod,经常不止一个容器,可能包含:
galeraexporter/metricsinit容器等
所以直接 kubectl exec 不指定容器时,可能进不到想要的数据库主容器。
正确做法通常是先看容器名:
kubectl get pod -n sso mariadb-ape-mariadb-si-ss-0 -o jsonpath='{.spec.containers[*].name}'
然后明确指定进入 galera 容器:
kubectl exec -it -n sso mariadb-ape-mariadb-si-ss-0 -c galera -- sh
2. 这不是“Galera 集群参数”,而是“指定 Pod 中的 galera 容器”
所以不是所谓的“加上 --galera 方式”,而是:
-c galera
这只是 kubectl exec 的容器选择参数。
八、Galera 集群下,哪些操作是“直接进 Pod”就行,哪些需要看集群状态
因为这个库是 MariaDB Galera 集群,所以排查时也要注意操作边界。
1. 看磁盘、看挂载、看文件大小
这类操作直接进具体 Pod 就行,因为每个 Pod 都有自己独立的 PVC。
例如:
kubectl exec -it -n sso mariadb-ape-mariadb-si-ss-0 -c galera -- df -h
kubectl exec -it -n sso mariadb-ape-mariadb-si-ss-0 -c galera -- df -h /var/lib/mysql
2. 看数据库参数
可以进入某个 Pod 后连本地数据库,查询参数:
log_binexpire_logs_daysdatadirlog_bin_basename
3. 看 Galera 集群状态
这时就要查 wsrep 相关状态变量,例如:
wsrep_cluster_sizewsrep_cluster_statuswsrep_local_state_commentwsrep_ready
这些能帮助判断当前节点是否处于 Synced 状态、集群是否处于 Primary。
所以:
看磁盘:直接进具体 Pod
看集群健康:通过 SQL 看
wsrep状态高风险变更:不能只凭“随便进了一个 Pod”就操作
九、今天这轮问题的最终结论
今天这轮问题梳理下来,可以总结成几点。
1. K8S PVC 能不能扩,不是只看节点磁盘剩余
必须同时看:
StorageClass 是否支持扩容
存储池是否还有可分配空间
节点底层盘是否已扩
2. 节点底层盘扩完,不代表 PVC 立刻能扩
必须通过:
kubectl get lsc -A
确认可分配空间已经同步增加。
3. 对 MariaDB 这类数据库,容量规划不能只按“当前数据量”来算
还必须预留:
binlog 空间
上线批量写入增长
血缘数据初始化增长
异常补跑带来的重复写入
4. 从 100G 提到 120G 是合理的
尤其是在有 UAT 跑满磁盘前车之鉴的情况下,这种预留是必要的。
5. Galera Pod 的进入方式要注意指定容器
通常应使用:
kubectl exec -it -n sso mariadb-ape-mariadb-si-ss-0 -c galera -- sh
而不是误以为需要什么特殊“集群方式”。
十、后续建议
结合今天的情况,后续可以补充几项长期措施。
1. 对 MariaDB PVC 做标准化容量基线
不要等到上线前才临时扩容,可以对数据库、ES、ClickHouse 这类有状态组件预先设定安全基线。
2. 对 binlog 增长建立监控
至少上线窗口期间,要能看到:
PVC 使用率
数据目录增长
binlog 目录增长
节点磁盘剩余空间
3. 对批量任务做节奏控制
如果血缘数据初始化量很大,建议分批执行,避免瞬时把 binlog 和数据盘一起压爆。
4. 对 UAT 跑满盘的问题做复盘
既然之前出现过,就值得把那次全量任务的写入量、binlog 增速、峰值空间占用做一次复盘,给后续容量规划提供依据。
十一、常用命令汇总
1. 查看 MariaDB Pod 和所在节点
kubectl get pod -n sso -owide | grep mariadb
2. 查看 PVC
kubectl get pvc -n sso | grep mariadb
kubectl describe pvc data-mariadb-ape-mariadb-si-ss-0 -n sso
3. 查看 StorageClass
kubectl get sc tceinf-csi-loopdevice -o yaml
4. 查看存储池
kubectl get lsc -A
5. 查看节点磁盘
df -TH
6. 进入 Galera 容器
kubectl exec -it -n sso mariadb-ape-mariadb-si-ss-0 -c galera -- sh
7. 查看容器内磁盘
kubectl exec -it -n sso mariadb-ape-mariadb-si-ss-0 -c galera -- df -h
kubectl exec -it -n sso mariadb-ape-mariadb-si-ss-0 -c galera -- df -h /var/lib/mysql
十二、总结
这次问题的核心并不在于 K8S 配置本身,而在于对数据库存储扩容链路的完整理解。真正影响 PVC 扩容成功与否的,不只是节点磁盘是否有剩余,而是要同时关注:
Pod 与 PVC 的绑定关系
StorageClass 是否支持扩容
CSI 存储池当前可分配容量
节点底层盘是否真正扩到位
数据库上线期间 binlog 与批量写入带来的空间压力
对于 MariaDB Galera 这类有状态数据库组件,扩容时更要把“底层盘、存储池、PVC、容器、数据库参数”作为一条完整链路去看,而不是只盯某一层。
本次从 100G 提升到 120G 的决策,是基于实际业务压力、历史 UAT 跑满盘经验以及 binlog 风险做出的更稳妥容量预留,整体判断是合理的。
本作品采用《CC 协议》,转载必须注明作者和本文链接
关于 LearnKu