利用文件存储NAS搭建K8S集群的Mysql主从复制+读写分离

本文介绍如何利用阿里云文件存储NAS替代K8S原生NFS系统,从而实现存储系统脱离于集群之外的可弹性扩展,高可用,高性能 的部署

搭建的结构

  • 一个 master 节点和多个从 master 上异步复制数据的 slave 节点组成,即一主多从复制模型。其中,master 节点可用来处理用户的读写请求,slave 节点只能用来处理用户的读请求。
  • 通过statefulset部署的mysql能通过无头服务的域名发现彼此,实现主从表同步。
  • 我们除了要实现这个之外,还需要将mysql的数据存储到nas文件存储系统当中
  • 将主从表的配置放在configMap中
  • 通过内网的负载均衡,将只读查询通过轮询或其他规则的请求打在从表一二之上。而读写请求则可以直接访问主表。

 

利用文件存储NAS搭建K8S集群的Mysql主从复制+读写分离

 

搭建思路

  1. 申请阿里云NAS文件系统,使用NAS存储系统;

  2. 创建Storage存储类;

  3. 创建configMap配置字典;

  4. 部署headless无头服务;

  5. 部署statefulset应用;

  6. 查看状态;

  7. 部署对外只读服务 Read-Only;

  8. 部署对外读写服务 Read-Write

 

下面我们开始一步步开始搭建部署

 

具体步骤

  1. 创建使用NAS存储系统

  • 创建nas文件系统

这里其实推荐极速型,更适用于读写频繁的数据库,因为它低时延,大概只有3ms左右

利用文件存储NAS搭建K8S集群的Mysql主从复制+读写分离

 

  • 挂载使用,我这里只演示通用型的

挂载点:0c28c4801a-l***5.cn-hangzhou.nas.aliyuncs.com

利用文件存储NAS搭建K8S集群的Mysql主从复制+读写分离

  • 在linux上挂载使用,方便管理

  • #如果您使用CentOS、Redhat、Aliyun Linux操作系统,请执行以下命令
    sudo yum install nfs-utils
    ​
    #增加同时发起的NFS请求的数量。
    #请执行以下命令,将同时发起的NFS请求数量修改为128
    sudo echo "options sunrpc tcp_slot_table_entries=128" >>  /etc/modprobe.d/sunrpc.conf
    sudo echo "options sunrpc tcp_max_slot_table_entries=128" >>  /etc/modprobe.d/sunrpc.conf
    ​
    查看
    mount -l
    
    查看当前文件系统的容量信息
    df -h

    利用文件存储NAS搭建K8S集群的Mysql主从复制+读写分离

  • 下面开始把NAS挂载到集群内

创建StorageClass存储类

先创建一个命名空间为mysql,这里不详细演示;

这里创建的sc能够动态帮我们创建使用存储类的持久化存储卷声明(PVC),然后存储持久化声明通知系统它需要一个使用存储类创建的持久化存储卷(PV),它会自动帮我们在挂载NAS系统中去寻找一块可用的空间并进行绑定

kind :StorageClass

name:data

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: data
mountOptions:
- nolock,tcp,noresvport
- vers=3
parameters:
  server: "2b474***-cxc20.cn-shenzhen.nas.aliyuncs.com:/nasroot1/"
  driver: flexvolume
provisioner: alicloud/nas
reclaimPolicy: Delete

官方文档链接:https://help.aliyun.com/document_detail/27518.html

 

创建configMap配置字典

kind :ConfigMap

创建mysql的配置项

apiVersion: v1
kind: ConfigMap

metadata:
  name: mysql
  labels:
    app: mysql
data:
  master.cnf: |
    # Apply this config only on the master.
    [mysqld]
    log-bin
  slave.cnf: |
    # Apply this config only on slaves.
    [mysqld]
    super-read-only

创建mysql的账号密码配置

apiVersion: v1
kind: ConfigMap
metadata:
  name: mconfig
  labels:
    app: mconfig
data:
  passwd.cnf: |
    [mysql]
    user=root
    password=123456
    [mysqladmin]
    user=root
    password=123456

 

部署headless无头服务

kind: Service

clusterIP: None

apiVersion: v1
kind: Service
metadata:
  name: mysql-headless
  labels:
    app: mysql
