k8s 部署MySQL主从集群(一主两从)1.0

一、环境清单

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 组,版本为 v1
  • kind: 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_DATABASEMYSQL_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_ErrorLast_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_RunningSlave_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
相关推荐
xdpcxq10294 分钟前
MySQL 5.6 2000 万行高频读写表新增字段
数据库·mysql
岁岁种桃花儿12 分钟前
流量入口Nginx动态发现K8s Ingress Controller实操指南
nginx·架构·kubernetes
礼拜天没时间.23 分钟前
Docker基础操作——镜像与容器管理
linux·运维·服务器·docker·容器·centos
冗量23 分钟前
Kubernetes (K8s) 基础知识、部署与运维指南
运维·容器·kubernetes
qinyia36 分钟前
在Ubuntu 22.04.5 LTS上安装MySQL 8并设置root密码的完整协作流程
mysql·ubuntu·adb
晔子yy1 小时前
MySQL存储引擎全面解析
数据库·mysql
青衫客361 小时前
从 TLS 到 Kubernetes PKI:一条证书链如何支撑整个集群安全(问题合集)
容器·kubernetes·k8s·tls
Swift社区1 小时前
Docker Compose 一键部署前后端分离项目
运维·docker·容器
予枫的编程笔记1 小时前
【MySQL飞升篇】MySQL主从复制灵魂三问:Binlog怎么选?线程如何工作?延迟怎么解?
mysql·性能优化·binlog·主从复制·数据库运维·并行复制·延迟解决
银发控、9 小时前
MySQL联合索引
数据库·mysql