Kubernetes控制器(四)______StatefulSet

控制器介绍

StatefulSet: 是Kubernetes中用于管理有状态应用的控制器。与Deployment不同,StatefulSet用于部署和管理需要持久标识、有序部署和唯一网络标识的 Pod。典型的用例包括数据库、缓存和队列等有状态应用。(有状态服务:单点故障整体崩溃。无状态服务:单点故障不影响整体)

  1. 稳定的网络标识符: StatefulSet中的每个Pod都具有稳定的网络标识符,其名称遵循固定的命名模式。即Pod重新调度后其PodName和HostName不变,基于Headless Service(无头服务)来实现。(无头服务:就是跳过这个4层或者7层代理,直接与pod的真实Ip进行访问。正常我们通过nslookup解析到service的ip,而无头服务,通过解析后得到的是后端pod的ip地址)
  2. 有序部署: StatefulSet确保Pod按照固定顺序部署和更新。在Pod启动和停止时,StatefulSet会按照定义的顺序逐个进行,从而确保有序的启动和停止。即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次进行(即从0到N-1,在下一个Pod运行之前的所有Pod必须都是Running和Ready状态),基于init containers来实现。
  3. 持久标识: StatefulSet中的每个Pod都具有持久标识符,通常用于持久化存储(例如持久卷)。即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现。
  4. 有状态服务: StatefulSet管理的Pod可以用作有状态服务。与Deployment不同,StatefulSet管理的Pod具有固定的网络标识符和持久标识符,因此更适合于有状态应用的需求。
  5. 有序收缩,有序删除(即从N-1到0)

从StatefulSet的特点和应用场景发现,StatefulSet通过Headless Service生成可解析的DNS记录;通过volumeClaimTemplates创建pvc和对应的pv绑定;后定义StatefulSet来创建pod。

和Deployment相比:相同的是StatefulSet和Deployment管理了基于相同容器定义的一组Pod。但和Deployment不同的是,StatefulSet为它们的每个Pod维护了一个固定的ID。这些Pod是基于相同的声明来创建的,但是不能相互替换,无论怎么调度,每个Pod都有一个永久不变的ID。你在StatefulSet对象中定义你期望的状态,然后StatefulSet的控制器就会通过各种更新来达到那种你想要的状态。

基于StatefulSet的Mysql部署

在生产环境中数据的存储尤为重要,不管什么哪一类的Mysql高可用(keepalived+双主,MHA,MMM,Heartbeat+DRBD)基础都是Msql的主从复制。Mysql主从同步的过程第一部分就是master记录二进制日志。在每个事务更新数据完成之前,master在二日志记录这些改变。MySQL将事务写入二进制日志。在事件写入二进制日志完成后,master通知存储引擎提交事务。 下一步就是slave将master的binary log拷贝到它自己的中继日志。首先,slave开始一个工作线程——I/O线程。I/O线程在master上打开一个普通的连接,然后开始binlog dump process。Binlog dump process从master的二进制日志中读取事件,如果已经同步了master,它会睡眠并等待master产生新的事件。I/O线程将这些事件写入中继日志。 SQL slave thread(SQL从线程)处理该过程的最后一步。SQL线程从中继日志读取事件,并重放其中的事件而更新slave的数据,使其与master中的数据一致。只要该线程与I/O线程保持一致,中继日志通常会位于OS的缓存中,所以中继日志的开销很小。

k8s部署mysql集群(一主两从)在K8s官网有示例:https://kubernetes.io/zh-cn/docs/tasks/run-application/run-replicated-stateful-application/可供参考。集群读写分离,读数据库通过Service服务访问。通过headless服务对数据库实现写操作同时使Slave数据库与Master数据库实现同步。

部署持久化存储

可参考:https://blog.csdn.net/qq42004/article/details/137113713?spm=1001.2014.3001.5502

修改StorgaeClass对象中的parameters.pathPattern: “ . P V C . n a m e s p a c e / {.PVC.namespace}/ .PVC.namespace/{.PVC.annotations.nfs.io/storage-path}”
为:parameters.pathPattern: " . P V C . n a m e s p a c e / {.PVC.namespace}/ .PVC.namespace/{.KaTeX parse error: Expected 'EOF', got '}' at position 9: PVC.name}̲{.PVC.annotations.nfs.io/storage-path}"在mysql的Pod获取PVC后在存储后端创建的目录增加一层。否则容器创建的持久化目录都在一个目录内。

[root@localhost k8s]# tree -d /mnt/k8s/my
/mnt/k8s/my #命名空间名称:PVC.namespace
├── data-mysql-pass-0 #Pvc名称:$PVC.name
│   └── mysql #StatufulSet中Mountvolume中的subPaht
│       ├── mysql
│       ├── performance_schema
│       ├── sys
│       ├── test
│       └── xtrabackup_backupfiles

部署Service

apiVersion: v1
kind: Service
metadata:
  name: mysql-pass-svc
  labels: 
    ser: mysql-ser 
  namespace: my
