[TOC]

0x00 记一次在K8s集群搭建的MySQL主从无法正常启动之数据迁移恢复实践

描述: 在K8s集群中里利用bitnami提供的mysql:5.7.32-debian-10-r61镜像并利用helm进行MySQL集群的部署安装, 在进行网络调整后发现mysql-master-0 pod无法正常启动,导致MySQL数据库无法访问,同时设定的root密码被更改导致无法提取持久化目录中相关数据信息,我们可以依据如下方式进行错误排查以及数据恢复。


前置知识


环境说明:

1
2
3
4
5
6
7
Kubernetes 集群版本: v1.22.2
mysql 镜像及其版本: docker.io/bitnami/mysql:5.7.32-debian-10-r61

kubectl get pod -n database -l app=mysql
# NAME READY STATUS RESTARTS AGE
# mysql-master-0 0/0 Error 0 11d
# mysql-slave-0 1/1 Running 0 11d


操作流程:

  • Step 0.操作任何数据之前,第一要务就是必须要进行数据备份,防止在操作的过程中对数据造成破坏。
    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
    # 查看 mysql-master 持久化的 PVC 信息
    $ kubectl get pvc -n database | grep "mysql"
    data-mysql-master-0 Bound pvc-4bef6f99-8a7a-47f4-8517-18721fa5d724 8Gi RWO nfs-devops 304d
    data-mysql-slave-0 Bound pvc-2e5de4c5-a1a4-4c79-8e5b-fc21b66d6221 8Gi RWO nfs-devops 304d

    # 进入到持久化目录中
    $ kubectl describe pv -n database pvc-4bef6f99-8a7a-47f4-8517-18721fa5d724 | grep "Path"
    Path: /Kubernetes/pvc/devops/database-data-mysql-master-0-pvc-4bef6f99-8a7a-47f4-8517-18721fa5d724

    # 备份持久化的数据
    $ cp -a /Kubernetes/pvc/devops/database-data-mysql-master-0-pvc-4bef6f99-8a7a-47f4-8517-18721fa5d724 /tmp/mysql-backup
    $ ls /tmp/mysql-backup/data | head -n 15
    auto.cnf
    ca-key.pem
    ca.pem
    client-cert.pem
    client-key.pem
    auth
    ib_buffer_pool
    ibdata1
    ib_logfile0
    ib_logfile1
    ibtmp1
    msg
    my_database
    mysql
    mysql-bin.000001


  • Step 1.K8s中资源服务清单部署的应用无法启动错误信息排查思路。
    1
    2
    3
    4
    5
    6
    7
    8
    # sts 资源查看
    kubectl get sts -n database mysql-master
    # 查看资源控制器创建的pod日志
    kubectl logs -f --tail 50 -n database mysql-master-0
    # 查看 pod 详细信息以及事件
    kubectl describe pod -n database mysql-master-0
    # 查看 pod 启动状态
    kubectl get pod -n database -l app=mysql


  • Step 2.通过前面错误信息我知道了,mysql-master-0 Pod无法启动的原因是,因为该资源清单了设置Pod健康检查即Liveness探针与Readiness探针,而正是因为root密码被修改导致Pod无法重新启动,从而导致无法正常提供服务。
    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
    # livenessProbe 探针 -- 
    livenessProbe:
    exec:
    command:
    - sh
    - -c
    - |
    password_aux="${MYSQL_ROOT_PASSWORD:-}"
    if [ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]; then
    password_aux=$(cat $MYSQL_ROOT_PASSWORD_FILE)
    fi
    mysqladmin status -uroot -p$password_aux
    failureThreshold: 3 # 失效阈值为3次
    initialDelaySeconds: 120 # 初始化延迟时间120s
    periodSeconds: 10 # 探测周期,默认情况下是 10 秒。
    successThreshold: 1 # 成功阈值为3次
    timeoutSeconds: 1 # 超时时间1s

    # readinessProbe 探针 --
    readinessProbe:
    exec:
    command:
    - sh
    - -c
    - |
    password_aux="${MYSQL_ROOT_PASSWORD:-}"
    if [ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]; then
    password_aux=$(cat $MYSQL_ROOT_PASSWORD_FILE)
    fi
    mysqladmin status -uroot -p$password_aux
    failureThreshold: 3
    initialDelaySeconds: 30
    periodSeconds: 10
    successThreshold: 1
    timeoutSeconds: 1


  • Step 3.查看database名称空间下的secrets资源中设定的mysql相关凭据(默认以Base64编码)。
    1
    2
    3
    4
    5
    6
    7
    $ kubectl get secrets -n database mysql -o yaml
    apiVersion: v1
    data:
    mysql-password: dXNlcnBhc3N3ZA== # userpasswd
    mysql-replication-password: cmVwbGljYXRpb24= # replication
    mysql-root-password: cm9vdHBhc3N3ZA== # rootpasswd 由于root密码被其它开发人员进行更改而又忘记导致。
    .....


  • Step 4.分别查看database名称空间下的mysql-mastermysql-slave的configMap配置文件(实际上配置差不多)。
    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
    $ kubectl get cm -n database mysql-master -oyaml
    $ kubectl get cm -n database mysql-slave -oyaml
    apiVersion: v1
    data:
    my.cnf: |-
    [mysqld]
    bind-address=0.0.0.0
    port=3306
    skip-name-resolve
    explicit_defaults_for_timestamp
    basedir=/opt/bitnami/mysql
    plugin_dir=/opt/bitnami/mysql/plugin
    socket=/opt/bitnami/mysql/tmp/mysql.sock
    datadir=/bitnami/mysql/data
    tmpdir=/opt/bitnami/mysql/tmp
    pid-file=/opt/bitnami/mysql/tmp/mysqld.pid
    log-error=/opt/bitnami/mysql/logs/mysqld.log
    character-set-server=UTF8
    collation-server=utf8_general_ci
    max_allowed_packet=160M
    default_authentication_plugin=mysql_native_password

    [client]
    port=3306
    socket=/opt/bitnami/mysql/tmp/mysql.sock
    default-character-set=UTF8
    plugin_dir=/opt/bitnami/mysql/plugin

    [manager]
    port=3306
    socket=/opt/bitnami/mysql/tmp/mysql.sock
    pid-file=/opt/bitnami/mysql/tmp/mysqld.pid


  • Step 5.前面说到由于mysql数据库的root密码被修改无法通过Pod容器探针检测所以无法启用, 所以我们可以先将资源清单中的livenessProbe与readinessProbe这两部分先停用,以恢复业务正常运行,等待晚上访问流量较小时,关闭应用进行停机维护重置root密码,此时我们可以进行如下操作。

    1
    2
    # 编辑 mysql-master 的 configMap 配置文件,在 [mysqld] 行之下加入 skip-grant-tables 字符串, 然后 使用vim 的 :wq 进行保存。
    kubectl edit cm -n database mysql-master


  • Step 6.等待 mysql-master-0 Pod运行成功正常后进入到容器内部shell之中,随之执行如下命令。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    # 进入 mysql-master-0 Pod 容器内部
    kubectl exec -it -n database mysql-master-0 bash

    # 此时我们可以利用root用户以及空密码登陆 MySQL 服务器中,执行如下SQL语句。
    mysql -u root -p
    mysql> status;
    mysql> update mysql.user set authentication_string = password("rootpasswd") where user="root";
    mysql> update mysql.user set authentication_string = password("userpasswd") where user="user";
    mysql> update mysql.user set authentication_string = password("replication") where user="replicator";
    # 刷新用户权限信息必须
    mysql> flush privileges;

    # 查看用户信息
    mysql> select host,user,authentication_string from mysql.user;
    +-----------+---------------+-------------------------------------------+
    | host | user | authentication_string |
    +-----------+---------------+-------------------------------------------+
    | % | user | *FBBDC6FD2E******A97D36A86BEAC3 |
    | % | root | *88CFEE431A******C570D24CEB3C7A167F |
    | localhost | mysql.sys | *THISISNOTA******SSWORDTHATCANBEUSEDHERE |
    | % | replicator | *32005F59F5******E5AA8898DE51D456CD90 |
    | localhost | mysql.session | *THISISNOTA******ORDTHATCANBEUSEDHERE |
    +-----------+---------------+-------------------------------------------+
    5 rows in set (0.00 sec)


  • Step 7.修改完成后将 mysql-master 的 configMap 设置的 my.cnf 配置文件中的 skip-grant-tables 字符串去掉,重启Pod即可。
    1
    2
    3
    4
    kubectl get pod -n database -l app=mysql
    # NAME READY STATUS RESTARTS AGE
    # mysql-master-0 1/1 Running 0 3m12s
    # mysql-slave-0 1/1 Running 0 65s

