StatefulSet[实例]-Mysql集群

Introduce

基于StatefulSet搭建K8s集群中的Mysql集群

  1. —个‘主从复制”(MaserˉSlaveReplicatjon)的MySQL集群;

  2. 有一个主节点(Master);

  3. 有多个从节点(Slave);

  4. 从节点需要能水平扩展;

  5. 所有写操作只能在主节点上执行;

  6. 读操作可以在所有节点上执行

How to work

  1. 通过XtraBackup实现MySql备份

  2. 配置从节点,复制到/var/lib/mysql,执行

    1
    2
    3
    4
    5
    6
    7
    CHANGE MASTER TO
    MASTER_HOST='$masterip';
    MASTER_USER='xxx';
    MATSER_PASSWORK='xxx';
    # 这个文件来资源第一个创建的xtrabackup_binlog_info
    MASTER_LOG_FILE='TheMaster-bin.00001';
    MASTER_LOG_POS=481;
  3. 启动节点备份:

    1
    START SLAVE;
  4. 添加节点

Deploy

  1. ConfigMap-MySql

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: mysql
    labels:
    app: mysql
    data:
    master-conf: |
    # 主节点配置文件
    [mysqld]
    log-bin
    slave.conf: |
    # 从节点配置文件
    super-read-only
  2. svc

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    apiVersion: v1
    kind: Service
    metadata:
    name: mysql-headless
    labels:
    app: mysql
    spec:
    clusterIP: None
    selector:
    app: mysql
    ports:
    - port: 3306
    name: mysql
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: mysql-read
    labels:
    app: mysql
    spec:
    selector:
    app: mysql
    ports:
    - port: 3306
    name: mysql
  3. StatefulSet

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
    name: mysql-statefulset
    spec:
    selector:
    matchLabels:
    app: mysql # 必须匹配 .spec.template.metadata.labels
    serviceName: "mysql-headless"
    replicas: 1 # 默认值是 1
    minReadySeconds: 10 # 默认值是 0
    template:
    metadata:
    labels:
    app: mysql # 必须匹配 .spec.selector.matchLabels
    spec:
    terminationGracePeriodSeconds: 10
    initContainers:
    - name: init-mysql
    image: mysql:8.0.
    command:
    - bash
    - "-c"
    - |
    set -ex
    # 从Pod的序号生成server-id
    [[ `hostname` =~ -([0-9]+) $ ]] exit ]
    ordinal=${BASH_REMATCH[1]}
    echo [mysqld] > /mnt/conf.d/server-id.cnf
    #由于server-1d=0有特殊含义, 因此给ID加一个100避开它
    echo server-id=$(100+$ordinal)) >> /mnt/conf.d/server-id.crd

    if [[ $ordinal -eq 0 ]] ; then
    cp /mnt/config-map/master.conf /mnt/conf.d/
    else
    cp /mnt/config-map/slave.conf /mnt/conf.d/
    fi
    volumeMounts:
    - name: conf
    mountPath: /mnt/conf.d
    - name: config-map
    mountPath: /mnt/config-map
    - name: clone-mysql
    image: grc.io/google-samples/xtrabackup:1.0
    command:
    - bash
    - "-c"
    - |
    set -ex
    # 复制操作只要在第一次启动运行,数据存在就pass
    [ [ -d /var/lib/mysql/mysql ] ] && exit 0
    # 主节点(序号为0)不需要进行该操作
    [[ `hostname` =~ -([0-9+)$ ]] && exit 1
    ordinal=${BASH_REMATCH[1]}
    # 使用nact 远程从一个节点复制数据到本地
    nact --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
    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: xtrabackup
    image: grc.io/google-samples/xtrabackup:1.0
    ports:
    - name: xtrabackup
    containerPort: 3007
    command:
    - bash
    - "-c"
    - |
    set -ex
    cd /var/lib/mysql

    # 从备份文件里面读取MASTER_LOG_FILEM 和 MASTER_LOG_POS 这俩个字段的值
    if [[ -f xtrabackup_slave_info ]] ; then
    # 如果xtrabackup_slave_info 存在,说明备份数据来自另外一个节点
    mv xtrabackup_slave_info change change_master_to.sql.in
    rm -f xtrabackup_slave_info
    elifi [[ -f xtrabackup_binlog_info ]]; then
    # 如果只存在 xtrabackup_binlog_info 文件,说明备份来自主节点
    [[ `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

    # 如果change_master_to.sql.in 存在吗,进行集群初始化
    if [[ -f change_master_to.sql.in ]];then
    echo "Waiting ofr 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文件重命名,防止重复初始化
    mv change_master_to.sql.in change_master_to.sql.in.orig
    mysql -h 127.0.0.1 <<EOF
    $(<change_master_to.sql.in.orig),
    MASTER_HOST='myslq-0.mysql'
    MASTER_USER='root',
    MASTER_PASSWORD='',
    MASTER_CONNECT_RETRY=10;
    START SLAVE;
    EOF
    fi

    # ncat 监听3307,收到传输请求执行 xtrabackup --backup
    exec ncat --listen --keep-one --send-only --max-conns=1 3308 -c \
    " xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root "
    volumeMounts:
    - mountPath: /var/lib/mysql
    name: data
    subPath: mysql
    - mountPath: conf
    name: conf
    subPath: /etc/mysql/conf.d
    - name: mysql
    image: mysql:8.0.26
    env:
    - name: MYSQL_ALLOW_EMPTY_PASSWORD
    value: "1"
    - name: MYSQL_ROOT_PASSWORD
    value: root
    - name: MYSQL_USER_HOST
    value: '%'
    - name: MYSQL_ROOT_HOST
    value: '%'
    - name: MYSQL_PORT
    value: "3306"
    - name: TZ
    value: Asia/Shanghai
    - name: LANG
    value: en_US.UTF-8
    ports:
    - containerPort: 3306
    name: mysql
    volumeMounts:
    - mountPath: /var/lib/mysql
    name: data
    - mountPath: /etc/mysql/conf.d
    name: conf
    resources:
    requests:
    cpu: 500m
    memory: 1Gi
    livenessProbe:
    exec:
    command: [mysqladmin,"ping"]
    initialDelaySeconds: 30
    periodSeconds: 10
    timeoutSeconds: 5
    readinessProbe:
    exec:
    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