基于StatefulSet控制器在Kubernetes上部署MySQL一主多从

news2024/11/18 12:24:58

一、前提--StatefuSet特性

1.1 有状态的节点控制器 -- StatefulSet 及其网络状态

容器的解决方案是针对无状态应用场景的最佳实践,但对于有状态应用来说,就并非如此了。Kubernetes 用 StatefulSet 解决了有状态应用编排的问题,本文我们就来初步认识一下 StatefulSet。

StatefulSet 将应用设计抽象为了两种状态:

拓扑状态:

       应用的多个实例必须按照某种顺序启动,并且必须成组存在,例如一个应用中必须存在一个 A Pod 和两个 B Pod,且 A Pod 必须先于 B Pod 启动的场景。

存储状态:

      应用存在多个实例,但每个实例绑定的存储数据不同,那么对于一个 Pod 来说,无论它是否被重新创建,它读到的数据状态应该是一致的。

Kubernetes 的 Service 就是对外提供的可访问服务,它有两种访问方式:

  1. VIP 方式:它是 Virtual IP 的缩写,通过将服务绑定到 Kubernetes 虚拟的 IP 地址,提供给外部调用,通过虚拟 IP 地址隐藏了服务的具体实现与地址。

  2. DNS 方式:与虚拟 IP 地址类似,外部通过访问 DNS 记录的方式实现对具体 Service 的转发。

DNS 的两种处理方式

  1. Normal Service:将 DNS 地址绑定到虚拟 IP 地址,从而复用虚拟 IP 地址的设计和逻辑;

  2. Headless Service:将 DNS 地址直接代理到 Pod。

clusterIP 设置为了 None,表示不为这个 Service 分配 VIP,而是通过 Headless DNS 的方式来处理该 Service 的调用。

<pod-name>.<svc-name>.<namespace>.svc.cluster.local

这个 DNS 就是 Kubernetes 为 Pod 分配的唯一可解析身份,这样一来,只要有了 Pod 的名字和 Service 的名字,我们就能唯一确定一个能够访问这个 Pod 的 DNS 地址了。

只要我们使用 DNS 记录来访问 StatefulSet 控制器控制下的 Pod,即使 Pod 发生了宕机和重启,DNS 记录对应的Pod记录本身是不会发生变化的,同一个“名字-编号”组合的 Pod 在 StatefulSet 中总是稳定地对外提供服务的,进而实现了整个“网络状态”的稳定。

1.2 有状态的节点控制器 StatefulSet 的存储状态

StatefulSet 通过为每一个 pod 分配有粘性的 ID,并且在 pod 发生变更时,维持 ID 的稳定,从而保证了网络状态下不对等关系的各个 Pod 在启动、删除和重建过程中能够始终保持稳定。

但在实际的使用场景中,我们不仅仅需要维护网络拓扑的稳定性,Pod 与分布式存储的存储节点之间关系的稳定性往往也是非常重要的,而这也正是 StatefulSet 的另一个优势。

对于一个 Pod 来说,它需要挂载和使用的分布式存储节点必须是稳定的。Id 为 web-0 的 Pod 如果在某一时刻挂载了 web-1 Pod 对应的存储资源,结果可能是不堪设想的。

StatefulSet 控制器通过 volumeClaimTemplates 解决了这一问题。

如果我们为一个 StatefulSet 配置了 volumeClaimTemplates,那么就意味着,这个控制器中管理的每个 Pod 都会自动声明一个自己 ID 所对应的 PVC,而这个 PVC 定义所需的属性,则均来自于 volumeClaimTemplates 中的声明。

StatefuSet里声明了volumeClaimTemplates后,该StatefulSet 创建出来的所有 Pod,都会声明使用编号的 PVC。比如,在名叫 web-0 的 Pod 的 volumes 字段,它会声明使用名叫 www-web-0 的 PVC,从而挂载到这个 PVC 所绑定的 PV。

当 web-0 Pod 向挂载给他的 PV 节点中写入数据后,即使 web-0 Pod 发生宕机或重启,从而被一个全新的同样 ID 为 web-0 的 Pod 替换后,由于新的 Pod 挂载的仍然是 Id 为 www-web-0 的 PVC,所以,它依然可以读取到此前 web-0 Pod 写入的数据。

二、有状态应用典型案例--MySQL主从