spec:
  ports:
  - name: mysql 
    port: 3306
  clusterIP: None
  selector:
    tai: mysql

---

apiVersion: v1
kind: Service
metadata:
  name: mysql-read-pass
  labels:
    app: mysql-ser-node
  namespace: my
spec:
  ports:
  - name: mysql
    port: 3306
    protocol: TCP
    nodePort: 32302
    targetPort: 3306
  selector:
    tai: mysql 
  type: NodePort

部署Secret、ConfigMap

apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
type: Opaque
data:
  bwk: cGFzc3dvcmQxMjM0NTY=  # 这里的值是密码的base64编码表示,echo -n '12345678' | base64
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-cnf-map
  labels:
    app: mysql
  namespace: my 
data:
  primary.cnf: |
    [mysqld]
    log-bin=mysql-bin 
    bind-address = 0.0.0.0
    binlog_format=mixed 
    log-bin-index=mysql-bin.index 
    lower_case_table_names=1
    relay-log-index = slave-relay-bin.index
  replica.cnf: |
    [mysqld]
    bind-address = 0.0.0.0
    super-read-only
    log-bin=mysql-bin 
    relay-log=relay-bin 
    relay-log-index=slave-relay-bin.index 
    lower_case_table_names=1

部署StatefulSet

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql-pass
  labels:
    da: mysql
  namespace: my