至此, MySQL集群故障恢复完成! 除此之外我们还可以通过独立的Docker容器将其数据备份出来,例如下节的数据迁移恢复


数据迁移恢复

  • Step 1. 准备一份配置文件给docker单独部署的容器进行数据恢复使用,我们需要在配置中加入 skip-grant-tables 参数, 忽略root密码进行登陆到mysql中。
    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
    tee /tmp/my.cnf <<'EOF'
    [mysqld]
    bind-address=0.0.0.0
    port=3306
    skip-grant-tables
    skip-name-resolve
    explicit_defaults_for_timestamp
    basedir=/opt/bitnami/mysql
    socket=/opt/bitnami/mysql/tmp/mysql.sock
    datadir=/bitnami/mysql/data
    tmpdir=/opt/bitnami/mysql/tmp
    pid-file=/opt/bitnami/mysql/tmp/mysqld.pid
    log-error=/opt/bitnami/mysql/logs/mysqld.log
    character-set-server=UTF8
    collation-server=utf8_general_ci
    max_allowed_packet=16M
    default_authentication_plugin=mysql_native_password

    [client]
    port=3306
    socket=/opt/bitnami/mysql/tmp/mysql.sock
    default-character-set=UTF8

    [manager]
    port=3306
    socket=/opt/bitnami/mysql/tmp/mysql.sock
    pid-file=/opt/bitnami/mysql/tmp/mysqld.pid
    EOF


  • Step 2.执行如下命令,并利用前面备份的持久化数据的文件创建一个新的MySQL数据库容器。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 创建 MySQL 容器
    docker run -d --name mysql \
    -e BITNAMI_DEBUG=true \
    -e ALLOW_EMPTY_PASSWORD=yes \
    -e MYSQL_AUTHENTICATION_PLUGIN=mysql_native_password \
    -e MYSQL_USER=user -e MYSQL_PASSWORD=userpasswd \
    -v /tmp/mysql-backup/data:/bitnami/mysql/data \
    -v /tmp/my.cnf:/opt/bitnami/mysql/conf/my.cnf \
    -v /tmp/logs:/opt/bitnami/mysql/logs \
    -p 32160:3306 \
    bitnami/mysql:5.7.32-debian-10-r61

    # 查看 mysql 容器启动日志
    docker logs -f --tail 50 mysql


  • Step 3.同样进入到mysql容器内部shell中执行如下命令, 查看并导出相应数据库的sql文件。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    docker exec -it mysql bash
    mysql -u root -p
    mysql> show databases;
    | auth |
    | msg |
    | my_database |

    # 解决:终端备份数据库乱码(指定编码以及导出的文件路径)
    cd /opt/bitnami/mysql/logs
    mysqldump -uroot -p --default-character-set=UTF8 --databases auth --hex-blob --result-file=auth.sql
    mysqldump -uroot -p --default-character-set=UTF8 --databases msg --hex-blob --result-file=msg.sql
    mysqldump -uroot -p --default-character-set=UTF8 --databases my_database --hex-blob --result-file=my_database.sql


  • Step 4.在本机的/tmp/logs目录中我们可以查看到mysqldump出的sql文件。
    1
    2
    ls /tmp/logs
    auth.sql msg.sql my_database.sql mysqld.log


  • Step 5.如果需要外部使用该Docker搭建的MySQL数据库,你需要更改用户对应的账号密码并刷新权限,在my.cnf配置文件中去除skip-grant-tables字段,并重启容器即可。
    1
    2
    3
    4
    $ docker restart mysql
    $ docker ps -a
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    c72268dc95cf bitnami/mysql:5.7.32-debian-10-r61 "/opt/bitnami/script…" 11 days ago Start (0) 29 hours ago 0.0.0.0:3306 mysql

至此,K8s集群搭建的MySQL数据库迁移恢复实践完毕!