mysql 集群是一个非常典型的有状态应用,怎么在kubernetes集群上部署mysql集群,就需要使用statefuset控制器和持久数据PV、PVC。

对于 mysql 集群来说,我们首先要选取主节点,并且启动它,如果这是一个已有数据 mysql 节点,还需要考虑如何备份 mysql 主节点上的数据。

此后,我们需要用另一套配置来启动若干从节点,并且在这些从节点上恢复上一步中主节点上的备份数据。

完成上述配置之后,我们还必须考虑如何保证只让主节点处理写请求,而读请求则可以在任意节点上执行。

由此可见,mysql 主从集群的构建具有网络状态 -- 主节点必须先行启动,并且具有存储状态 -- 每个节点需要有自己独立的存储,很显然,用 Deployment 作为控制器来进行 mysql 集群的搭建是无法实现的,而这恰恰是 StatefulSet 擅长处理的场景。

三、主从节点的区分 -- 配置与读写

3.1 主从节点不同的配置文件

mysql 主节点与从节点拥有完全不同的配置,主节点需要开启 log-bin 通过二进制的方式导出 bin-log 来实现主从复制,从节点需要配置主节点的信息外,还需要配置 super-read-only 来实现从节点的只读。

在 Kubernetes 中我们只需要在 ConfigMap 中定义两套配置,然后在 pod 描述中依据不同的 pod 序号选择挂载不同的配置即可。

下面是一个 ConfigMap 的示例:

apiVersion: v1
kind: ConfigMap
metadata:
 name: mysql
 labels:
   app: mysql
data:
  master.cnf: |
   # 主节点MySQL的配置文件
   [mysqld]
   log-bin
  slave.cnf: |
   # 从节点MySQL的配置文件
   [mysqld]
   super-read-only

3.2 用 Service 实现主从的读写分离

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

由于第一个 Service 配置了 clusterIP: None,所以它是一个 Headless Service,我们只用它代理编号为 0 的节点,也就是主节点。

而第二个 Service,由于在 selector 中指定了 app: mysql,所以它会代理所有具有这个 label 的节点,也就是集群中的所有节点。

四、集群初始化

集群启动前,集群初始化步骤有:

  1. 各个节点正确获取对应的 ConfigMap 中的配置文件,并且放置在 mysql 配置文件所在的路径。

  2. 如果节点是从节点,那么需要先将主节点的数据全量拷贝到对应路径下。

  3. 在从节点上执行数据初始化命令。

这些操作我们可以通过定义一系列 InitContainers 来实现。

4.1 根据pod节点名称获取对应的配置文件

