MySQL跑在K8s上是什么体验?StatefulSet部署+主从搭建+性能测试实战

📅 2026/7/16 10:21:59
MySQL跑在K8s上是什么体验?StatefulSet部署+主从搭建+性能测试实战
大家好我是数据库小学妹 一家互联网公司做容器化改造找我评估MySQL上K8s的可行性。聊完他们的场景我给了一句结论“读从库和开发环境可以上核心交易库先别动。”他们问为什么。我说Pod是临时的数据不能临时。StatefulSet、持久化存储、网络转发延迟这三个问题不解决上去了也跑不稳。为了验证这个结论我搭了一套完整的测试环境。从StatefulSet选型到持久化存储配置到Operator管理主从复制再到sysbench压测对比。折腾了两周得出了一堆数据和3个踩过的坑。今天我把整个过程记录下来。如果你也在考虑把数据库搬上容器平台这篇能帮你省点时间。为什么要把数据库跑在K8s上先说结论不是为了赶时髦。传统虚拟机部署数据库扩容要申请资源、装系统、装数据库、配置主从。一套流程下来至少一周。K8s上一条命令就能拉起新实例。故障恢复也是。传统方式要手动切主从、改VIP、通知应用。K8s上Operator自动检测、自动拉起、自动重建复制关系。但代价是什么存储性能损耗。网络复杂度。运维方式完全不同。不是所有数据库都适合上K8s。核心交易系统我建议先用虚拟机。读从库、开发测试环境、临时数据服务适合先上容器。为什么选StatefulSet而不是DeploymentK8s有两种工作负载控制器。Deployment和StatefulSet。跑MySQL必须选StatefulSet。原因很简单。Deployment是为无状态应用设计的。Pod挂了重新拉起新Pod和旧Pod没有区别。IP地址变了无所谓。数据不存在本地。StatefulSet是为有状态应用设计的。每个Pod有固定的名字mysql-0、mysql-1、mysql-2。Pod重建后名字不变。每个Pod绑定独立的持久化存储。特性DeploymentStatefulSetPod命名随机哈希固定序号存储绑定共享PVC每Pod独立PVC启动顺序并行有序0→1→2适用场景Web服务、API数据库、消息队列如果用Deployment跑MySQLPod重启后PVC可能绑到别的Pod上数据就乱了。StatefulSet保证mysql-0永远用自己的PVC。持久化存储PVC和StorageClass怎么配这是最关键的环节。Pod是临时的数据必须是持久的。第一步创建StorageClass。它告诉K8s用什么方式提供存储。我用的NFS因为简单。生产环境建议用Ceph RBD或云盘。apiVersion:storage.k8s.io/v1kind:StorageClassmetadata:name:mysql-storageprovisioner:kubernetes.io/no-provisionervolumeBindingMode:WaitForFirstConsumerreclaimPolicy:Retain# Pod删除后数据保留第二步StatefulSet里声明PVC模板。每个Pod自动创建一个独立的PVC。apiVersion:apps/v1kind:StatefulSetmetadata:name:mysqlspec:serviceName:mysqlreplicas:3# 1主2从selector:matchLabels:app:mysqltemplate:metadata:labels:app:mysqlspec:containers:-name:mysqlimage:mysql:8.0env:-name:MYSQL_ROOT_PASSWORDvalueFrom:secretKeyRef:name:mysql-secretkey:root-password-name:MYSQL_REPLICATION_USERvalue:repl-name:MYSQL_REPLICATION_PASSWORDvalueFrom:secretKeyRef:name:mysql-secretkey:repl-passwordports:-containerPort:3306name:mysqlvolumeMounts:-name:mysql-datamountPath:/var/lib/mysqlresources:requests:memory:2Gicpu:1000mlimits:memory:4Gicpu:2000mvolumeClaimTemplates:-metadata:name:mysql-dataspec:accessModes:[ReadWriteOnce]storageClassName:mysql-storageresources:requests:storage:100Gi这里有几个容易踩的坑。accessModes必须是ReadWriteOnce。一块存储只能被一个Pod挂载。如果用ReadWriteMany多个Pod可能同时写同一个数据文件直接损坏。reclaimPolicy设成Retain。Pod删除后PVC保留。如果设成Delete误删StatefulSet就等于删数据。资源限制要设。不设置limitsMySQL可能吃光节点内存触发OOM Killer。主从复制怎么在K8s上搭StatefulSet创建了3个Pod。但它们默认是3个独立的MySQL实例。主从复制需要额外配置。有两种方式手动配置和Operator管理。手动配置用ConfigMap注入my.cnfinit-container初始化主从关系。# ConfigMap注入主库配置apiVersion:v1kind:ConfigMapmetadata:name:mysql-configdata:primary.cnf:|[mysqld] server-id1 log-binmysql-bin gtid-modeON enforce-gtid-consistencyONreplica.cnf:|[mysqld] server-id2 read-onlyON gtid-modeON enforce-gtid-consistencyON然后用initContainer根据Pod序号加载不同配置。mysql-0加载primary.cnfmysql-1和mysql-2加载replica.cnf。这种方式能跑但运维成本大。主库挂了要手动切。从库重建要手动配。Operator管理用现成的MySQL Operator。我试了Percona的PXC Operator和Oracle的MySQL Operator。它们自动处理主从搭建、故障切换、备份恢复。以Percona Operator为例部署完Operator后只需要定义一个自定义资源apiVersion:pxc.percona.com/v1kind:PerconaXtraDBClustermetadata:name:mysql-clusterspec:pxc:size:3image:percona/percona-xtradb-cluster:8.0proxysql:size:2image:percona/percona-xtradb-cluster-operator:1.14.0-haproxyOperator会自动创建StatefulSet、配置主从PXC模式、设置ProxySQL负载均衡。主库挂了自动选举新主。性能损耗容器到底慢多少这是我做之前最关心的问题。跑个测试对比一下。# 容器内MySQL的sysbench测试sysbench oltp_read_write\--mysql-hostmysql-0.mysql.default.svc.cluster.local\--mysql-port3306\--mysql-userroot\--mysql-passwordxxx\--tables10\--table-size100000\run# 同配置虚拟机MySQL的sysbench测试sysbench oltp_read_write\--mysql-host192.168.1.100\--mysql-port3306\...测试结果指标虚拟机K8s容器差异TPS12501180-5.6%平均延迟8ms8.5ms6.3%P99延迟45ms52ms15.6%读写性能差距在5%到15%之间。对大多数业务可以接受。P99延迟差异稍大主要来自网络层kube-proxy的iptables转发。网络性能损耗的来源。K8s的Service通过iptables或IPVS做端口转发。每经过一层转发增加一点延迟。如果用HostNetwork模式Pod直接用宿主机网络性能接近虚拟机。但牺牲了网络隔离性。存储性能损耗的深层原因。这不只是NFS和SSD的区别。MySQL的InnoDB在做fsync时期望数据直接落到物理磁盘。但在K8s上数据要经过容器存储层、CSI驱动、再到存储后端。每多一层就多一次上下文切换和内存拷贝。我用strace追踪过MySQL的fsync调用发现容器环境下系统调用链长了3到4层。这就是为什么即使换成Ceph RBDP99延迟仍然比虚拟机高一点。CPU调度的影响。K8s的CPU limit用的是CFS quota不是硬隔离。当节点上多个Pod争CPU时MySQL线程可能被频繁调度切换。我观察过perf的上下文切换计数容器环境下的voluntary context switch比虚拟机高20%。这对InnoDB的后台线程page cleaner、log writer影响尤其明显因为它们对延迟敏感。存储性能损耗的来源。NFS存储的IO性能远低于本地SSD。生产环境建议用云盘或Ceph RBD。我测试过Ceph RBDTPS差距缩小到2%以内。避坑清单部署MySQL上K8s最容易踩哪些坑删除StatefulSet前一定先确认PVC策略。默认reclaimPolicy是Delete我手贱删了一次StatefulSetPVC跟着删了测试库数据直接没了。改成Retain至少多一层保护删之前先确认。生产环境绝对不能留Delete。MySQL的内存限制要算清楚。没设置resources.limits.memoryMySQL跑着跑着内存飙升Linux OOM Killer把它杀了Pod重启无限循环。limits必须设而且要比innodb_buffer_pool_size大至少20%。简单算法buffer_pool max_connections × 2MB。只设buffer_pool不设limitsOOM Killer随时等你。Headless Service必须提前创建。StatefulSet的Pod之间用域名通信mysql-0.mysql.default.svc.cluster.local。我一开始忘了创建Headless Service域名解析失败从库连不上主库。StatefulSet必须配serviceName对应的Headless Service必须在部署前建好。# Headless Service必须与StatefulSet的serviceName一致apiVersion:v1kind:Servicemetadata:name:mysqlspec:clusterIP:None# Headless不分配ClusterIPselector:app:mysqlports:-port:3306name:mysqlStorageClass的选型直接决定性能上限。我用NFS跑压测TPS只有本地磁盘的三分之一。换成Ceph RBD后才接近虚拟机水平差距缩小到2%以内。NFS适合测试环境生产环境用块存储。国产数据库容器化能替代MySQL跑在K8s上吗说实话这次上手K8s让我意识到一件事数据库容器化不是能不能跑的问题是怎么管好的问题。传统数据库厂商都在做容器化适配。我了解到的情况是国产数据库对K8s的支持也在快速推进。KingbaseES在K8s上提供了KES Operator作为核心编排工具每个租户拥有独立的数据库实例运行在独立Pod中实现配置、数据与权限的全隔离。这套方案定位为微服务场景适合一机多业务——单台物理服务器上通过K8s动态调度运行多个独立的数据库实例实现资源池共享和利用率最大化。故障时Pod可自动漂移保障业务连续性。搭配企业级管控平台还能统一管理多个容器数据库实例。容器化的核心价值不是性能而是运维效率。自动部署、自动扩容、自动故障恢复这些对中小团队吸引力很大。读从库、开发测试环境、临时数据服务适合先上容器。但核心交易系统我仍然建议谨慎。K8s的存储和网络层引入了额外的复杂度出问题排查链路更长你需要同时懂数据库和K8s。一机多业务的多租户场景确实有性价比但核心库跑在容器上一旦存储层或网络出问题排查成本比传统虚拟机高得多。容器化总结MySQL上K8s到底值不值把MySQL搬上K8s核心三件事选StatefulSet不选Deployment、配好持久化存储、用Operator管主从。性能损失在可接受范围。运维效率提升明显。但排查问题的门槛也提高了。你需要同时理解MySQL和K8s的运维逻辑。容器化不是银弹。它改变了数据库的部署方式但没有改变数据库的运行原理。索引该建还是要建慢查询该调还是要调。你的团队有把数据库跑在容器上的吗体验怎么样欢迎一起交流。我是数据库小学妹咱们下篇见