spec:
  ports:
  - name: mysql
    port: 3306
  clusterIP: None
  selector:
    app: mysql
 
 

#服务名称 + 命名空间 + 集群域名+@coredns的IP地址
mysql-master-svc.mysql.svc.cluster.local @10.27.0.80

部署statefulset应用

具体请参考:国际渣男的文章:https://blog.csdn.net/qq_38900565/article/details/114832445

 

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql-ss
spec: 
  selector: 
    matchLabels: 
      app: mysql 
  serviceName: mysql-headless
  replicas: 3
  template: 
    metadata:
      labels:
        app: mysql 
    spec:
      initContainers:
      - name: init-mysql
        image: mysql:5.7
        imagePullPolicy: IfNotPresent
        command:
        - bash
        - "-c"
        - |
          set ex
          # 从hostname中获取索引,比如(mysql-1)会获取(1)
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          echo [mysqld] > /mnt/conf.d/server-id.cnf
          # 为了不让server-id=0而增加偏移量
          echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
          # 拷贝对应的文件到/mnt/conf.d/文件夹中
          if [[ $ordinal -eq 0 ]]; then
            cp /mnt/config-map/master.cnf /mnt/conf.d/
          else
            cp /mnt/config-map/slave.cnf /mnt/conf.d/
          fi
        volumeMounts:
        - name: conf
          mountPath: /mnt/conf.d
        - name: config-map
          mountPath: /mnt/config-map
      - name: clone-mysql
        image: ist0ne/xtrabackup
        imagePullPolicy: IfNotPresent
        command:
        - bash
        - "-c"
        - |
          set -ex
          # 整体意思:
          # 1.如果是主mysql中的xtrabackup,就不需要克隆自己了,直接退出
          # 2.如果是从mysql中的xtrabackup,先判断是否是第一次创建,因为第二次重启本地就有数据库,无需克隆。若是第一次创建(通过/var/lib/mysql/mysql文件是否存在判断),就需要克隆数据库到本地。
          # 如果有数据不必克隆数据,直接退出()
          [[ -d /var/lib/mysql/mysql ]] && exit 0
          # 如果是master数据也不必克隆
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          [[ $ordinal -eq 0 ]] && exit 0
          # 从序列号比自己小一的数据库克隆数据,比如mysql-2会从mysql-1处克隆数据
          ncat --recv-only mysql-ss-$(($ordinal-1)).mysql-headless 3307 | xbstream -x -C /var/lib/mysql
          # 比较数据,登录
          xtrabackup --user=root --password=123456 --prepare --target-dir=/var/lib/mysql
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
      containers:
      - name: mysql
        image: mysql:5.7
        imagePullPolicy: IfNotPresent
        args: ["--default-authentication-plugin=mysql_native_password"]
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "123456"
        ports:
        - name: mysql
          containerPort: 3306
        volumeMounts:
        - name: mconfig                          
          mountPath: /var/passwd.cnf
          subPath: var/passwd.cnf
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 50m
            memory: 50Mi
        livenessProbe:
          exec:
            command: ["mysqladmin", "--defaults-extra-file=/var/passwd.cnf", "ping"]
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          exec:
            # Check we can execute queries over TCP (skip-networking is off).
            command: ["mysql", "--defaults-extra-file=/var/passwd.cnf","-h", "127.0.0.1", "-e", "SELECT 1"]
          initialDelaySeconds: 5
          periodSeconds: 2
          timeoutSeconds: 1
      - name: xtrabackup
        image: ist0ne/xtrabackup
        imagePullPolicy: IfNotPresent
        ports:
        - name: xtrabackup
          containerPort: 3307
        command:
        - bash
        - "-c"
        - |
          set -ex
          # 确定binlog 克隆数据位置(如果binlog存在的话).
          cd /var/lib/mysql
          # 如果存在该文件,则该xrabackup是从现有的从节点克隆出来的。
          if [[ -s xtrabackup_slave_info ]]; then
            mv xtrabackup_slave_info change_master_to.sql.in
            rm -f xtrabackup_binlog_info
          elif [[ -f xtrabackup_binlog_info ]]; then         
            [[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
            rm xtrabackup_binlog_info
            echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
                  MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
          fi
          # Check if we need to complete a clone by starting replication.     
          if [[ -f change_master_to.sql.in ]]; then
            echo "Waiting for mysqld to be ready (accepting connections)"
            until mysql -h 127.0.0.1 -p123456 -e "SELECT 1"; do sleep 1; done
            echo "Initializing replication from clone position"
            mv change_master_to.sql.in change_master_to.sql.orig
            mysql -h 127.0.0.1 -p123456 <<EOF
          $(<change_master_to.sql.orig),
            MASTER_HOST='mysql-ss-0.mysql-headless',
            MASTER_USER='root',
            MASTER_PASSWORD='123456',
            MASTER_CONNECT_RETRY=10;
          START SLAVE;
          EOF
          fi
          exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
            "xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root --password=123456"
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 10m
            memory: 10Mi
      volumes:
      - name: mconfig          
        configMap:
          name: mconfig
          items:
          - key: passwd.cnf
            path: var/passwd.cnf
      - name: conf
        emptyDir: {}
      - name: config-map
        configMap:
          name: mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes:
        - ReadWriteOnce
      storageClassName: data
      resources:
        requests:
          storage: 2Gi

查看状态

利用文件存储NAS搭建K8S集群的Mysql主从复制+读写分离

利用文件存储NAS搭建K8S集群的Mysql主从复制+读写分离

测试看看

kubectl get pods -n mysql -o wide

kubectl exec -it mysql-ss-0 -n mysql -c mysql /bin/bash

分别进行在主数据库中创建表,插入数据,在从表1中查询,在从表2中查询

mysql-master创建一个staffsde表

利用文件存储NAS搭建K8S集群的Mysql主从复制+读写分离

在mysql-slaver-1中也能查看得到

利用文件存储NAS搭建K8S集群的Mysql主从复制+读写分离

这样mysql就部署好了,我们还需要讲应用暴露进行测试

部署对外只读服务(Only-Read)

这里只读服务可以主从三个表都可以使用,两种方法

kind:NodePort

nodeport 方式部署,将mysql以节点端口方式向外暴露,在本地进行只读连接测试(使用集群任意节点IP:nodePort进行连接)

apiVersion: v1
kind: Service
metadata:
  name: mysql-Read
  labels:
    app: mysql
spec:
  ports:
  - name: mysql
    port: 3306
    targetPort: 3306
    nodePort: 30036
  type: NodePort
  selector:
    app: mysql

kind: LoadBalancer

LoadBalancer负载均衡方式将mysql,暴露

apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: mysql
spec:
  ports:
  - port: 80
    protocol: 30036
    targetPort: 3306
  selector:
    run: nginx
  type: LoadBalancer

在navicat中连接mysql进行测试,尝试插入数据,显示只读,部署完成

利用文件存储NAS搭建K8S集群的Mysql主从复制+读写分离

部署对外读写服务(Read-Write)

这里读写服务只允许主表能够使用,通过标签选择:statefulset.kubernetes.io/pod-name: mysql-ss-0

kind:NodePort

apiVersion: v1
kind: Service
metadata:
  name: mysql-Read
  labels:
    app: mysql
spec:
  ports:
  - name: mysql
    port: 3306
    targetPort: 3306
    nodePort: 30036
  type: NodePort
  selector:
    statefulset.kubernetes.io/pod-name: mysql-ss-0

 

kind: LoadBalancer

LoadBalancer负载均衡方式将mysql,暴露

apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: mysql
spec:
  ports:
  - port: 80
    protocol: 30036
    targetPort: 3306
  selector:
    statefulset.kubernetes.io/pod-name: mysql-ss-0
  type: LoadBalancer

此时可以通过读写连接测试(使用集群任意节点IP:nodePort进行连接)

利用文件存储NAS搭建K8S集群的Mysql主从复制+读写分离

至此,部署完成啦;

 

让我们现在通过集群外的主机看看nas挂载的数据变成什么样吧

利用文件存储NAS搭建K8S集群的Mysql主从复制+读写分离

创建的staffs表可以看到是在存储卷内的。至此,部署完成,哈哈

 

上一篇:code embedding研究系列六-C-BERT


下一篇:震惊!python仅需一行代码便可实现超炫酷可视化