🏢 官方博客地址: https://mariadb.org/mariadb-k8s-how-to-replicate-mariadb-in-k8s/

🏆 k8s 搭建 mariadb数据库主从

🍪 准备工作

1️⃣ k8s集群

主机名 ip
k8s-master 192.168.200.10
k8s-node01 192.168.200.20

2️⃣ 工序:

  1. 编写服务的 headless Svc
  2. 编写 PV
  3. 将配置文件存储至 configmap,用户密码存储到 secret(非必须)
  4. 编写 Statefulset

🌟 配置 Headless

[root@k8s-master Mariadb]# cat Headless.yaml
apiVersion: v1
kind: Service
metadata:
  name: mariadb
  labels:
    app: mariadb
spec:
  ports:
  - name: mariadb
    port: 3306
  clusterIP: None
  selector:
    app: mariadb
  
##查看
[root@k8s-master Mariadb]# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP    10d
mariadb      ClusterIP   None         <none>        3306/TCP   4h32m

🌟 编写 PV

需要安装 nfs

[root@k8s-master Mariadb]# cat pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mariadb-pv3
spec:
  capacity:
    storage: 5Gi
  accessModes:
  - ReadWriteOnce
  volumeMode: Filesystem
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs
  nfs:
    path: /nfs/pv3
    server: 192.168.200.20
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mariadb-pv4
spec:
  capacity:
    storage: 5Gi
  accessModes:
  - ReadWriteOnce
  volumeMode: Filesystem
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs
  nfs:
    path: /nfs/pv4
    server: 192.168.200.20
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mariadb-pv5
spec:
  capacity:
    storage: 5Gi
  accessModes:
  - ReadWriteOnce
  volumeMode: Filesystem
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs
  nfs:
    path: /nfs/pv5
    server: 192.168.200.20

🌟 将配置文件存储/密码至 ConfigMap/Secret

[root@k8s-master Mariadb]# cat configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
 name: mariadb
 labels:
   app: mariadb
data:
  master.cnf: |
    [mysqld]
    character-set-server=utf8
    collation-server=utf8_general_ci
    log-bin= mysql-bin #二进制日志,后面指定存放位置。如果只是指定名字,默认存放在/var/lib/mysql下(一般如果是简单的主从配置)
    lower_case_table_names=1 # 不区分大小写
    binlog-format=ROW    #二进制日志文件格式(一般如果是简单的主从配置)
    log-slave-updates=True    #slave更新是否记入日志
    sync-master-info=1    #值为1确保信息不会丢失
    slave-parallel-threads=3 #同时启动多少个复制线程,最多与要复制的数据库数量相等即可
    binlog-checksum=CRC32    #效验码
    master-verify-checksum=1    #启动主服务器效验
    slave-sql-verify-checksum=1   #启动从服务器效验
  slave.cnf: |
    [mysqld]
    character-set-server=utf8
    collation-server=utf8_general_ci
    log-bin= mysql-bin #log-bin是二进制文件 如果是为了在中继的话就可以开,不需要没必要开
    relay_log = relay-bin    # 中继日志, 后面指定存放位置。如果只是指定名字,默认存放在/var/lib/mysql下
    lower_case_table_names=1

##执行查看
[root@k8s-master Mariadb]# kubectl apply -f configmap.yml
[root@k8s-master Mariadb]# kubectl get configmaps
NAME           DATA   AGE
mariadb        2      10h

🌟 配置 StatefulSet

思路是,将 configmappod中的 conf目录挂载到这个 PodTemplate中,如何使用 init容器,判断该 podmaster还是 slave,复制不一样的配置文件,然后编写脚本来进行初始化,将 /var/lib/mysqlmysql 数据目录)使用 pvc挂载保证数据的可靠性,

1️⃣ 完整代码

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mariadb
  labels:
    app: mariadb
