下载配置文件
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_PASSWORD;existingSecret 设置时不创建 |
| 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 --set 或 helm 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 默认保留(数据不丢)。如需彻底删除数据:
bashkubectl 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 | Running 且 READY 1/1 |
STATUS 非 CrashLoopBackOff/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 校验 :
bashhelm 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,需显式指定。