一、环境清单
1. 集群基础信息
| 项目 | 配置值 |
|---|---|
| 集群类型 | 单 Master + 3 Worker(1 主 3 从) |
| Kubernetes 版本 | v1.23.0 |
| 容器运行时 | Docker 26.1.4 |
| 网络插件 | Calico v3.22 |
| 操作系统 | CentOS 7.9 |
| 内核版本 | 3.10.0-1160.el7.x86_64 |
| Pod 网络段 | 10.244.0.0/16(Calico 默认) |
| Service 网络段 | 10.96.0.0/12(K8s 默认) |
| CPU | 4 核 |
| 内存 | 8G |
2. 节点信息
| 机器名称 | 机器 IP | 操作系统 | 角色 | 主要功能 |
|---|---|---|---|---|
| k8s-master | 10.132.47.60 | Centos7.9 | Master + NFS 服务器 | 集群控制面 + 提供 NFS 共享存储 |
| k8s-node1 | 10.132.47.61 | Centos7.9 | Worker | 运行应用 Pod + NFS 客户端 |
| k8s-node2 | 10.132.47.62 | Centos7.9 | Worker | 运行应用 Pod + NFS 客户端 |
| k8s-node3 | 10.132.47.63 | Centos7.9 | Worker | 运行应用 Pod + NFS 客户端 |
3. NFS 服务器配置(master 节点)
| 配置项 | 配置值 |
|---|---|
| 共享目录路径 | /data/nfs/mysql |
| 目录权限 | 777(读写执行) |
| 目录所属用户 / 组 | nfsnobody:nfsnobody |
| 共享规则(/etc/exports) | /data/nfs/mysql 10.132.47.0/24(rw,sync,no_root_squash,no_all_squash) |
| 依赖服务 | rpcbind、nfs-server(已开机自启) |
| 服务状态 | 运行中 |
4. NFS 动态供应器配置
| 配置项 | 配置值 |
|---|---|
| 供应器名称 | nfs-client-provisioner |
| 部署命名空间 | kube-system |
| 服务账户名称 | nfs-provisioner |
| 集群角色名称 | nfs-provisioner-runner |
| 集群角色绑定名称 | run-nfs-provisioner |
| 供应器镜像 | registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/nfs-subdir-external-provisioner:v4.0.2 |
| 供应器标识(PROVISIONER_NAME) | k8s-sigs.io/nfs-subdir-external-provisioner |
| 关联 NFS 服务器 IP | 10.132.47.60 |
| 关联 NFS 共享目录 | /data/nfs/mysql |
| 运行副本数 | 1 |
| 容器挂载点 | /persistentvolumes |
| 核心权限 | PV/PVC/StorageClass 操作、endpoints/leases leader 选举权限 |
5. StorageClass 配置
| 配置项 | 配置值 |
|---|---|
| StorageClass 名称 | nfs-storage |
| 关联供应器 | k8s-sigs.io/nfs-subdir-external-provisioner |
| 回收策略 | Delete(删除 PVC 时自动删除 PV) |
| 允许扩容 | 是(allowVolumeExpansion: true) |
| 绑定模式 | Immediate(立即绑定) |
| 额外参数 | archiveOnDelete: "false"(删除 PVC 不归档数据) |
| 状态 | 可用(Available) |
6. PVC 配置(测试 / 业务用)
| 配置项 | 配置值 |
|---|---|
| PVC 名称 | mysql-data-pvc |
| 所属命名空间 | default |
| 关联 StorageClass | nfs-storage |
| 访问模式 | ReadWriteMany(RWX,多节点读写) |
| 申请存储容量 | 5Gi |
| 状态 | 待绑定 / 已绑定(根据实际部署结果更新) |
| 用途 | 模拟 MySQL 持久化存储 |
7. PV 配置(动态创建)
| 配置项 | 配置值 |
|---|---|
| PV 名称 | 自动生成(格式:pvc-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) |
| 关联 PVC | default/mysql-data-pvc |
| 存储容量 | 5Gi(与 PVC 申请容量一致) |
| 访问模式 | ReadWriteMany(RWX) |
| 回收策略 | Delete(继承 StorageClass 配置) |
| 存储后端 | NFS(10.132.47.60:/data/nfs/mysql/[子目录]) |
| 动态创建触发条件 | PVC 创建后自动生成 |
二、验证相关服务
bash
# 1. 确认 NFS 动态供应器运行正常
kubectl get pods -n kube-system | grep nfs-client-provisioner
#示例
[root@k8s-master nfs]# kubectl get pods -n kube-system | grep nfs-client-provisioner
nfs-client-provisioner-5bfb45b7b8-9gbbn 1/1 Running 0 33m
------------------------------------------------------------------------------------------------------------
# 2. 确认 StorageClass 存在
kubectl get sc | grep nfs-storage
#示例
[root@k8s-master nfs]# kubectl get sc | grep nfs-storage
nfs-storage k8s-sigs.io/nfs-subdir-external-provisioner Delete Immediate true 44m
------------------------------------------------------------------------------------------------------------
# 3. 确认 NFS 服务器共享目录可访问(在任意节点执行)
showmount -e 10.132.47.60
#示例
[root@k8s-master nfs]# showmount -e 10.132.47.60
Export list for 10.132.47.60:
/data/nfs/mysql 10.132.47.60/24
------------------------------------------------------------------------------------------------------------
环境步骤参考我的文章:
1.k8s集群搭建:Centos7.9 安装K8S 1master3node-CSDN博客
2.NFS服务搭建:k8s 部署NFS和动态供应器-CSDN博客
三、创建命名空间
1. 创建命名空间 YAML
文件名:mysql-namespace.yaml
yaml
apiVersion: v1
kind: Namespace
metadata:
name: mysql-cluster # 命名空间名称,明确标识集群用途
labels:
app: mysql # 统一标签,便于资源管理
字段解释
apiVersion: v1:Namespace 属于 K8s 核心 API 组,版本为 v1kind: Namespace:资源类型为命名空间,用于隔离不同应用的资源(避免与其他应用冲突)metadata.name: mysql-cluster:命名空间名称,后续所有 MySQL 资源都部署在这里labels.app: mysql:给命名空间打标签,方便用标签筛选资源
2. 部署与验证
bash
# 部署命名空间
kubectl apply -f mysql-namespace.yaml
# 验证(看到 mysql-cluster 表示成功)
kubectl get namespaces | grep mysql-cluster
------------------------------------------------------------------------------------------------------------
#示例
[root@k8s-master mysql-1]# kubectl apply -f mysql-namespace.yaml
namespace/mysql-cluster created
[root@k8s-master mysql-1]# kubectl get namespaces | grep mysql-cluster
mysql-cluster Active 5s
四、创建 Secret 存储敏感信息
为什么用 Secret?
生产环境中,密码、密钥等敏感信息不能明文写在 YAML 里(容易泄露),Secret 是 K8s 专门用于存储敏感数据的资源,会加密存储在 ETCD 中,使用时挂载到 Pod 或作为环境变量注入,更安全。
1. 创建 Secret YAML
文件名:mysql-secret.yaml
yaml
apiVersion: v1
kind: Secret
metadata:
name: mysql-secrets # Secret 名称,后续引用
namespace: mysql-cluster # 与 MySQL 集群同命名空间
type: Opaque # 通用类型,存储键值对(非 TLS 等特殊场景)
data:
# 所有值必须是 Base64 编码(生产环境避免明文,这里提供编码方法)
root-password: U2h5c2h5NTIxNTIxIQ== # 原始值:Shyshy521521!(Base64 编码命令:echo -n "Shyshy521521!" | base64)
repl-password: U2h5c2h5NTIxNTIxIQ== # 复制用户密码(与 root 一致,简化管理)
app-password: U2h5c2h5NTIxNTIxIQ== # 业务用户密码(与 root 一致,简化管理)
字段解释
-
type: Opaque:Secret 的默认类型,适用于存储任意键值对(其他类型如kubernetes.io/tls用于存储证书) -
data:存储敏感数据的字段,所有值必须经过 Base64 编码
(避免明文泄露)
root-password:root 用户密码(MySQL 超级管理员)repl-password:复制用户密码(主从复制时从库连接主库用)app-password:业务用户密码(应用程序连接 MySQL 用)
2. 编码验证
bash
# 解码验证(输出 Shyshy521521! 表示正确)
echo -n "U2h5c2h5NTIxNTIxIQ==" | base64 -d
#示例
[root@k8s-master mysql-1]# echo -n "U2h5c2h5NTIxNTIxIQ==" | base64 -d
Shyshy521521![root@k8s-master mysql-1]#
3. 部署验证
bash
# 部署 Secret
kubectl apply -f mysql-secret.yaml
# 验证(看到 mysql-secrets 表示成功)
kubectl get secrets -n mysql-cluster
# 查看 Secret 详情(验证数据存在,会显示编码后的值)
kubectl describe secret mysql-secrets -n mysql-cluster
------------------------------------------------------------------------------------------------------------
#示例
[root@k8s-master mysql-1]# vim mysql-secret.yaml
[root@k8s-master mysql-1]# kubectl apply -f mysql-secret.yaml
secret/mysql-secrets created
[root@k8s-master mysql-1]# kubectl get secrets -n mysql-cluster
NAME TYPE DATA AGE
default-token-6nt62 kubernetes.io/service-account-token 3 4m30s
mysql-secrets Opaque 3 4s
[root@k8s-master mysql-1]# kubectl describe secret mysql-secrets -n mysql-cluster
Name: mysql-secrets
Namespace: mysql-cluster
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
repl-password: 13 bytes
root-password: 13 bytes
app-password: 13 bytes
五、创建ConfigMap 存储 MySQL 配置
为什么用 ConfigMap?
MySQL 配置(如主从复制参数、字符集、连接数等)如果硬编码到镜像中,修改时需要重新构建镜像,非常麻烦。ConfigMap 是 K8s 用于存储配置文件的资源,可动态挂载到 Pod 中,修改配置后只需重启 Pod 即可生效,无需改镜像
1. 创建 ConfigMap YAML
文件名:mysql-configmap.yaml
yaml
# 指定Kubernetes API版本,v1是核心API版本,ConfigMap属于此版本下的资源
apiVersion: v1
# 声明资源类型为ConfigMap,用于存储非加密的配置数据,供Pod挂载使用
kind: ConfigMap
# 资源的元数据信息
metadata:
# ConfigMap的名称,在所属命名空间内唯一,后续Pod可通过此名称引用配置
name: mysql-config
# 指定所属命名空间为mysql-cluster,限制配置的作用域,仅该命名空间内的资源可访问
namespace: mysql-cluster
# ConfigMap的核心数据部分,存储键值对形式的配置内容
data:
# 定义MySQL主节点的配置文件,键为master.cnf,值为具体配置内容(|表示保留换行符的多行文本)
master.cnf: |
# [mysqld]表示配置片段适用于MySQL服务器进程
[mysqld]
# 设置MySQL服务器的默认字符集为utf8mb4,支持包括emoji在内的所有Unicode字符
character-set-server=utf8mb4
# 设置服务器的默认排序规则为utf8mb4_unicode_ci,基于Unicode的排序规则,更符合多语言需求
collation-server=utf8mb4_unicode_ci
# 最大连接数限制为1000,防止过多连接导致服务器资源耗尽
max_connections=1000
# 禁用DNS反向解析,加快连接建立速度(避免连接时解析客户端主机名的耗时)
skip-name-resolve
# 设置默认存储引擎为InnoDB,支持事务、行级锁等高级特性
default-storage-engine=InnoDB
# 主节点的唯一标识ID,在主从集群中必须唯一,用于区分不同节点
server-id=1
# 启用二进制日志,日志文件前缀为mysql-bin,用于主从复制(记录所有数据修改操作)
log-bin=mysql-bin
# 二进制日志格式为ROW(行级模式),记录数据行的变更详情,复制准确性最高
binlog-format=ROW
# 允许从节点将复制的日志再写入自身的二进制日志(用于级联复制,如主->从->从架构)
log-slave-updates=1
# 二进制日志自动过期时间为7天,自动清理旧日志,避免磁盘空间耗尽
expire-logs-days=7
# InnoDB事务日志刷新策略:每次事务提交时立即将日志写入磁盘并刷新,确保数据持久性(ACID中的D)
innodb_flush_log_at_trx_commit=1
# 二进制日志同步策略:每次事务提交时立即将binlog同步到磁盘,防止服务器崩溃导致binlog丢失
sync_binlog=1
# 定义MySQL从节点的配置文件,键为slave.cnf,值为具体配置内容
slave.cnf: |
[mysqld]
# 与主节点一致,确保字符集兼容
character-set-server=utf8mb4
# 与主节点一致,确保排序规则兼容
collation-server=utf8mb4_unicode_ci
# 从节点最大连接数同样限制为1000
max_connections=1000
# 同主节点,禁用DNS解析加速连接
skip-name-resolve
# 默认存储引擎与主节点一致,确保数据格式兼容
default-storage-engine=InnoDB
# 从节点的唯一标识ID,必须与主节点及其他从节点不同
server-id=2
# 从节点同样启用二进制日志(如果需要作为其他节点的主节点,如级联复制)
log-bin=mysql-bin
# 二进制日志格式与主节点一致,确保复制兼容性
binlog-format=ROW
# 二进制日志过期时间与主节点一致
expire-logs-days=7
# 同主节点,确保从节点数据持久性
innodb_flush_log_at_trx_commit=1
# 同主节点,确保从节点binlog安全性
sync_binlog=1
# 定义中继日志文件前缀为mysql-relay-bin,用于存储从主节点复制过来的日志(从节点特有)
relay-log=mysql-relay-bin
# 同主节点,允许从节点将复制的日志写入自身binlog,支持级联复制
log-slave-updates=1
# 设置从节点为只读模式,防止直接修改从节点数据(普通用户无法写入)
read-only=1
# 强化只读模式,即使是超级用户也无法写入(进一步保证从节点数据与主节点一致)
#super-read-only=1
新:
yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
namespace: mysql-cluster
data:
master.cnf: |
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
max_connections=1000
skip-name-resolve
default-storage-engine=InnoDB
server-id=1
log-bin=mysql-bin
binlog-format=ROW
log-slave-updates=1
expire-logs-days=7
innodb_flush_log_at_trx_commit=1
sync_binlog=1
slave.cnf: |
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
max_connections=1000
skip-name-resolve
default-storage-engine=InnoDB
server-id=2
log-bin=mysql-bin
binlog-format=ROW
expire-logs-days=7
innodb_flush_log_at_trx_commit=1
sync_binlog=1
relay-log=mysql-relay-bin
log-slave-updates=1
read-only=1
#super-read-only=1
字段解释(重点)
data:存储配置文件的核心字段,key 是配置文件名(master.cnf/slave.cnf),value 是配置文件内容- 主库关键配置:
server-id=1:集群内唯一,主库必须是唯一值(从库不能重复)log-bin=mysql-bin:启用二进制日志(主从复制的基础,没有日志就无法同步)binlog-format=ROW:生产推荐模式,复制时只记录数据行变化,避免 SQL 模式不一致导致的同步失败
- 从库关键配置:
server-id=2:第二个从库需改为 3(后续部署时修改)relay-log=mysql-relay-bin:中继日志,从库先下载主库的二进制日志到中继日志,再执行中继日志中的 SQL 同步数据read-only=1+super-read-only=1:从库只读,防止业务误写(生产必开)
2. 部署与验证
bash
# 部署 ConfigMap
kubectl apply -f mysql-configmap.yaml
------------------------------------------------------------------------------------------------------------
# 验证(看到 mysql-config 表示成功)
kubectl get configmap -n mysql-cluster
#示例
[root@k8s-master mysql-1]# kubectl get configmap -n mysql-cluster
NAME DATA AGE
kube-root-ca.crt 1 11m
mysql-config 2 6s
------------------------------------------------------------------------------------------------------------
# 用 jsonpath 读取(若方式1能看到,方式2即使输出空也不影响,部署后Pod能正常挂载)
kubectl get configmap mysql-config -n mysql-cluster -o json | jq '.data.master.cnf'
kubectl get configmap mysql-config -n mysql-cluster -o json | jq '.data.slave.cnf'
#示例
[root@k8s-master mysql-1]# kubectl get configmap mysql-config -n mysql-cluster -o json | jq '.data.master.cnf'
null
[root@k8s-master mysql-1]# kubectl get configmap mysql-config -n mysql-cluster -o json | jq '.data.slave.cnf'
null
------------------------------------------------------------------------------------------------------------
# 直接查看 ConfigMap 详情(最直观)
kubectl describe configmap mysql-config -n mysql-cluster
#示例
[root@k8s-master mysql-1]# kubectl describe configmap mysql-config -n mysql-cluster
Name: mysql-config
Namespace: mysql-cluster
Labels: <none>
Annotations: <none>
Data
====
master.cnf:
----
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
max_connections=1000
skip-name-resolve
default-storage-engine=InnoDB
server-id=1
log-bin=mysql-bin
binlog-format=ROW
log-slave-updates=1
expire-logs-days=7
innodb_flush_log_at_trx_commit=1
sync_binlog=1
slave.cnf:
----
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
max_connections=1000
skip-name-resolve
default-storage-engine=InnoDB
server-id=2
log-bin=mysql-bin
binlog-format=ROW
expire-logs-days=7
innodb_flush_log_at_trx_commit=1
sync_binlog=1
relay-log=mysql-relay-bin
log-slave-updates=1
read-only=1
super-read-only=1
BinaryData
====
Events: <none>
六、创建 PVC
核心逻辑
主库和从库的数据需要独立持久化(避免相互覆盖),因此创建 3 个 PVC(1 主 + 2 从),每个 PVC 对应一个 NFS 动态生成的 PV,数据存储在 NFS 服务器的 /data/nfs/mysql 目录下。
1. 创建主库 PVC YAML
文件名:mysql-master-pvc.yaml
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-master-pvc # PVC 名称,主库专用
namespace: mysql-cluster # 与集群同命名空间
spec:
accessModes:
- ReadWriteMany # NFS 支持多节点读写(RWX),适合主从复制场景(从库无需写数据,但配置兼容)
resources:
requests:
storage: 10Gi # 主库申请 10GB 存储(根据业务数据量调整,生产建议至少 50GB)
storageClassName: nfs-storage # 关联现有 NFS 动态存储类(自动创建 PV)
2. 创建从库 1 PVC YAML
文件名:mysql-slave1-pvc.yaml
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-slave1-pvc # 从库 1 PVC 名称
namespace: mysql-cluster
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi # 从库存储与主库一致(同步主库数据,需同等空间)
storageClassName: nfs-storage
3. 创建从库 2 PVC YAML
文件名:mysql-slave2-pvc.yaml
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-slave2-pvc # 从库 2 PVC 名称
namespace: mysql-cluster
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
storageClassName: nfs-storage
字段解释
accessModes: ReadWriteMany:NFS 支持的多节点读写模式(RWX),虽然从库是只读,但配置统一,后续若主从切换无需修改resources.requests.storage: 10Gi:申请的存储容量,主从保持一致(从库需要存储主库同步的所有数据)storageClassName: nfs-storage:指定使用现有 NFS 动态存储类,K8s 会自动创建对应的 PV,无需手动创建
4. 部署与验证
bash
# 部署 3 个 PVC
kubectl apply -f mysql-master-pvc.yaml
kubectl apply -f mysql-slave1-pvc.yaml
kubectl apply -f mysql-slave2-pvc.yaml
------------------------------------------------------------------------------------------------------------
# 验证 PVC 状态(所有 PVC 状态为 Bound 表示成功,PV 已自动创建)
kubectl get pvc -n mysql-cluster
# 预期输出:
# NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
# mysql-master-pvc Bound pvc-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 10Gi RWX nfs-storage 1m
# mysql-slave1-pvc Bound pvc-yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy 10Gi RWX nfs-storage 1m
# mysql-slave2-pvc Bound pvc-zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz 10Gi RWX nfs-storage 1m
#我这里配置的2G
[root@k8s-master mysql-1]# kubectl get pvc -n mysql-cluster
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-master-pvc Bound pvc-56c8378e-b644-4abc-8f99-f2d038fa08c1 2Gi RWX nfs-storage 19s
mysql-slave1-pvc Bound pvc-f5818768-07a5-4eac-b12c-da2881626151 2Gi RWX nfs-storage 13s
mysql-slave2-pvc Bound pvc-c095f935-a265-4827-b777-65a121d2d9c0 2Gi RWX nfs-storage 7s
------------------------------------------------------------------------------------------------------------
# 验证 PV 自动创建(3 个 PV 与 PVC 一一对应)
kubectl get pv | grep nfs-storage
#示例
[root@k8s-master mysql-1]# kubectl get pv | grep nfs-storage
pvc-56c8378e-b644-4abc-8f99-f2d038fa08c1 2Gi RWX Delete Bound mysql-cluster/mysql-master-pvc nfs-storage 62s
pvc-c095f935-a265-4827-b777-65a121d2d9c0 2Gi RWX Delete Bound mysql-cluster/mysql-slave2-pvc nfs-storage 50s
pvc-f5818768-07a5-4eac-b12c-da2881626151 2Gi RWX Delete Bound mysql-cluster/mysql-slave1-pvc nfs-storage 56s
七、部署主库 Service
1. 创建主库 Service YAML(mysql-master-service.yaml)
apiVersion: v1
kind: Service
metadata:
name: mysql-master-svc # 主库 Service 名称,从库连接时要用
namespace: mysql-cluster # 必须和主库在同一个命名空间
labels:
app: mysql
role: master
spec:
selector:
app: mysql
role: master # 筛选主库 Pod(通过标签匹配,和主库 Deployment 的标签一致)
ports:
- port: 3306 # Service 内部端口(从库连接时用这个端口)
targetPort: 3306 # 映射到主库 Pod 的 3306 端口(MySQL 容器的端口)
nodePort: 30036 # 外部访问端口(可选,用于外部客户端连接主库)
type: NodePort # 类型:NodePort,既支持集群内访问(从库用),也支持外部访问
2. 部署主库 Service
# 执行部署命令
kubectl apply -f mysql-master-service.yaml
# 验证 Service 是否部署成功(看到以下输出表示正常)
kubectl get svc -n mysql-cluster | grep mysql-master-svc
# 预期输出:
# mysql-master-svc NodePort 10.96.xxx.xxx <none> 3306:30036/TCP 10s
八、部署MySQL
1. 部署MySQL主库
1.1 主库 Deployment YAML(mysql-master-deployment.yaml)
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-master # 主库 Deployment 名称,唯一标识
namespace: mysql-cluster # 与集群资源同命名空间
labels:
app: mysql
role: master # 标签:标识应用和角色(主库),便于 Service 筛选和管理
spec:
replicas: 1 # 主库单副本(多副本会导致数据不一致,主从架构常规配置)
selector:
matchLabels:
app: mysql
role: master # 选择器:只管理带该标签的 Pod(避免误操作其他资源)
template:
metadata:
labels:
app: mysql
role: master # Pod 标签:与选择器一致,让 Deployment 能识别并管理
spec:
containers:
- name: mysql-master # 容器名称,便于日志查看和区分
image: mysql:8.0.32 # 兼容 CentOS 7.9 的镜像(规避 x86-64-v2 指令集报错)
imagePullPolicy: IfNotPresent # 优先使用本地镜像,无则远程拉取(加速部署)
ports:
- containerPort: 3306 # MySQL 容器默认监听端口(必须与 Service 映射一致)
env:
# 1. root 密码(从 Secret 读取,生产级安全,避免明文泄露)
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secrets # 关联之前创建的 Secret 名称
key: root-password # Secret 中存储 root 密码的键
# 2. 初始化创建业务数据库(应用启动前提前建库,无需手动操作)
- name: MYSQL_DATABASE
value: appdb # 业务数据库名称(可按需修改,如 shopdb、userdb)
# 3. 初始化创建业务用户(应用用普通用户连接,限制权限,降低风险)
- name: MYSQL_USER
value: appuser # 业务用户名
# 4. 业务用户密码(从 Secret 读取,避免明文)
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secrets
key: app-password
# 5. MySQL 8.0 认证插件(兼容旧客户端,避免连接失败)
- name: MYSQL_AUTHENTICATION_PLUGIN
value: mysql_native_password
# 容器健康检查(确保 MySQL 正常运行,异常自动重启)
livenessProbe:
tcpSocket:
port: 3306 # 存活探针:检测 3306 端口是否通(判断容器是否活着)
initialDelaySeconds: 60 # 启动后延迟 60 秒检测(给 MySQL 初始化时间)
periodSeconds: 10 # 每 10 秒检测一次
timeoutSeconds: 5 # 检测超时时间 5 秒
readinessProbe:
exec:
# 就绪探针:执行 mysqladmin ping 命令(判断容器是否能提供服务)
command: ["mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$(MYSQL_ROOT_PASSWORD)"]
initialDelaySeconds: 30 # 启动后延迟 30 秒检测
periodSeconds: 5 # 每 5 秒检测一次
timeoutSeconds: 3 # 超时 3 秒
# 资源限制(避免 MySQL 占用过多资源,影响其他应用)
resources:
requests: # 最小资源需求(调度时保证分配)
cpu: 500m # 0.5 核 CPU
memory: 1Gi # 1GB 内存
limits: # 最大资源限制(防止资源滥用)
cpu: 1000m # 1 核 CPU
memory: 2Gi # 2GB 内存
# 挂载配置文件和持久化存储
volumeMounts:
# 1. 挂载主库配置文件(从 ConfigMap 读取)
- name: master-config
mountPath: /etc/mysql/conf.d/master.cnf # MySQL 自动加载此目录下的 .cnf 文件
subPath: master.cnf # 只挂载 master.cnf,避免覆盖目录内其他文件
# 2. 挂载持久化存储(数据目录)
- name: master-data
mountPath: /var/lib/mysql # MySQL 数据默认存储路径(必须挂载,否则数据丢失)
subPath: mysql # 在 PV 中创建 mysql 子目录,隔离其他数据
# 定义 volumes(关联 PVC 和 ConfigMap)
volumes:
# 1. 主库配置文件卷(关联 ConfigMap)
- name: master-config
configMap:
name: mysql-config # 关联之前创建的 ConfigMap
items:
- key: master.cnf # ConfigMap 中的配置文件名
path: master.cnf # 挂载到容器内的文件名(保持一致)
# 2. 主库持久化存储卷(关联 PVC)
- name: master-data
persistentVolumeClaim:
claimName: mysql-master-pvc # 关联之前创建的主库 PVC
1.2 YAML 关键字段详细解释
| 字段路径 | 字段值 | 核心作用 |
|---|---|---|
metadata.labels |
app: mysql, role: master |
给资源打标签,后续 Service 可通过标签筛选主库 Pod,也便于批量管理(如筛选所有 MySQL 资源) |
spec.replicas: 1 |
主库单副本 | 主从架构中主库只能有一个 "写入源",多副本会导致数据冲突(生产高可用可配合主从切换工具,此处先单副本) |
image: mysql:8.0.32 |
兼容 CentOS 7.9 | 之前报错是因为 MySQL 8.0.33+ 依赖 x86-64-v2 指令集,CentOS 7.9 内核不支持,此版本完美兼容 |
env.MYSQL_AUTHENTICATION_PLUGIN |
mysql_native_password |
MySQL 8.0 默认用 caching_sha2_password,部分旧客户端(如 Python2 驱动)不兼容,此配置兼容所有客户端 |
livenessProbe |
TCP 检测 3306 端口 | 存活探针:如果端口不通,说明容器 "死了",K8s 会自动重启容器(比如 MySQL 崩溃时) |
readinessProbe |
mysqladmin ping 命令 |
就绪探针:不仅要端口通,还要能执行命令,确保 MySQL 已初始化完成并能提供服务(避免启动中就接收流量) |
resources |
请求 0.5 核 / 1G,限制 1 核 / 2G | 资源调度的关键:- requests:告诉 K8s 这个 Pod 至少需要多少资源,调度时会分配到有足够资源的节点- limits:防止 MySQL 异常时占用过多资源(比如死循环吃满 CPU) |
volumeMounts[0] |
挂载 master.cnf 到 /etc/mysql/conf.d/ |
MySQL 启动时会自动加载 /etc/mysql/conf.d/ 目录下的所有 .cnf 文件,无需手动指定配置文件路径 |
volumeMounts[1] |
挂载 /var/lib/mysql 到 PVC |
这是数据持久化的核心!MySQL 的所有数据(表、日志等)都存在这个目录,挂载 PVC 后,即使 Pod 被删除,数据也会保存在 NFS 服务器上 |
volumes |
关联 ConfigMap 和 PVC | 告诉 K8s 容器挂载的 "文件" 和 "存储" 来自哪里,是连接 Pod 与外部资源的桥梁 |
1.3 部署主库并验证
bash
# 1. 部署主库 Deployment
kubectl apply -f mysql-master-deployment.yaml
------------------------------------------------------------------------------------------------------------
# 2. 查看主库 Pod 状态(需要等 1-2 分钟,MySQL 初始化需要时间)
kubectl get pods -n mysql-cluster -l app=mysql,role=master
# 预期输出(STATUS 为 Running,READY 为 1/1 表示成功):
[root@k8s-master mysql-1]# kubectl get pod -n mysql-cluster mysql-master-79db8746fd-k9b4p --show-labels
NAME READY STATUS RESTARTS AGE LABELS
mysql-master-79db8746fd-k9b4p 1/1 Running 0 101s app=mysql,pod-template-hash=79db8746fd,role=master
[root@k8s-master mysql-1]# kubectl get pod -n mysql-cluster mysql-master-79db8746fd-k9b4p -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql-master-79db8746fd-k9b4p 1/1 Running 0 62s 10.244.169.156 k8s-node2 <none> <none>
[root@k8s-master mysql-1]# kubectl get pods -n mysql-cluster -l app=mysql,role=master
NAME READY STATUS RESTARTS AGE
mysql-master-79db8746fd-k9b4p 1/1 Running 0 83s
------------------------------------------------------------------------------------------------------------
# 3. 若状态异常(Pending/Error/CrashLoopBackOff),查看日志排查
# 替换 <pod-name> 为实际 Pod 名称
kubectl logs -n mysql-cluster <pod-name> --tail=100
------------------------------------------------------------------------------------------------------------
# 4. 验证主库配置是否生效(进入 Pod 查看 MySQL 配置)
kubectl exec -it -n mysql-cluster <pod-name> -- /bin/bash
# 登录 MySQL
mysql -u root -pShyshy521521!
# 查看主库关键配置(确认 server-id 和 log-bin 已启用)
show variables like 'server_id'; # 输出:1(与 ConfigMap 一致)
show variables like 'log_bin'; # 输出:ON(二进制日志已启用,主从复制基础)
exit && exit # 退出 MySQL 和容器
#示例
[root@k8s-master mysql-1]# kubectl exec -it -n mysql-cluster mysql-master-79db8746fd-k9b4p -- /bin/bash
bash-4.4# mysql -u root -pShyshy521521!
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 62
Server version: 8.0.32 MySQL Community Server - GPL
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show variables like 'server_id';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 1 |
+---------------+-------+
1 row in set (0.01 sec)
mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | ON |
+---------------+-------+
1 row in set (0.01 sec)
mysql> exit && exit
-> ^C
mysql> exit
Bye
bash-4.4# exit
exit
2. 配置主库复制权限(主从复制核心)
主从复制的原理是:从库通过「复制用户」连接主库,读取主库的二进制日志,同步数据。因此需要在主库创建复制用户并授权。
2.1 进入主库 Pod 并登录 MySQL
bash
# 1. 查看主库 Pod 名称
kubectl get pods -n mysql-cluster -l app=mysql,role=master
# 2. 进入 Pod(替换 <pod-name> 为实际名称)
kubectl exec -it -n mysql-cluster <pod-name> -- /bin/bash
# 3. 用 root 登录 MySQL(密码:Shyshy521521!)
mysql -u root -pShyshy521521!
2.2 创建复制用户并授权
bash
-- 1. 创建复制用户(repluser:复制用户名,%:允许所有 IP 连接,生产可限制为从库 IP)
CREATE USER 'repluser'@'%' IDENTIFIED BY 'Shyshy521521!';
-- 2. 授予复制权限(replication slave 是主从复制必需的权限,仅允许同步数据,无其他权限)
GRANT REPLICATION SLAVE ON *.* TO 'repluser'@'%';
-- 3. 刷新权限(使授权立即生效)
FLUSH PRIVILEGES;
-- 4. 验证用户和权限(可选,确认配置正确)
SELECT user, host FROM mysql.user WHERE user = 'repluser'; # 能看到 repluser 用户
SHOW GRANTS FOR 'repluser'@'%'; # 能看到 replication slave 权限
------------------------------------------------------------------------------------------------------------
#示例
[root@k8s-master mysql-1]# kubectl exec -it -n mysql-cluster mysql-master-79db8746fd-k9b4p -- /bin/bash
bash-4.4# mysql -u root -pShyshy521521!
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 97
Server version: 8.0.32 MySQL Community Server - GPL
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> CREATE USER 'repluser'@'%' IDENTIFIED BY 'Shyshy521521!';
Query OK, 0 rows affected (0.01 sec)
mysql> GRANT REPLICATION SLAVE ON *.* TO 'repluser'@'%';
Query OK, 0 rows affected (0.00 sec)
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.27 sec)
mysql> SELECT user, host FROM mysql.user WHERE user = 'repluser';
+----------+------+
| user | host |
+----------+------+
| repluser | % |
+----------+------+
1 row in set (0.00 sec)
mysql> SHOW GRANTS FOR 'repluser'@'%';
+--------------------------------------------------+
| Grants for repluser@% |
+--------------------------------------------------+
| GRANT REPLICATION SLAVE ON *.* TO `repluser`@`%` |
+--------------------------------------------------+
1 row in set (0.00 sec)
2.3 锁定主库并记录二进制日志状态
mysql
-- 1. 锁定主库(防止创建用户后有新数据写入,导致从库同步起点不一致)
FLUSH TABLES WITH READ LOCK;
-- 2. 查看主库状态(记录 File 和 Position 的值,从库配置时必须用!)
SHOW MASTER STATUS;
------------------------------------------------------------------------------------------------------------
mysql> SHOW MASTER STATUS;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 | 864 | | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
File:主库当前二进制日志文件名(如mysql-bin.000001)Position:日志文件中的偏移量(如156)- 注意:不要关闭当前终端,保持锁定状态,等两个从库都配置完成后再解锁!
3. 部署第一个从库(Slave1)
从库负责读取数据,与主库配置差异主要在 server-id 和只读配置,部署流程与主库类似。
3.1 从库 1 Deployment YAML(mysql-slave1-deployment.yaml)
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-slave1 # 从库 1 名称,唯一标识
namespace: mysql-cluster
labels:
app: mysql
role: slave
slave-id: "1" # 关键修复:数字加引号,转为字符串类型(K8s 标签值必须是字符串)
spec:
replicas: 1 # 从库单副本
selector:
matchLabels:
app: mysql
role: slave
slave-id: "1" # 选择器标签同步改为字符串
template:
metadata:
labels:
app: mysql
role: slave
slave-id: "1" # Pod 标签同步改为字符串
spec:
containers:
- name: mysql-slave1 # 容器名称
image: mysql:8.0.32 # 与主库相同镜像,保证版本兼容
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3306
env:
# 1. root 密码(与主库一致,从 Secret 读取)
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secrets
key: root-password
# 2. 认证插件(与主库一致,保证兼容性)
- name: MYSQL_AUTHENTICATION_PLUGIN
value: mysql_native_password
# 健康检查(与主库一致,确保从库正常运行)
livenessProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
exec:
command: ["mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$(MYSQL_ROOT_PASSWORD)"]
initialDelaySeconds: 30
periodSeconds: 5
timeoutSeconds: 3
# 资源限制(与主库一致,保证性能)
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 1000m
memory: 2Gi
# 挂载配置文件和存储
volumeMounts:
# 1. 挂载从库配置文件(从 ConfigMap 读取 slave.cnf)
- name: slave-config
mountPath: /etc/mysql/conf.d/slave.cnf
subPath: slave.cnf
# 2. 挂载从库持久化存储
- name: slave1-data
mountPath: /var/lib/mysql
subPath: mysql
# 定义 volumes
volumes:
# 1. 从库配置文件卷(关联 ConfigMap 的 slave.cnf)
- name: slave-config
configMap:
name: mysql-config
items:
- key: slave.cnf
path: slave.cnf
# 2. 从库存储卷(关联 slave1 的 PVC)
- name: slave1-data
persistentVolumeClaim:
claimName: mysql-slave1-pvc
3.2 从库 YAML 与主库的核心差异解释
| 差异点 | 主库配置 | 从库配置 | 原因 |
|---|---|---|---|
metadata.labels |
role: master |
role: slave, slave-id: "1" |
区分主从角色,同时用 slave-id 区分两个从库 |
volumeMounts[0] |
挂载 master.cnf |
挂载 slave.cnf |
从库需要只读、中继日志等特有配置 |
volumes[1] |
关联 mysql-master-pvc |
关联 mysql-slave1-pvc |
从库数据独立存储,避免与主库 / 其他从库冲突 |
env |
有 MYSQL_DATABASE 和 MYSQL_USER |
无 | 从库会同步主库的数据库和用户,无需单独初始化 |
3.3 部署从库 1 并验证
bash
# 1. 部署从库 1
kubectl apply -f mysql-slave1-deployment.yaml
------------------------------------------------------------------------------------------------------------
# 2. 查看从库 1 Pod 状态
kubectl get pods -n mysql-cluster -l app=mysql,slave-id=1
# 预期输出:
[root@k8s-master mysql-1]# kubectl get pods -n mysql-cluster -l app=mysql,slave-id=1
NAME READY STATUS RESTARTS AGE
mysql-slave1-557ffc7cfc-km4gc 1/1 Running 1 (93s ago) 103s
------------------------------------------------------------------------------------------------------------
# 3. 验证从库配置(进入 Pod)
kubectl exec -it -n mysql-cluster <slave1-pod-name> -- /bin/bash
mysql -u root -pShyshy521521!
# 查看从库关键配置
show variables like 'server_id'; # 输出:2(与 ConfigMap 一致)
show variables like 'read_only'; # 输出:ON(从库只读配置生效)
show variables like 'relay_log'; # 输出:mysql-relay-bin(中继日志已启用)
exit && exit
#验证
[root@k8s-master mysql-1]# kubectl exec -it -n mysql-cluster mysql-slave1-86cdb9f5f6-tfk8x -- /bin/bash
bash-4.4# mysql -uroot -pShyshy521521!
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 7
Server version: 8.0.32 MySQL Community Server - GPL
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show variables like 'server_id';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 2 |
+---------------+-------+
1 row in set (0.01 sec)
mysql> show variables like 'read_only';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| read_only | ON |
+---------------+-------+
1 row in set (0.01 sec)
mysql> show variables like 'relay_log';
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| relay_log | mysql-relay-bin |
+---------------+-----------------+
1 row in set (0.00 sec)
4. 部署第二个从库(Slave2)
与 Slave1 配置基本一致,仅需修改 slave-id 和关联的 PVC,避免冲突。
4.1 从库 2 Deployment YAML(mysql-slave2-deployment.yaml)
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-slave2 # 从库 2 名称,唯一标识
namespace: mysql-cluster
labels:
app: mysql
role: slave
slave-id: "2" # 修复:数字加双引号,转为字符串(K8s 标签值必须是字符串)
spec:
replicas: 1 # 从库单副本
selector:
matchLabels:
app: mysql
role: slave
slave-id: "2" # 选择器标签同步改为字符串
template:
metadata:
labels:
app: mysql
role: slave
slave-id: "2" # Pod 标签同步改为字符串
spec:
containers:
- name: mysql-slave2 # 容器名称,与从库 1 区分
image: mysql:8.0.32 # 兼容 CentOS 7.9 的镜像,与主库版本一致
imagePullPolicy: IfNotPresent # 优先使用本地镜像,加速部署
ports:
- containerPort: 3306 # MySQL 容器默认端口
env:
# 1. root 密码:从 Secret 读取,生产级安全(避免明文)
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secrets # 关联之前创建的 Secret
key: root-password # Secret 中存储 root 密码的键
# 2. 认证插件:与主库一致,兼容所有客户端
- name: MYSQL_AUTHENTICATION_PLUGIN
value: mysql_native_password
# 存活探针:检测容器是否运行,异常自动重启
livenessProbe:
tcpSocket:
port: 3306 # 检测 3306 端口连通性
initialDelaySeconds: 60 # 启动后延迟 60 秒检测(给 MySQL 初始化时间)
periodSeconds: 10 # 每 10 秒检测一次
timeoutSeconds: 5 # 检测超时 5 秒
# 就绪探针:检测容器是否能提供服务,避免启动中接收流量
readinessProbe:
exec:
# 执行 mysqladmin ping 命令,验证 MySQL 可用
command: ["mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$(MYSQL_ROOT_PASSWORD)"]
initialDelaySeconds: 30 # 启动后延迟 30 秒检测
periodSeconds: 5 # 每 5 秒检测一次
timeoutSeconds: 3 # 检测超时 3 秒
# 资源限制:避免 MySQL 占用过多资源,影响其他应用
resources:
requests: # 最小资源需求(调度时保证分配)
cpu: 500m # 0.5 核 CPU
memory: 1Gi # 1GB 内存
limits: # 最大资源限制(防止资源滥用)
cpu: 1000m # 1 核 CPU
memory: 2Gi # 2GB 内存
# 挂载配置文件和持久化存储
volumeMounts:
# 1. 挂载从库配置文件:从 ConfigMap 读取 slave.cnf
- name: slave-config
mountPath: /etc/mysql/conf.d/slave.cnf # MySQL 自动加载此目录下的 .cnf 文件
subPath: slave.cnf # 只挂载 slave.cnf,避免覆盖目录内其他文件
# 2. 挂载持久化存储:数据目录(核心,保证数据不丢失)
- name: slave2-data
mountPath: /var/lib/mysql # MySQL 数据默认存储路径
subPath: mysql # 在 PV 中创建 mysql 子目录,隔离数据
# 定义 volumes:关联 ConfigMap 和 PVC
volumes:
# 1. 从库配置文件卷:关联 mysql-config ConfigMap
- name: slave-config
configMap:
name: mysql-config # 关联之前创建的 ConfigMap
items:
- key: slave.cnf # ConfigMap 中的配置文件名
path: slave.cnf # 挂载到容器内的文件名(保持一致)
# 2. 从库存储卷:关联 slave2 的 PVC(独立存储,避免与其他节点冲突)
- name: slave2-data
persistentVolumeClaim:
claimName: mysql-slave2-pvc # 关联之前创建的 slave2 PVC
4.2 部署从库 2 并验证
# 1. 部署从库 2
kubectl apply -f mysql-slave2-deployment.yaml
# 2. 查看从库 2 状态
kubectl get pods -n mysql-cluster -l app=mysql,slave-id=2
# 预期输出:
[root@k8s-master mysql-1]# kubectl get pods -n mysql-cluster -l app=mysql,slave-id=2
NAME READY STATUS RESTARTS AGE
mysql-slave2-58fd746d86-knmf9 1/1 Running 1 (52s ago) 61s
# 3. 验证从库 2 配置(可选,与 Slave1 验证步骤一致)
kubectl exec -it -n mysql-cluster <slave2-pod-name> -- /bin/bash
mysql -u root -pShyshy521521!
show variables like 'server_id'; # 输出:2(后续手动修改为 3,避免与 Slave1 冲突)
exit && exit
5. 配置主从复制(核心步骤)
两个从库都部署完成后,开始配置与主库的复制关系,让从库同步主库数据。
5.1 配置从库 1 同步主库
# 1. 进入 Slave1 Pod
kubectl exec -it -n mysql-cluster <slave1-pod-name> -- /bin/bash
mysql -u root -pShyshy521521!
# 2. 停止从库复制进程(首次配置可忽略,防止已有复制进程干扰)
STOP SLAVE;
# 3. 配置主库信息(替换以下参数为步骤 2.3 记录的主库状态)
CHANGE MASTER TO
MASTER_HOST='mysql-master-svc.mysql-cluster.svc.cluster.local', # 主库 Service 名称(集群内 DNS 地址,自动解析主库 IP)
MASTER_PORT=3306, # 主库端口
MASTER_USER='repluser', # 复制用户
MASTER_PASSWORD='Shyshy521521!', # 复制用户密码
MASTER_LOG_FILE='mysql-bin.000003', # 步骤 2.3 记录的 File 值
MASTER_LOG_POS=864; # 步骤 2.3 记录的 Position 值
# 4. 启动从库复制进程
START SLAVE;
# 5. 查看从库复制状态(关键!验证是否成功)
SHOW SLAVE STATUS\G;
关键验证指标(SHOW SLAVE STATUS\G 输出)
Slave_IO_Running: Yes(IO 线程运行正常:从库成功连接主库并下载二进制日志)Slave_SQL_Running: Yes(SQL 线程运行正常:从库成功执行中继日志中的 SQL)- 若两个都是
Yes,表示从库 1 复制配置成功! - 若为
No,查看Last_IO_Error或Last_SQL_Error字段,根据错误信息排查(常见错误:主库 IP 错误、复制用户密码错误、日志文件 / 位置错误)。
5.2 配置从库 2 同步主库(与 Slave1 步骤一致)
# 1. 进入 Slave2 Pod
kubectl exec -it -n mysql-cluster <slave2-pod-name> -- /bin/bash
mysql -u root -pShyshy521521!
# 2. 配置主库信息(参数与 Slave1 一致)
STOP SLAVE;
CHANGE MASTER TO
MASTER_HOST='mysql-master-svc.mysql-cluster.svc.cluster.local',
MASTER_PORT=3306,
MASTER_USER='repluser',
MASTER_PASSWORD='Shyshy521521!',
MASTER_LOG_FILE='mysql-bin.000001', # 与主库记录的一致
MASTER_LOG_POS=156; # 与主库记录的一致
# 3. 启动复制并验证
START SLAVE;
SHOW SLAVE STATUS\G;
验证:两个从库的 Slave_IO_Running 和 Slave_SQL_Running 都为 Yes。
5.3 解锁主库(所有从库配置完成后)
回到步骤 2.3 锁定主库的终端,执行以下命令解锁(允许主库写入数据):
UNLOCK TABLES; # 解锁主库,允许新数据写入
exit && exit # 退出 MySQL 和容器
6. 部署主库 Service(外部可访问)
主库需要被外部应用访问(写入数据),用 NodePort 类型暴露服务(适合测试 / 小型生产环境)。
6.1 主库 Service YAML(mysql-master-service.yaml)
apiVersion: v1
kind: Service
metadata:
name: mysql-master-svc # 主库服务名称
namespace: mysql-cluster
labels:
app: mysql
role: master
spec:
selector:
app: mysql
role: master # 筛选主库 Pod(通过标签匹配)
ports:
- port: 3306 # Service 内部端口(集群内其他应用访问用)
targetPort: 3306 # 映射到 Pod 的 3306 端口(MySQL 容器端口)
nodePort: 30036 # 外部访问端口(范围 30000-32767,确保未被占用)
type: NodePort # 服务类型:NodePort(外部通过 节点IP:nodePort 访问)
sessionAffinity: ClientIP # 会话亲和性:同一客户端始终访问同一个 Pod(主库单副本,保证连接稳定)
6.2 YAML 字段解释
spec.selector:通过标签找到主库 Pod,确保流量转发到主库。port: 3306:集群内应用访问主库的端口(如从库同步时用的mysql-master-svc.mysql-cluster.svc:3306)。nodePort: 30036:外部访问端口,比如本地电脑用10.132.47.60:30036连接主库。type: NodePort:K8s 会在所有节点上开放30036端口,外部通过任意节点 IP + 该端口即可访问主库。
6.3 部署 Service 并验证外部访问
# 1. 部署主库 Service
kubectl apply -f mysql-master-service.yaml
# 2. 查看 Service 状态
kubectl get svc -n mysql-cluster -l app=mysql,role=master
# 预期输出:
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# mysql-master-svc NodePort 10.96.xxx.xxx <none> 3306:30036/TCP 1m
# 3. 开放防火墙端口(所有节点执行,否则外部无法访问)
firewall-cmd --zone=public --add-port=30036/tcp --permanent
firewall-cmd --reload
# 4. 外部访问验证(在集群外的电脑执行,如本地 Windows/Mac)
mysql -h 10.132.47.60 -P 30036 -u root -pShyshy521521!
# 若连接成功,进入 MySQL 命令行,说明外部访问生效!
九、验证主从复制集群可用性(最终测试)
通过在主库写入数据,验证两个从库是否自动同步,确认集群正常工作。
1. 主库写入测试数据
# 1. 外部连接主库(或进入主库 Pod)
mysql -h 10.132.47.60 -P 30036 -u root -pShyshy521521!
# 2. 切换到业务库 appdb
use appdb;
# 3. 创建测试表并插入数据
CREATE TABLE test_sync (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) NOT NULL,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO test_sync (name) VALUES ('主库测试数据1'), ('主库测试数据2');
# 4. 查看数据(主库应显示 2 条数据)
SELECT * FROM test_sync;
2. 从库 1 验证同步数据
bash
# 1. 进入 Slave1 Pod
kubectl exec -it -n mysql-cluster <slave1-pod-name> -- /bin/bash
mysql -u root -pShyshy521521!
# 2. 切换到 appdb 库(应自动同步主库的库和表)
use appdb;
# 3. 查看测试表数据(应与主库一致,显示 2 条数据)
SELECT * FROM test_sync;
3. 从库 2 验证同步数据
bash
# 1. 进入 Slave2 Pod
kubectl exec -it -n mysql-cluster <slave2-pod-name> -- /bin/bash
mysql -u root -pShyshy521521!
# 2. 切换到 appdb 库
use appdb;
# 3. 查看测试表数据(应与主库一致)
SELECT * FROM test_sync;
4. 验证从库只读(生产关键)
在从库 1 尝试写入数据,验证只读配置生效:
bash
# 在 Slave1 的 MySQL 命令行执行
INSERT INTO test_sync (name) VALUES ('从库写入测试');
# 预期报错:ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement