使用helm在k8s集群部署MySQL单实例,支持当前所有主版本的最新子版本

下载配置文件

https://atomgit.com/zhudev2026/mysql-deploy/tree/main/kubernetes/deploy/v2/mysql-chart

MySQL Helm Chart

一个使用workbuddy+hy3开发的 MySQL 部署 Chart,基于 StatefulSet + headless Service + 可选 NodePort Service 实现。

本 Chart 是 v1.yaml(裸 K8s 清单)的 Helm 化改造版本,零 override 的渲染结果与 v1.yaml 完全等价,同时把每一项配置都参数化,可灵活调整。

适用场景:单实例 MySQL(非 HA)、需要挂载持久化存储、并可按需对外暴露端口的场景。


一、功能及特性

特性 说明
StatefulSet 有状态部署 稳定的网络标识(serviceName 与 headless Service 对齐)、有序启停、PVC 随 Pod 持久化
持久化存储 通过 volumeClaimTemplates 自动创建 PVC,数据与 Pod 解耦,重建 Pod 不丢数据、不重新初始化
存储类可配 storage.storageClass 留空即使用集群默认 StorageClass,填入则指定
镜像可配 仓库 / 镜像名 / tag / 拉取策略分离,默认指向华为云 SWR 同步镜像
完整健康检查 startupProbe / readinessProbe / livenessProbe 全部基于 mysqladmin ping,防止初始化慢被误杀
优雅关闭 terminationGracePeriodSeconds 默认 60s,给 MySQL 充足时间落盘
容器安全加固 securityContext 禁用权限提升与特权模式(修正了 drop: ALL 导致无法降权启动的问题)
外部访问可开关 独立的 mysql-external NodePort Service,externalService.enabled 控制是否创建;nodePort 可固定或随机分配
密码双模式 引用已有 Secret / 安装时传入 / 随机生成(升级不变),避免明文落盘泄露
my.cnf 全参数暴露 字符集、连接数、文件句柄、时区等均可调
标准标签与命名 采用 Helm 标准 app.kubernetes.io/* 标签,资源名随 release 名(多实例不冲突)

二、目录结构

复制代码
mysql-chart/
├── Chart.yaml              # Chart 元信息(name / version / appVersion)
├── values.yaml             # 所有可配置项,默认值 == v1.yaml 效果
├── .helmignore
└── templates/
    ├── _helpers.tpl        # fullname / 标准 labels / selectorLabels 助手
    ├── secret.yaml         # mysql Secret(密码双模式)
    ├── configmap.yaml      # mysql ConfigMap(my.cnf)
    ├── service.yaml        # 无头 Service(StatefulSet 必需)
    ├── service-external.yaml  # NodePort Service(可开关)
    ├── statefulset.yaml    # 主资源(容器 / 探针 / 存储)
    └── NOTES.txt           # 部署后连接信息提示

渲染后会创建 5 类资源(外部 Service 关闭时为 4 类):

资源 名称(以 release=mysql 为例) 说明
Secret mysql ROOT_PASSWORDexistingSecret 设置时不创建
ConfigMap mysql my.cnf
Service mysql 无头 Service(clusterIP: None
Service mysql-external NodePort(仅 enabled: true
StatefulSet mysql 主工作负载,PVC 名为 data-mysql-0

三、配置修改说明

所有配置集中在 values.yaml默认值即与 v1.yaml 等价 。修改时推荐用 helm install --sethelm upgrade --set 传入,避免把密码等敏感信息写进文件;如需版本化管理,可维护一份自己的 values-override.yaml

配置项一览

配置路径 默认值 说明
image.registry swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io 镜像仓库地址
image.repository mysql 镜像名
image.tag 26.7.0 镜像 tag
image.pullPolicy IfNotPresent 拉取策略
replicaCount 1 副本数(单实例,非 HA)
terminationGracePeriodSeconds 60 优雅关闭宽限期(秒)
tz Asia/Shanghai 容器时区
rootPassword "" root 密码;空则随机生成(建议用 --set 传入,勿写入文件)
existingSecret.name "" 引用集群已有 Secret 的名称(优先级高于 rootPassword
existingSecret.key ROOT_PASSWORD 已有 Secret 中密码对应的 key
resources.requests.memory 1Gi 内存请求
resources.requests.cpu 500m CPU 请求
resources.limits.memory 4Gi 内存上限
resources.limits.cpu 2 CPU 上限
storage.size 10Gi PVC 容量
storage.storageClass "" 留空 = 集群默认 StorageClass
securityContext.allowPrivilegeEscalation false 禁止权限提升
securityContext.privileged false 禁止特权容器
probes.startup.failureThreshold 30 启动探针失败阈值(×period 即最长等待)
probes.startup.periodSeconds 10 启动探针间隔
probes.readiness.initialDelaySeconds 10 就绪探针初始延迟
probes.readiness.periodSeconds 5 就绪探针间隔
probes.readiness.failureThreshold 3 就绪探针失败阈值
probes.liveness.initialDelaySeconds 30 存活探针初始延迟
probes.liveness.periodSeconds 10 存活探针间隔
probes.liveness.failureThreshold 3 存活探针失败阈值
mycnf.characterSetServer utf8mb4 服务端字符集
mycnf.collationServer utf8mb4_unicode_ci 排序规则
mycnf.skipNameResolve true 跳过 DNS 反向解析(加速连接)
mycnf.maxConnections 4000 最大连接数
mycnf.openFilesLimit 65535 文件句柄上限
mycnf.defaultTimeZone +8:00 数据库时区
mycnf.raw "" 非空时【整体替换】生成的 my.cnf(见第七节)
mycnf.extra "" 非空时在默认配置后【追加】一段自定义配置
service.port 3306 无头 Service 端口
externalService.enabled true 是否创建外部 NodePort Service
externalService.type NodePort 外部 Service 类型
externalService.port 3306 Service 端口
externalService.targetPort 3306 容器目标端口
externalService.nodePort "" 留空 = 随机分配 30000--32767
extraLabels {} 附加到 Pod 的额外标签
podAnnotations {} Pod 注解
fullnameOverride "" 覆盖资源全名
nameOverride "" 覆盖资源基础名

常见修改示例

bash 复制代码
# 改镜像版本
--set image.tag=9.7.2

# 指定存储类(华为云 CCE 默认)
--set storage.storageClass=csi-disk

# 固定 NodePort 端口
--set externalService.nodePort=30306

# 关闭外部暴露(仅集群内可访问)
--set externalService.enabled=false

# 调整资源
--set resources.limits.memory=8Gi --set resources.limits.cpu=4

# 调大最大连接数
--set mycnf.maxConnections=8000

四、部署说明

前置要求

  • 已安装 Kubernetes 集群(建议 v1.20+)且 kubectl 可连通
  • 已安装 Helm 3.x
  • 集群存在可用的 StorageClass(当 storage.storageClass 留空时依赖默认 StorageClass)

安装

bash 复制代码
# 推荐:用 --set 传入 root 密码(不要写进 values.yaml)
helm install mysql ./mysql-chart -n mysql --create-namespace \
  --set rootPassword='你的强密码'
bash 复制代码
# 方式二:引用集群中已有的 Secret(最安全,不会创建新 Secret)
# 假设已有 secret 名为 my-db-secret,key 为 ROOT_PASSWORD
helm install mysql ./mysql-chart -n mysql --create-namespace \
  --set existingSecret.name=my-db-secret --set existingSecret.key=ROOT_PASSWORD
bash 复制代码
# 方式三:密码留空,chart 随机生成(升级时密码不变)
helm install mysql ./mysql-chart -n mysql --create-namespace

查看渲染结果(不实际部署)

bash 复制代码
helm template mysql ./mysql-chart -n mysql

升级

bash 复制代码
# 修改配置后升级(密码若用 --set 传入需再次带上,否则走随机/复用逻辑)
helm upgrade mysql ./mysql-chart -n mysql \
  --set rootPassword='你的强密码' \
  --set storage.storageClass=csi-disk

说明:密码管理逻辑------existingSecret 优先;否则使用 rootPassword;两者都空时,若集群中已存在该 Secret 则复用旧密码(lookup),否则随机生成 16 位。因此升级不会意外改变密码

卸载

bash 复制代码
helm uninstall mysql -n mysql

卸载 StatefulSet 和 Service,但 PVC 默认保留(数据不丢)。如需彻底删除数据:

bash 复制代码
kubectl delete pvc data-mysql-0 -n mysql   # ⚠️ 数据将永久丢失

查看部署后提示

bash 复制代码
helm get notes mysql -n mysql

五、访问测试说明

0. 查看所有部署资源的状态

部署后建议先整体核对资源是否就绪,再去做连接测试。以下命令按"从总览到明细"的顺序排列:

bash 复制代码
# (1) Helm 视角的发布状态(含资源数、最后状态、备注)
helm status mysql -n mysql

# (2) 一次性查看本 release 创建的全部资源(用标准 selector 过滤)
kubectl get all -n mysql -l app.kubernetes.io/instance=mysql

# (3) 或按资源类型分别查看(StatefulSet / Pod / Service / Secret / ConfigMap / PVC)
kubectl get sts,po,svc,secret,configmap,pvc -n mysql -l app.kubernetes.io/instance=mysql

逐项解读各资源的"健康信号":

资源 期望状态 关键检查点
StatefulSet READY 1/1 副本数与 replicaCount 一致,无反复重启
Pod RunningREADY 1/1 STATUSCrashLoopBackOff/ImagePullBackOff/Pending
无头 Service (mysql) CLUSTER-IP None 存在且 SELECTOR 命中 Pod
外部 Service (mysql-external) TYPE NodePort externalService.enabled=true 时出现
Secret Opaque 存在 ROOT_PASSWORD key 存在
ConfigMap 1 个 data my.cnf
PVC (data-mysql-0) STATUS Bound 不为 Pending(Pending 通常因存储类缺失)

补充排障命令:

bash 复制代码
# Pod 调度/启动失败的原因(Events 区最有用)
kubectl describe statefulset mysql -n mysql
kubectl describe pod mysql-0 -n mysql

# PVC 是否成功绑定
kubectl get pvc -n mysql
kubectl describe pvc data-mysql-0 -n mysql

# Pod 落在哪个节点、分配到的集群 IP
kubectl get pods -n mysql -o wide

# 持续观察(退出按 Ctrl+C)
kubectl get all -n mysql -l app.kubernetes.io/instance=mysql -w

提示:若 kubectl get all 列出的资源比预期少(例如没有 mysql-external),多半是 externalService.enabled=false 的故意行为,属正常。

1. 查看 Pod 与密码

bash 复制代码
# Pod 状态(Running 且 READY 1/1 表示就绪)
kubectl get pod -n mysql -w
kubectl logs -n mysql mysql-0 -f

# 获取 root 密码(随机生成或未用 existingSecret 时)
kubectl get secret mysql -n mysql -o jsonpath='{.data.ROOT_PASSWORD}' | base64 -d; echo

注意:MySQL 首次初始化的日志会分「初始化阶段 → 临时实例 → 常驻实例」三段,属正常现象 ,最后出现 ready for connections ... port: 3306 即表示启动成功。

2. 集群内部连接(推荐)

在集群内任意 Pod(或装有 mysql 客户端的临时 Pod)中:

bash 复制代码
mysql -h mysql.mysql.svc -P 3306 -u root -p

地址格式:<release名>.<namespace>.svc,本例即 mysql.mysql.svc

3. 集群外部连接(NodePort)

bash 复制代码
# 获取实际分配的节点端口
NODE_PORT=$(kubectl get svc mysql-external -n mysql -o jsonpath='{.spec.ports[0].nodePort}')
echo "NodePort = $NODE_PORT"

# 用任意集群节点 IP 连接
mysql -h <任意节点IP> -P $NODE_PORT -u root -p
  • 若设置了 externalService.nodePort=30306,则直接用 30306 连接,无需查询。
  • nodePort 留空,Kubernetes 会在 30000--32767 范围随机分配。
  • 使用 externalService.enabled=false 部署时不创建该 Service,只能集群内访问。

4. 快速连通性验证

bash 复制代码
kubectl exec -n mysql mysql-0 -- mysqladmin ping -h 127.0.0.1 -u root -p"$MYSQL_ROOT_PASSWORD"
# 期望输出:mysqld is alive

六、使用自定义 my.cnf

Chart 提供三种方式配置 MySQL 参数,按"与默认的差异程度"选择:

方式一:结构化参数(适合微调,默认)

直接改 mycnf.* 各项(字符集、连接数、文件句柄、时区等),渲染后生成标准 [mysqld] 配置,等价于 v1.yaml 默认效果。

方式二:整体替换 mycnf.raw(适合差异很大的新 my.cnf)

当你有一份完整、与默认差异较大的 my.cnf 时,把它整体传入,生成的默认配置会被完全忽略,ConfigMap 中只写你提供的文件内容。

推荐用 --set-file 直接读取本地文件(最干净,不把配置内联进命令行):

bash 复制代码
helm install mysql ./mysql-chart -n mysql --create-namespace \
  --set rootPassword='你的强密码' \
  --set-file mycnf.raw=./my-new.cnf

也可在 values 覆盖文件里内联(用 | 多行块):

yaml 复制代码
mycnf:
  raw: |
    [mysqld]
    server-id=10
    log-bin=mysql-bin
    innodb-buffer-pool-size=2G
    # ... 你的完整配置

方式三:追加 mycnf.extra(保留默认 + 补自定义)

想保留默认调优参数,只在后面追加一段自己的配置(如开启慢查询、主从复制等):

bash 复制代码
helm upgrade mysql ./mysql-chart -n mysql \
  --set-file mycnf.extra=./my-extra.cnf

渲染结果 = 默认结构化配置 + 你追加的段落(两段可各自带 [mysqld],MySQL 会自动合并)。

⚠️ 自定义 my.cnf 的注意事项

  • 务必保留 [mysqld] 段头 :服务端参数必须写在 [mysqld] 下,否则不生效。

  • 不要覆盖以下关键项 (会让容器起不来或探针失败):

    • datadir:镜像固定用 /var/lib/mysql(PVC 挂载点),改了会找不到数据。
    • socket / pid-file:默认 /var/run/mysqld,改错会导致启动/关闭异常。
    • user:镜像以 mysql 用户运行,改了可能无权限。
    • bind-address:默认监听 0.0.0.0。若改成 127.0.0.1 以外的单一 IP,而探针用的是 -h 127.0.0.1,会导致 startupProbe 一直失败、Pod 永远 NotReady。保持 0.0.0.0* 最稳妥。
    • skip-grant-tables:跳过权限校验,极危险,勿用。
  • 字符集/时区一致性:若应用依赖特定字符集或时区,确保新配置与建库时一致,避免乱码或时间错乱。

  • 优先级mycnf.raw 非空时整体替换(忽略 extra 与结构化参数);raw 为空时结构化参数生效,extra 再追加。

  • 先 dry-run 校验

    bash 复制代码
    helm template mysql ./mysql-chart -n mysql --set-file mycnf.raw=./my-new.cnf > /tmp/out.yaml
    # 用集群做客户端校验(不实际创建)
    kubectl apply --dry-run=client -f /tmp/out.yaml -n mysql

    部署后确认无 [ERROR]kubectl logs -n mysql mysql-0 -f,直到出现 ready for connections ... port: 3306

提示:--set-file 传入的是文件完整内容(字符串);Windows 环境下若路径用 /c/... 形式报错,改用 C:/Users/... 风格绝对路径。

七、注意事项与已知限制

  • 非高可用replicaCount: 1,无副本同步、无 PDB、无定期备份。生产环境请另行规划备份与 HA 方案。
  • 版本降级不兼容 :MySQL 仅支持同大版本内小版本降级。若 PVC 中的数据由 26.7.0 初始化,切勿用 9.7.2 等跨大版本镜像挂载,否则会报 Invalid MySQL server downgrade 而启动失败。迁移需逻辑导出(mysqldump)后导入新实例。
  • root 默认可从任意主机连接 :MySQL 官方镜像默认 MYSQL_ROOT_HOST=%,即 root@'%' 可远程登录。生产环境建议结合网络策略或收紧访问来源。
  • Secret 加密rootPassword 进入的 Secret 在 etcd 中默认仅 base64 编码(非加密)。生产集群建议开启 etcd 静态加密(如华为云 CCE 对接 KMS),并通过 Sealed Secrets / SOPS / External Secrets 管理清单中的密钥。
  • 存储类storage.storageClass 留空依赖集群默认 StorageClass;若集群无默认 SC,PVC 会一直处于 Pending,需显式指定。
相关推荐
2401_834636991 小时前
从零吃透 K8s 网络:ServiceIngressMetalLB 实操手册
网络·容器·kubernetes
灯澜忆梦4 小时前
【MySQL11】进阶篇 | 索引_#3使用规则
数据库·sql·mysql·性能优化
wWYy.5 小时前
Mysql:有哪些锁?
数据库·mysql
hey_sml5 小时前
MySQL常用操作速记:从字符集到游标全解析
java·开发语言·mysql
云技纵横5 小时前
线上接口突然超时,怎么判断卡在 Nginx、线程池、连接池还是 SQL?
后端·sql·mysql
技术长镜头6 小时前
别再死记 Record、Gap、Next-Key:沿一条 SQL 看懂 InnoDB 锁
后端·mysql
swordbob8 小时前
mysql数据库,明明有索引为啥不用
数据库·mysql
灯澜忆梦20 小时前
【MySQL10】进阶篇 | 索引_#2性能优化
数据库·sql·mysql·性能优化
不老刘21 小时前
【Java入门】Java服务部署方式全景对比:从java -jar到K8s
java·kubernetes·jar