spec:
  replicas: 2
  selector:
    matchLabels:
      app: mariadb
  serviceName: mariadb
  template:
    metadata:
      labels:
        app: mariadb
    spec:
      initContainers:
      - name: init-mariadb
        image: docker.io/library/mariadb:10.6.4
        command:
        - bash
        - "-c"
        - |
          #判断pod是主节点还是从节点,注意需要pod名为 xxx-1,xxx-0 这种,当然StatefulSet会自动生成
          name=$(hostname|cut -d - -f 2)
          if [ name -eq 0 ]; then
            cp /mnt/config-map/master.cnf /mnt/conf.d/server.cnf
            echo server_id = $(expr 100 + $name) >> /mnt/conf.d/server.cnf
          else
            cp /mnt/config-map/slave.cnf /mnt/conf.d/server.cnf
            echo server_id = $(expr 100 + $name) >> /mnt/conf.d/server.cnf
          fi
        volumeMounts:
        - name: config
          mountPath: /mnt/conf.d
        - name: config-map
          mountPath: /mnt/config-map
      containers:
      - name: mariadb
        image: docker.io/library/mariadb:10.6.4
        env:
        - name: MARIADB_ROOT_PASSWORD
          value: "000000"
        volumeMounts:
        - name: host-time
          mountPath: /etc/localtime
        - name: data
          mountPath: /var/lib/mysql
        - name: config
          mountPath: /etc/mysql/conf.d
      - name: mariadb-sidecar
        image: docker.io/library/mariadb:10.6.4
        command:
        - bash
        - "-c"
        - |
           set -ex
           cd /etc/mysql/conf.d/
           name=$(hostname|cut -d - -f 2)
           until mysql -h 127.0.0.1 -uroot -p000000 -e "select 1";do sleep 1;done
           #通过name判断
              if [ $name -eq 0 ];then
              mysql  -h 127.0.0.1 -uroot  -p000000 -e "grant replication slave, replication client on *.* to 'sl'@'%' identified by '000000';"
              mysql  -h 127.0.0.1  -uroot -p000000 -e "flush privileges;"
            else
             ## 这里需要注意pod的名称和headless
             binglogName=$(mysql -uroot -h mariadb-0.mariadb.default.svc.cluster.local -p000000 -e "show master status\G" | grep -o "mysql-bin.*")
             echo $binglogName
             posName=$(mysql -uroot -h mariadb-0.mariadb.default.svc.cluster.local -p000000 -e "show master status\G" | grep Position | awk '{print $2}')
             echo $posName
             mysql   -h 127.0.0.1  -uroot -p000000 -e "stop slave;"
             mysql  -h 127.0.0.1  -uroot -p000000 -e "change master to master_host='mariadb-0.mariadb.default.svc.cluster.local',master_user='sl',master_password='000000',master_log_file='$binglogName',master_log_pos=$posName;"
             mysql   -h 127.0.0.1 -uroot -p000000 -e "start slave;"
             mysql   -h 127.0.0.1 -uroot -p000000 -e "show slave status\G;"
            fi
            tail -f /etc/passwd
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
        - name: config
          mountPath: /etc/mysql/conf.d
      volumes:
      - name: host-time
        hostPath:
          path: /etc/localtime
          type: ''
      - name: config
        emptyDir: {}
      - name: config-map
        configMap:
         name: mariadb
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      storageClassName: nfs
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 5Gi

2️⃣ initcontainer分析

initContainers:
- name: init-mariadb
image: docker.io/library/mariadb:10.6.4
command:
- bash
- "-c"
- |
  #判断pod是主节点还是从节点,注意需要pod名为 xxx-1,xxx-0 这种,当然StatefulSet会自动生成
  name=$(hostname|cut -d - -f 2)
  if [ name -eq 0 ]; then
    cp /mnt/config-map/master.cnf /mnt/conf.d/server.cnf
    echo server_id = $(expr 100 + $name) >> /mnt/conf.d/server.cnf
  else
    cp /mnt/config-map/slave.cnf /mnt/conf.d/server.cnf
    echo server_id = $(expr 100 + $name) >> /mnt/conf.d/server.cnf
  fi
volumeMounts:
- name: config
  mountPath: /mnt/conf.d
- name: config-map
  mountPath: /mnt/config-map

1.镜像可以替换成 busybox等其他镜像

2.执行了一个 command,使用 statefulsetid判断是否为主还是从,复制存储在 configmap中的配置,注意这里是因为在 volumes中挂载了 configmap

volumes:
- name: host-time
hostPath:
  path: /etc/localtime
  type: ''
- name: config
emptyDir: {}
- name: config-map
configMap:
 name: mariadb

3.根据 id 计算数据库的唯一标识并重定向至配置文件中

3️⃣ 边车容器分析

  - name: mariadb-sidecar
    image: docker.io/library/mariadb:10.6.4
    command:
    - bash
    - "-c"
    - |
       set -ex
       cd /etc/mysql/conf.d/
       name=$(hostname|cut -d - -f 2)
       until mysql -h 127.0.0.1 -uroot -p000000 -e "select 1";do sleep 1;done
       #通过name判断
          if [ $name -eq 0 ];then
          mysql  -h 127.0.0.1 -uroot  -p000000 -e "grant replication slave, replication client on *.* to 'sl'@'%' identified by '000000';"
          mysql  -h 127.0.0.1  -uroot -p000000 -e "flush privileges;"
        else
         ## 这里需要注意pod的名称和headless
         binglogName=$(mysql -uroot -h mariadb-0.mariadb.default.svc.cluster.local -p000000 -e "show master status\G" | grep -o "mysql-bin.*")
         echo $binglogName
         posName=$(mysql -uroot -h mariadb-0.mariadb.default.svc.cluster.local -p000000 -e "show master status\G" | grep Position | awk '{print $2}')
         echo $posName
         mysql   -h 127.0.0.1  -uroot -p000000 -e "stop slave;"
         mysql  -h 127.0.0.1  -uroot -p000000 -e "change master to master_host='mariadb-0.mariadb.default.svc.cluster.local',master_user='sl',master_password='000000',master_log_file='$binglogName',master_log_pos=$posName;"
         mysql   -h 127.0.0.1 -uroot -p000000 -e "start slave;"
         mysql   -h 127.0.0.1 -uroot -p000000 -e "show slave status\G;"
        fi
        tail -f /etc/passwd
    volumeMounts:
    - name: data
      mountPath: /var/lib/mysql
    - name: config
      mountPath: /etc/mysql/conf.d

1.边车容器需要对主 mariadb容器进行主从配置操作操作

2.也是通过 id来判断主从操作,如果是主则创建主从配置用户,如果是从则从主容器中拿取数据并配置 slave

3.注意边车容器不能关闭,所以需要使用某些方法来挂起

4️⃣ 主容器分析

  containers:
  - name: mariadb
    image: docker.io/library/mariadb:10.6.4
    env:
    - name: MARIADB_ROOT_PASSWORD
      value: "000000"
    volumeMounts:
    - name: host-time
      mountPath: /etc/localtime
    - name: data
      mountPath: /var/lib/mysql
    - name: config
      mountPath: /etc/mysql/conf.d

1.需要通过 MARIADB_ROOT_PASSWORD环境变量来设置数据的密码,详情请查看https://hub.docker.com/_/mariadb

2.挂载了 dataconfigconfig在之前 init容器中就已经复制好了配置文件,data是使用的 pv,在 statefulSet的后面还有一个 pvcTemplate,挂载至 /var/lib/mysql用于持久化保存数据

  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      storageClassName: nfs
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 5Gi

3.还是通过 id来判断主从节点的操作

5️⃣ 启动容器后查看

##执行
[root@k8s-master Mariadb]# kubectl apply -f StatefulSet.yml
statefulset.apps/mariadb created
##由于网络问题可能会重启pod几次
[root@k8s-master Mariadb]# kubectl get pod
NAME        READY   STATUS    RESTARTS   AGE
mariadb-0   2/2     Running   0          95s
mariadb-1   2/2     Running   3          92s
##查看mariadb-1边车容器配置
[root@k8s-master Mariadb]# kubectl logs -f mariadb-1 mariadb-sidecar
+ cd /etc/mysql/conf.d/
++ hostname
++ cut -d - -f 2
+ name=1
+ mysql -h 127.0.0.1 -uroot -p000000 -e 'select 1'
1
1
+ '[' 1 -eq 0 ']'
++ grep -o 'mysql-bin.*'
++ mysql -uroot -h mariadb-0.mariadb.default.svc.cluster.local -p000000 -e 'show master status\G'
+ binglogName=mysql-bin.000004
+ echo mysql-bin.000004
mysql-bin.000004
++ mysql -uroot -h mariadb-0.mariadb.default.svc.cluster.local -p000000 -e 'show master status\G'
++ grep Position
++ awk '{print $2}'
+ posName=661
+ echo 661
+ mysql -h 127.0.0.1 -uroot -p000000 -e 'stop slave;'
661
+ mysql -h 127.0.0.1 -uroot -p000000 -e 'change master to master_host='\''mariadb-0.mariadb.default.svc.cluster.local'\'',master_user='\''sl'\'',master_password='\''000000'\'',master_log_file='\''mysql-bin.000004'\'',master_log_pos=661;'
+ mysql -h 127.0.0.1 -uroot -p000000 -e 'start slave;'
+ mysql -h 127.0.0.1 -uroot -p000000 -e 'show slave status\G;'
*************************** 1. row ***************************
                Slave_IO_State: Checking master version
                   Master_Host: mariadb-0.mariadb.default.svc.cluster.local
                   Master_User: sl
                   Master_Port: 3306
                 Connect_Retry: 60
               Master_Log_File: mysql-bin.000004
           Read_Master_Log_Pos: 661
                Relay_Log_File: relay-bin.000001
                 Relay_Log_Pos: 4
         Relay_Master_Log_File: mysql-bin.000004
              Slave_IO_Running: Preparing
             Slave_SQL_Running: Yes
               Replicate_Do_DB:
           Replicate_Ignore_DB:
            Replicate_Do_Table:
        Replicate_Ignore_Table:
       Replicate_Wild_Do_Table:
   Replicate_Wild_Ignore_Table:
                    Last_Errno: 0
                    Last_Error:
                  Skip_Counter: 0
           Exec_Master_Log_Pos: 661
               Relay_Log_Space: 256
               Until_Condition: None
                Until_Log_File:
                 Until_Log_Pos: 0
            Master_SSL_Allowed: No
            Master_SSL_CA_File:
            Master_SSL_CA_Path:
               Master_SSL_Cert:
             Master_SSL_Cipher:
                Master_SSL_Key:
         Seconds_Behind_Master: NULL
 Master_SSL_Verify_Server_Cert: No
                 Last_IO_Errno: 0
                 Last_IO_Error:
                Last_SQL_Errno: 0
                Last_SQL_Error:
   Replicate_Ignore_Server_Ids:
              Master_Server_Id: 100
                Master_SSL_Crl:
            Master_SSL_Crlpath:
                    Using_Gtid: No
                   Gtid_IO_Pos:
       Replicate_Do_Domain_Ids:
   Replicate_Ignore_Domain_Ids:
                 Parallel_Mode: optimistic
                     SQL_Delay: 0
           SQL_Remaining_Delay: NULL
       Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
              Slave_DDL_Groups: 0
Slave_Non_Transactional_Groups: 0
    Slave_Transactional_Groups: 0
+ tail -f /etc/passwd
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
mysql:x:999:999::/home/mysql:/bin/sh



###查看slave状态
[root@k8s-master Mariadb]# kubectl exec -it mariadb-0 -- bash
Defaulting container name to mariadb.
Use 'kubectl describe pod/mariadb-0 -n default' to see all of the containers in this pod.
root@mariadb-0:/# mysql -uroot -p000000 -hmariadb-1.mariadb
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 17
Server version: 10.6.4-MariaDB-1:10.6.4+maria~focal-log mariadb.org binary distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> show slave status\G
*************************** 1. row ***************************
                Slave_IO_State: Waiting for master to send event
                   Master_Host: mariadb-0.mariadb.default.svc.cluster.local
                   Master_User: sl
                   Master_Port: 3306
                 Connect_Retry: 60
               Master_Log_File: mysql-bin.000004
           Read_Master_Log_Pos: 661
                Relay_Log_File: relay-bin.000002
                 Relay_Log_Pos: 555
         Relay_Master_Log_File: mysql-bin.000004
              Slave_IO_Running: Yes
             Slave_SQL_Running: Yes
               Replicate_Do_DB:
           Replicate_Ignore_DB:
            Replicate_Do_Table:
        Replicate_Ignore_Table:
       Replicate_Wild_Do_Table:
   Replicate_Wild_Ignore_Table:
                    Last_Errno: 0
                    Last_Error:
                  Skip_Counter: 0
           Exec_Master_Log_Pos: 661
               Relay_Log_Space: 858
               Until_Condition: None
                Until_Log_File:
                 Until_Log_Pos: 0
            Master_SSL_Allowed: No
            Master_SSL_CA_File:
            Master_SSL_CA_Path:
               Master_SSL_Cert:
             Master_SSL_Cipher:
                Master_SSL_Key:
         Seconds_Behind_Master: 0
 Master_SSL_Verify_Server_Cert: No
                 Last_IO_Errno: 0
                 Last_IO_Error:
                Last_SQL_Errno: 0
                Last_SQL_Error:
   Replicate_Ignore_Server_Ids:
              Master_Server_Id: 100
                Master_SSL_Crl:
            Master_SSL_Crlpath:
                    Using_Gtid: No
                   Gtid_IO_Pos:
       Replicate_Do_Domain_Ids:
   Replicate_Ignore_Domain_Ids:
                 Parallel_Mode: optimistic
                     SQL_Delay: 0
           SQL_Remaining_Delay: NULL
       Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
              Slave_DDL_Groups: 0
Slave_Non_Transactional_Groups: 0
    Slave_Transactional_Groups: 0
1 row in set (0.001 sec)