spec:
  minReadySeconds: 20
  replicas: 3
  revisionHistoryLimit: 20
  selector:
    matchLabels:
      tai: mysql
  serviceName: mysql-pass-svc
  #定义StatefulSet和Service的headless服务关联
  template:
    metadata:
      annotations: 
        description: "Mysql containers for Master-slave replication"
      name: mysql-containers
      labels:
        tai: mysql
      namespace: mysql
    spec:
      initContainers:
      - name: init-mysql
        image: mysql:5.7
        imagePullPolicy: IfNotPresent
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: bwk
      
        command:
        - bash
        - "-c"
        - |
          set -ex
          # 基于 Pod 序号生成 MySQL 服务器的 ID。
          [[ $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
          # 将合适的 conf.d 文件从 config-map 复制到 emptyDir。
          if [[ $ordinal -eq 0 ]]; then
            cp /mnt/config-map/primary.cnf /mnt/conf.d/
          else
            cp /mnt/config-map/replica.cnf /mnt/conf.d/
          fi          
        volumeMounts:
        - name: conf
          mountPath: /mnt/conf.d
        - name: confg-map
          mountPath: /mnt/config-map
      - name: clone-mysql
        image: docker.io/yizhiyong/xtrabackup:latest
        imagePullPolicy: IfNotPresent    
        command:
        - bash
        - "-c"
        - |
          set -ex
          # 如果已有数据,则跳过克隆。
          [[ -d /var/lib/mysql/mysql ]] && exit 0
          # 跳过主实例(序号索引 0)的克隆。
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          #获取自动定义Pod名称的最后一个字段。即生成名称的mysql-pass-0的最后一个数字。
          [[ $ordinal -eq 0 ]] && exit 0
          # 当等于0的时候推出不等于0的时候执行下面操作,即从原来的对等节点克隆数据。
          ncat --recv-only  mysql-pass-$(($ordinal-1)).mysql-pass-svc  3307 | xbstream -x -C /var/lib/mysql
          # 准备备份。ncat --recv-only 通过headless服务访问访问方式是pod-name.namespace.svc.cluster.local
          xtrabackup --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
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: bwk
        - name: LANG
          value: "C.UTF-8"
          #此处定义语言环境开发会在服务获取字符编码写Dockerfile的时候可以直接变更。
        ports:
          - name: mysql
            containerPort: 3306
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql 
        - name: conf
          mountPath: /etc/mysql/conf.d 
        - name: time
          mountPath: /etc/localtime
        resources:
          requests:
            cpu: 500m
            memory: 1Gi
        livenessProbe:
          exec:
            command: 
            - /bin/sh 
            - -ec
            - >-
              mysqladmin -uroot -p${MYSQL_ROOT_PASSWORD} PING
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          exec:
            # 检查我们是否可以通过 TCP 执行查询(skip-networking 是关闭的)。
            command:
            - /bin/sh
            - -ec 
            - >-
              mysql -h127.0.0.1 -uroot -p$MYSQL_ROOT_PASSWORD -e'SELECT 1' 
          initialDelaySeconds: 5
          periodSeconds: 2
          timeoutSeconds: 1
      - name: xtrabackup
        image: docker.io/yizhiyong/xtrabackup:latest
        imagePullPolicy: IfNotPresent
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: bwk
        ports:
        - name: xtrabackup
          containerPort: 3307
        command:
        - bash
        - "-c"
        - |
          set -ex
          cd /var/lib/mysql

          # 确定克隆数据的 binlog 位置(如果有的话)。
          if [[ -f xtrabackup_slave_info && "x$(<xtrabackup_slave_info)" != "x" ]]; then
            # XtraBackup 已经生成了部分的 “CHANGE MASTER TO” 查询
            # 因为我们从一个现有副本进行克隆。(需要删除末尾的分号!)
            cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
            # 在这里要忽略 xtrabackup_binlog_info (它是没用的)。
            rm -f xtrabackup_slave_info xtrabackup_binlog_info
          elif [[ -f xtrabackup_binlog_info ]]; then
            # 我们直接从主实例进行克隆。解析 binlog 位置。
            [[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
            rm -f xtrabackup_binlog_info xtrabackup_slave_info
            echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
                  MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
          fi

          # 检查我们是否需要通过启动复制来完成克隆。
          if [[ -f change_master_to.sql.in ]]; then
            echo "Waiting for mysqld to be ready (accepting connections)"
            until mysql -h 127.0.0.1 -uroot -p${MYSQL_ROOT_PASSWORD} -e "SELECT 1"; do sleep 1; done

            echo "Initializing replication from clone position"
            mysql -h 127.0.0.1 -uroot -p${MYSQL_ROOT_PASSWORD} \
                  -e "$(<change_master_to.sql.in), \
                          MASTER_HOST='mysql-pass-0.mysql-pass-svc', \
                          #访问的mysql master的Pod,此时master容器已经启动名称以及确定。
                          MASTER_USER='root', \
                          MASTER_PASSWORD='${MYSQL_ROOT_PASSWORD}', \
                          MASTER_CONNECT_RETRY=10; \
                         START SLAVE;" || exit 1
            # 如果容器重新启动,最多尝试一次。
            mv change_master_to.sql.in change_master_to.sql.orig
            #修改change_master_to.sql.in防止重启后再次找到文件重克隆
          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=${MYSQL_ROOT_PASSWORD}"  

        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
      volumes:
      - name: time
        hostPath: 
          path: /etc/localtime
          type: File
      - name: conf
        emptyDir: {}
      - name: confg-map
        configMap:
          name: mysql-cnf-map
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      storageClassName: "nfs-stgc-delete"
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 1Gi
      

容器挂载路径图:
在这里插入图片描述

  • StatefulSet.spec.serviceName配置headless服务名称。
  • 在容器init-mysql、mysql、xtrabackup添加环境变量配置mysql密码。clone-mysql和xtrabackup需要密码的进行修改
  • 在clone-mysql、xtrabackup访问Master的位置修改访问的主机名。
  • 挂载服务时间。

部署测试

查看Pod状态

[root@master my]# kubectl get pod -n my 
NAME           READY   STATUS    RESTARTS   AGE
mysql-pass-0   2/2     Running   0          3h6m
mysql-pass-1   2/2     Running   0          3h5m
mysql-pass-2   2/2     Running   0          3h5m

查看Service信息

kubectl describe svc mysql-pass-svc/mysql-read-pass -n my

查看时间同步:

kubectl exec mysql-pass-0 -c mysql -it -n my -- /bin/bash进入容器

bash-4.2# date
Sun Mar 31 15:29:12 CST 2024
bash-4.2# exit
[root@master my]# date
2024年 03月 31日 星期日 15:29:15 CST

mysql> select now();
 +---------------------+
 | now()               |
 +---------------------+
 | 2024-03-31 15:31:10 |
 +---------------------+
 1 row in set (0.00 sec)

测试mysql集群

[root@master my]#  kubectl run mysql-client --image=mysql:5.7 -i --rm --restart=Never --\
>   mysql -h mysql-pass-0.mysql-pass-svc.my -uroot -p12345678 <<EOF
> CREATE DATABASE test;
> CREATE TABLE test.messages (message VARCHAR(250));
> INSERT INTO test.messages VALUES ('hello');
> EOF
If you don't see a command prompt, try pressing enter.
pod "mysql-client" deleted
[root@master my]# kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
>   mysql -h mysql-read-pass.my -uroot -p12345678 -e "SELECT * FROM test.messages"
mysql: [Warning] Using a password on the command line interface can be insecure.
 +---------+
 | message |
 +---------+
 | hello   |
 +---------+
 pod "mysql-client" deleted

相关推荐

  1. KubernetesStatefulSet基本原理

    2024-04-04 03:58:01       12 阅读
  2. K8s: 控制器StatefulSets对象

    2024-04-04 03:58:01       11 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-04 03:58:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-04 03:58:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-04 03:58:01       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-04 03:58:01       20 阅读

热门阅读

  1. 用C++编写“多功能双人五子棋”游戏

    2024-04-04 03:58:01       14 阅读
  2. OpenJudge - 18:验证子串

    2024-04-04 03:58:01       14 阅读
  3. [leetcode] 61. 旋转链表

    2024-04-04 03:58:01       16 阅读
  4. P8597 [蓝桥杯 2013 省 B] 翻硬币

    2024-04-04 03:58:01       18 阅读
  5. 反转链表1

    2024-04-04 03:58:01       20 阅读