对于 StatefulSet 而言,每个 pod 各自的 hostname 中所具有的序号就是它们的唯一 id,因此我们可以通过正则表达式来获取这个 id,并且规定 id 为 0 表示主节点,于是,通过判断 server 的 id,就可以对 ConfigMap 中不同的配置进行获取:

   initContainers:
      - name: init-mysql
        image: harbor.jnlikai.cc/mysql/mysql:5.7.36
        command:
        - bash
        - "-c"
        - |
          set -ex
           # 从 Pod 的序号,生成 server-id
           [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          echo [mysqld] > /mnt/conf.d/server-id.cnf
          # 由于 server-id=0 有特殊含义,我们给 ID 加 100 来避开 0
          echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
           # 如果Pod序号是0,说明它是Master节点,拷贝 master 配置
           # 否则,拷贝 Slave 的配置
          [[ $ordinal -eq 0 ]] && cp /mnt/config-map/master.cnf /mnt/conf.d/ || cp /mnt/config-map/slave.cnf /mnt/conf.d/
        volumeMounts:
        - name: conf
          mountPath: /mnt/conf.d
        - name: config-map
          mountPath: /mnt/config-map

4.2 从节点copy前一个节点上的全量数据

- name: clone-mysql
         image: harbor.jnlikai.cc/mysql/xtrabackup:1.0
         command:
         - bash
         - "-c"
         - |
           set -ex
            # 拷贝操作只需要在第一次启动时进行,所以如果数据已经存在,跳过
            [[ -d /var/lib/mysql/mysql ]] && exit 0
            # Master节点(序号为0)不需要做这个操作
            [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
            ordinal=${BASH_REMATCH[1]}
            [[ $ordinal -eq 0 ]] && exit 0
            # 使用ncat指令,远程地从前一个节点拷贝数据到本地
            ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
            # 执行--prepare,这样拷贝来的数据就可以用作恢复了
            xtrabackup --prepare --target-dir=/var/lib/mysql
         volumeMounts:
         - name: data
           mountPath: /var/lib/mysql
           subPath: mysql
         - name: conf
           mountPath: /etc/mysql/conf.d

在这一部分,我们使用了 ncat 命令实现从上一个已经启动的节点拷贝数据到当前节点,并且使用了第三方的备份还原工具 xtrabackup 来实现数据的恢复。

五、MySQL容器启动

5.1 从节点上数据恢复和每个节点上开启实时同步的端口

在 initContainers 中,我们实现了在从节点中,将上一个节点的备份数据拷贝到当前节点的工作,那么,接下来我们就要去恢复这个数据了。

与此同时,我们还需要在 mysql 的实际运行中实时执行数据的同步、恢复与备份工作。上文提到的 xtrabackup 很方便地实现了这一系列功能。我们可以将这个集成工具作为一个 sidecar 启动,完成上述这些操作:

   - name: xtrabackup
       image: harbor.jnlikai.cc/mysql/xtrabackup:1.0
       ports:
       - name: xtrabackup
         containerPort: 3307
         command:
         - bash
         - "-c"
         - |
         set -ex
         cd /var/lib/mysql
          # 从备份信息文件里读取MASTER_LOG_FILEM和MASTER_LOG_POS这两个字段的值,用来拼装集群初始化SQL
         if [[ -f xtrabackup_slave_info ]]; then
          # 如果xtrabackup_slave_info文件存在,说明这个备份数据来自于另一个Slave节点。这种情况下,XtraBackup工具在备份的时候,就已经在这个文件里自动生成了"CHANGE MASTER TO"     SQL语句。所以,我们只需要把这个文件重命名为change_master_to.sql.in,后面直接使用即可
         mv xtrabackup_slave_info change_master_to.sql.in
          # 所以在这个示例中,我们使用了 ncat 命令实现从上一个已经启动的节点拷贝数据到当前节点,并且使用了第三方的备份还原工具 xtrabackup 来实现数据的恢复。,也就用不着xtra    backup_binlog_info了
         rm -f xtrabackup_binlog_info
         elif [[ -f xtrabackup_binlog_info ]]; then
          # 如果只存在xtrabackup_binlog_inf文件,那说明备份来自于Master节点,我们就需要解析这个备份信息文件,读取所需的两个字段的值
          [[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
         rm xtrabackup_binlog_info
          # 把两个字段的值拼装成SQL,写入change_master_to.sql.in文件
         echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
               MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
         fi
          # 如果change_master_to.sql.in,就意味着需要做集群初始化工作
         if [[ -f change_master_to.sql.in ]]; then
          # 但一定要先等MySQL容器启动之后才能进行下一步连接MySQL的操作
         echo "Waiting for mysqld to be ready (accepting connections)"
         until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done

         echo "Initializing replication from clone position"
            # 将文件change_master_to.sql.in改个名字,防止这个Container重启的时候,因为又找到了change_master_to.sql.in,从而重复执行一遍这个初始化流程
         mv change_master_to.sql.in change_master_to.sql.orig
            # 使用change_master_to.sql.orig的内容,也是就是前面拼装的SQL,组成一个完整的初始化和启动Slave的SQL语句
         mysql -h 127.0.0.1 <<EOF
         $(<change_master_to.sql.orig),
          MASTER_HOST='mysql-0.mysql',
          MASTER_USER='root',
          MASTER_PASSWORD='',
          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"
         volumeMounts:
         - name: data
           mountPath: /var/lib/mysql
           subPath: mysql
         - name: conf
           mountPath: /etc/mysql/conf.d

5.2 MySQL业务容器启动

         - name: mysql
        image: harbor.jnlikai.cc/mysql/mysql:5.7.36
        env:
        - name: MYSQL_ALLOW_EMPTY_PASSWORD
          value: "1"
        ports:
        - name: mysql
          containerPort: 3306
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 500m
            memory: 1Gi
        livenessProbe:
          exec:
            command: ["mysqladmin", "ping"]
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          exec:
           # 通过TCP连接的方式进行健康检查
            command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
          initialDelaySeconds: 5
          periodSeconds: 2
          timeoutSeconds: 1
   volumes:
       - name: conf
         emptyDir: {}
       - name: config-map
         configMap:
           name: mysql
 volumeClaimTemplates:
   - metadata:
       name: data
     spec:
       accessModes: ["ReadWriteOnce"]
       resources:
         requests:
           storage: 10Gi

业务容器里会用到PVC,所以在启动此statefuset控制器之前需要先创建足够的PV,使用NFS server做数据存储端,在10.49.33.147机器上NFS共享如下目录;

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql
spec:
  capacity:
    storage: 50Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /data/mysql
    server: 10.49.33.147
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql1
spec:
  capacity:
    storage: 50Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /data/mysql1
    server: 10.49.33.147

六、测试MySQL集群高可用

755bf78ff63a4a9faf2e38f781da155c.png

测试登录mysql-1可以看到刚才在msyql-0上创建的数据库mysqltests

d8cd0d4bb05d4f8b9b5d7dcaea95ffaa.png在从节点上测试写入会报错:

c500f06bac5b4bf3bd13d70bce2ed9a6.png

手动删除mysql-0 statefuset控制器会立即重建mysql-0,重建之后数据还会存在,因为重建的mysql-0还会挂载原来的PVC

769d77616e514f689eface14613e1c4a.png

2e86d00f285a4f34ad238349451350b3.png

392dc5b2c1f54c6a98864aaef6784ab9.png

参考文章:

实战 Kubernetes StatefulSet -- MySQL 主从集群搭建

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1664212.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计模式-创建型-原型模式-prototype

工作经验类 public class WorkExperience implements Cloneable {private String workDate;private String company;public void setWorkDate(String workDate) {this.workDate workDate;}public void setCompany(String company) {this.company company;}Overridepublic Ob…

【35分钟掌握金融风控策略21】贷前额度策略

目录 贷前策略审批流程和统一额度管理 贷前策略审批流程 统一额度管理 预授信策略 贷前策略审批流程和统一额度管理 贷前包含了多个风控场景&#xff0c;这些风控场景的策略在执行时是否存在先后顺序呢&#xff1f;在贷前&#xff0c;除上述主要的风控场景&#xff0c;还有…

2024年4月12日饿了么春招实习试题【第三题】-题目+题解+在线评测,2024.4.12,饿了么机试【Kruskal 算法, 最小生成树】

2024年4月12日饿了么春招实习试题【第三题】-题目题解在线评测&#xff0c;2024.4.12&#xff0c;饿了么机试 &#x1f3e9;题目一描述&#xff1a;样例1样例2解题思路一&#xff1a;[Kruskal 算法](https://baike.baidu.com/item/%E5%85%8B%E9%B2%81%E6%96%AF%E5%8D%A1%E5%B0%…

【第19章】spring-mvc之全局异常处理

文章目录 前言一、全局异常处理1. 前端2. 后端 二、常见错误页1.增加界面2.web.xml3.异常处理4.效果 总结 前言 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技术也越来越重要&#xff0c;很多人都开启了学习机器学习&#xff0c;本文就介绍了机器学习的基…

决策树的学习(Decision Tree)

1.对于决策树的概念&#xff1a; **本质上&#xff1a;**决策树就是模拟树的结构基于 if-else的多层判断 2.目的&#xff1a; 对实例进行分类的树形结构&#xff0c;通过多层判断&#xff0c;将所提供的数据归纳为一种分类规则。 3.优点&#xff1a; 1.计算量小&#xff0c;…

vs2019 STL库里 判断函数类型的模板 is_function_v 与 is_const_v

&#xff08;1&#xff09;源代码如下&#xff1a; 经简单代码测试后&#xff0c;得出 vs2019 的 c 编译器 和 其 STL 库的观点与设计&#xff1a;is_const_v 用来判断类型 T 内是否含有 const 修饰符&#xff0c;含有 const 则返回真。但若 T 是含有 const 的引用类型&#xf…

loongarch64 electron打包deb改成符合统信测试通过的deb

需要做软件适配统信系统的自主认证。 我之前是在 麒麟 龙芯 loongarch64 电脑上使用 electron 打包的 deb包&#xff1a;麒麟龙芯loongarch64 electron 打包deb包_electron麒麟系统打包的-CSDN博客 安装在统信电脑 处理器&#xff1a;Loongson-3A60000-HV 2.5GHz 可以使用&…

阿里云和AWS负载均衡服务对比分析

在云计算时代,负载均衡作为一种关键的网络基础设施,承担着在多个服务器之间分发网络流量的重要任务。作为全球两大主要的云服务提供商,阿里云和Amazon Web Services(AWS)都提供了强大的负载均衡解决方案。本文将从性能、功能、可用性和成本等方面对两者进行对比分析。我们九河云…

用python写算法——栈笔记

栈 栈的定义相关算法题 栈的定义 1.它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶&#xff0c;相对地&#xff0c;把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈&#xff0c;它是把新元素放到栈顶元素的上面&#xff0…

CLI举例:通过URL分类控制用户访问的网站

华为CLI举例&#xff1a;通过URL分类控制用户访问的网站 配置基于URL分类的URL过滤功能&#xff0c;可以实现对用户访问的某一类网站的控制。既可以是FW自带的预定义分类&#xff0c;也可以是管理员配置的自定义分类。 组网需求 如图1所示&#xff0c;FW作为企业网关部署在网络…

(Java)心得:LeetCode——5.最长回文子串

一、原题 给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同&#xff0c;则该字符串称为回文字符串。 示例 1&#xff1a; 输入&#xff1a;s "babad" 输出&#xff1a;"bab" 解释&#xff1a;"aba"…

ws注入js逆向调用函数

这里需要选择一个文件夹 随便 紫色为修改保存 记得ctrls保存 注入代码如下 (function() {var ws new WebSocket("ws://127.0.0.1:8080")ws.onmessage function(evt) {console.log("收到消息&#xff1a;" evt.data);if (evt.data "exit") {…

2024第十六届“中国电机工程学会杯”数学建模A题思路分析

文章目录 1 赛题思路2 比赛日期和时间3 竞赛信息4 建模常见问题类型4.1 分类问题4.2 优化问题4.3 预测问题4.4 评价问题 5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 比赛日期和时间 报名截止时间&#xff1a;2024…

Edge视频增强功能

edge://flags/#edge-video-super-resolution 搜索Video查找 Microsoft Video Super Resolution 设置为Enabled

模型 空船效应

1 空船效应的应用 1.1 空船效应帮助客户服务人员面对挑战性客户 赵敏是一家大型电信公司的客户服务经理。在一次服务中&#xff0c;一位客户因为网络连接问题而非常愤怒&#xff0c;他通过电话对赵敏大声抱怨&#xff0c;并要求立即解决问题。一般在这种情况下&#xff0c;客…

Linux日常管理和服务器配置(二)

一、在系统中配置FTP服务器&#xff1a; 准备工作&#xff1a; a.下载ftp命令 sudo apt install vsftpd 可以先用命令更新一下库 sudo apt-get update 接着输入 systemctl status vsftpd 检查ftp运行状态 然后进入vsftpd.conf文件中修改write为 vim /etc/vsftpdf.conf …

【教学类-54-01】20240510超级对对碰(圆点拼图)(9*5、0-255随机)

作品展示 背景需求&#xff1a; 奕娃幼儿园小中大班益智区超级对对碰 - 小红书#幼儿园益智区 #幼儿园益智区素材 #幼儿园区域材料 #幼儿园环创https://www.xiaohongshu.com/discovery/item/6279bb4d000000002103be71?app_platformandroid&ignoreEngagetrue&app_ve…

Xilinx 千兆以太网TEMAC IP核简介

Xilinx 公司提供了千兆以太网MAC控制器的可参数化LogiCORET™IP解决方案&#xff0c;通过这个IPCore可以实现FPGA与外部网络物理层芯片的互连。基于Xilinx FPGA 的以太网设计&#xff0c;大大降低了工程的设计复杂度&#xff0c;缩短了开发周期&#xff0c;加快了产品的面市速度…

QT 客户端软件开发

QT 是一种功能强大且灵活的跨平台应用程序开发框架&#xff0c;但也存在一些技术难点&#xff0c;需要开发者仔细考虑和克服。以下是一些常见的 QT 软件开发的技术难点。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1. 跨平台兼容性…

截图文字怎么识别?这里有三种识别方法

截图文字怎么识别&#xff1f;在数字化时代&#xff0c;信息的快速处理和转换成为了提高工作效率的关键。截图文字识别技术&#xff0c;作为连接视觉信息与数字文本的桥梁&#xff0c;极大地便利了我们的工作和生活。它允许用户从图像中提取文字内容&#xff0c;进而编辑、搜索…