kunpengcompute / kunpengcompute.github.io

Kunpeng Tech Blog: https://kunpengcompute.github.io/
Apache License 2.0
17 stars 5 forks source link

Mysql Innodb cluster on ARM64概述 #54

Open wangxiyuan opened 4 years ago

wangxiyuan commented 4 years ago

作者: wangxiyuan

本文介绍Mysql 8.0的Innodb cluster架构,以及在arm64上的部署流程

概述

Mysql Innodb cluster是Mysql 在HA场景下推荐的一种部署模型。支持多节点集群部署,保证Mysql的高可用性。需要配套Mysql Router使用,并建议使用Mysql shell进行部署。

架构

innodb_cluster_overview

如图所以,一个Mysql Innodb cluster主要由三个部分组成:

  1. Mysql HA集群。集群中包含多个Mysql服务,每个服务使用Innodb后端,并配置Group Replication。

  2. Mysql Router。提供前端Load Balance能力。

  3. Mysql Shell以及其他Client。前端客户端,用来部署、使用Cluster。

实战

本文使用源码编译的方式部署。

要求

  1. 3节点ARM64环境。

  2. Mysql使用Innodb存储引擎,并开启了Group Replication功能。

  3. Mysql Shell依赖Python。需要安装python3和python3-dev

  4. 必要的C/C++编译工具,例如gcc、gcc-c++、autoconf、cmake、make等等,不再赘述。

安装

  1. Mysql Server

    Mysql Server 8.0编译很简单。

    git clone https://github.com/mysql/mysql-server
    mkdir mysql-server/bld
    cd mysql-server/bld
    cmake .. -DDOWNLOAD_BOOST=1 -DWITH_BOOST={boost download folder}
    make -j8
  2. Mysql-shell

    Mysql官方没提供arm64的安装包,需要手动编译。

    $ git clone https://github.com/mysql/mysql-shell
    $ mkdir mysql-shell/bld
    $ cd mysql-shell/bld
    $ cmake .. -DDOWNLOAD_BOOST=1 -DENABLE_DOWNLOADS=1 -DMYSQL_SOURCE_DIR=/opt/mysql-server -DMYSQL_BUILD_DIR=/opt/mysql-server/bld -DHAVE_PYTHON=1
    $ make -j4

    这里有个问题:

    Mysql-shell依赖protobuf和Mysql server,而Mysql server也依赖protobuf。这两个protobuf要版本一致。不一致的话Mysql-shell无法编译通过
    
    但是Mysql server在代码中内置了自己fork的非官方protobuf。在默认编译参数下,Mysql Server会使用这个protobuf。
    
    因此这里有两个解决方法:
    
    1. 统一使用Mysql server的非官方protobuf,注意这里的protobuf必须是静态编译的(即有libprotobuf.a文件,而不是.so),这是Mysql-shell的依赖要求。但Mysql8.0默认动态编译protobuf,因此在编译Mysql需要给cmake命令添加 `-Dprotobuf_BUILD_SHARED_LIBS=OFF`参数。
    
    2. 统一使用官方protobuf,提前安装好protobuf,并在编译Mysql server时指定 `-DWITH_PROTOBUF=system`,编译Mysql-shell时指定`-DProtobuf_INCLUDE_DIR`
    
  3. Mysql Router

    Mysql Router官方也没有提供arm64安装包,也需要手动编译。但自Mysql 8.0以后,Mysql Router的源码已经合并到Mysql server中,因此编译Mysql server后,自带了Mysql Router。

部署

官方提供了两种部署方式:

  1. 沙盒(测试)环境。用户可以在单节点上体验、测试Innodb Cluster。

  2. 生产环境。多节点部署Innodb Cluster

本文先使用沙盒模式体验一下Innodb Cluster,然后再部署生产环境并测试Innodb Cluster。

沙盒环境

  1. 进入mysql-shell的bld/bin目录,执行./mysqlsh。mysql-shell支持多种语言的API,我们在前面章节编译的mysql-shell使用的是python,因此执行./mysqlsh后,可以看到mysql-py >这样的命令行提示符。Mysql官方文档使用的是mysql-js,与本文不同。以下的操作和命令都在mysql-py命令行中执行。

  2. 创建sandbox Instance

    dba.deploy_sandbox_instance(3310)

    根据提示创建密码后报错,找不到mysql,这里需要把之前编译要的Mysq server加入到PATH中

    PATH=$PATH:/opt/mysql-server/bld/bin/

    然后重复执行创建命令,成功后显示如下:

    mysql-py> dba.deploy_sandbox_instance(3310)
    A new MySQL sandbox instance will be created on this host in
    /root/mysql-sandboxes/3310
    
    Warning: Sandbox instances are only suitable for deploying and
    running on your local machine for testing purposes and are not
    accessible from external networks.
    
    Please enter a MySQL root password for the new instance: ****
    
    Deploying new MySQL instance...
    
    Instance localhost:3310 successfully deployed and started.
    Use shell.connect('root@localhost:3310') to connect to the instance.

    此时在$HOME目录下生成了/mysql-sandboxes/3310目录。并且拉起了两个如下进程。

    ./bin/mysqld --user mysql
    /root/mysql-sandboxes/3310/bin/mysqld --defaults-file=/root/mysql-sandboxes/3310/my.cnf --user=root

    然后在拉起两个Instance,组成Cluster

    dba.deploy_sandbox_instance(3320)
    dba.deploy_sandbox_instance(3330)

    查看进程,发现多了两个sandbox进程,分别对应新创的Instance。

  3. 组成Cluster

    3个Mysql Instance创建成功后,我们把他们配置成Cluster。登录Instance并创建cluster。

    shell.connect('root@localhost:3310')
    
    cluster=dba.create_cluster('testCluster')

    报错

    Traceback (most recent call last):
    File "<string>", line 1, in <module>
    SystemError: RuntimeError: Dba.create_cluster: error installing plugin 'group_replication': 127.0.0.1:3310: Can't open shared library '/opt/mysql-server/bld/lib/plugin/group_replication.so' (errno: 0 /opt/mysql-server/bld/lib/plugin/group_replication.so: cannot open shared object file: No such file or directory)

    这是mysql编译后直接只用bld目录的问题,手动创建该软连接。

    $ ln -s  /opt/mysql-server/bld/plugin_output_directory/ /opt/mysql-server/bld/lib/
    $ mv /opt/mysql-server/bld/lib/plugin_output_directory/ /opt/mysql-server/bld/lib/plugin/

    再次执行create命令,成功。然后给Cluster中添加Instance。

    cluster.add_instance('root@localhost:3320')
    cluster.add_instance('root@localhost:3330')

    至此,sandbox Innodb Cluster部署完成,查询Cluster状态。

    mysql-py []> cluster.status()
    {
        "clusterName": "testCluster",
        "defaultReplicaSet": {
            "name": "default",
            "primary": "127.0.0.1:3310",
            "ssl": "REQUIRED",
            "status": "OK",
            "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
            "topology": {
                "127.0.0.1:3310": {
                    "address": "127.0.0.1:3310",
                    "mode": "R/W",
                    "readReplicas": {},
                    "replicationLag": null,
                    "role": "HA",
                    "status": "ONLINE",
                    "version": "8.0.21"
                },
                "127.0.0.1:3320": {
                    "address": "127.0.0.1:3320",
                    "mode": "R/O",
                    "readReplicas": {},
                    "replicationLag": null,
                    "role": "HA",
                    "status": "ONLINE",
                    "version": "8.0.21"
                },
                "127.0.0.1:3330": {
                    "address": "127.0.0.1:3330",
                    "mode": "R/O",
                    "readReplicas": {},
                    "replicationLag": null,
                    "role": "HA",
                    "status": "ONLINE",
                    "version": "8.0.21"
                }
            },
            "topologyMode": "Single-Primary"
        },
        "groupInformationSourceMember": "127.0.0.1:3310"
    }
  4. 配置Mysql Router

    Mysql Server 8.0自带了Mysql Router,命令是mysqlrouter,在bld的bin目录下。执行初始化命令:

    mysqlrouter --bootstrap root@localhost:3310 --user=root

    返回如下信息:

    - Creating account(s) (only those that are needed, if any)
    - Verifying account (using it to run SQL queries that would be run by Router)
    - Storing account in keyring
    - Adjusting permissions of generated files
    - Creating configuration /opt/mysql-server/bld/mysqlrouter.conf
    
    # MySQL Router configured for the InnoDB Cluster 'testCluster'
    
    After this MySQL Router has been started with the generated configuration
    
       $ /etc/init.d/mysqlrouter restart
    or
       $ systemctl start mysqlrouter
    or
       $ mysqlrouter -c /opt/mysql-server/bld/mysqlrouter.conf
    
    the cluster 'testCluster' can be reached by connecting to:
    
    ## MySQL Classic protocol
    
    - Read/Write Connections: localhost:6446
    - Read/Only Connections:  localhost:6447
    
    ## MySQL X protocol
    
    - Read/Write Connections: localhost:64460
    - Read/Only Connections:  localhost:64470

    该命令会在mysql-server/bld/生成mysqlrouter.conf配置文件。

    根据返回提示信息,启动mysql router:

    mysqlrouter -c /opt/mysql-server/bld/mysqlrouter.conf

    又报错了:

    Error: Loading plugin for config-section '[metadata_cache:testCluster]' failed: /opt/mysql-server/bld/runtime_output_directory/../lib/mysqlrouter/metadata_cache.so: cannot open shared object file: No such file or directory

    这个错误和之前group_replication.so的问题一样,添加新的软链接即可:

    $ ln -s  /opt/mysql-server/bld/plugin_output_directory/ /opt/mysql-server/bld/lib/
    
    $ mv /opt/mysql-server/bld/lib/plugin_output_directory /opt/mysql-server/bld/lib/mysqlrouter

    再次启动,成功。这时就可以通过Mysql Router的6446端口访问Mysql Innodb Cluster了。在Mysql-shell中:

    mysql-py> \connect root@localhost:6446
    mysql-py []> \sql
    mysql-sql []> select @@port;
    +--------+
    | @@port |
    +--------+
    |   3310 |
    +--------+

    成功登入

生产环境

这里我手里没有3个物理环境,因此采用容器化方式模拟三节点,刚好可以测试一下msyql的容器化能力。

准备

提前准备三个my.cnf,命名为my1.cnf my2.cnf my3.cnf, 注意配置项report-host依次为172.8.0.100 172.8.0.101 172.8.0.102, server_id依次为1 2 3

# For advice on how to change settings please see
# http://dev.mysql.com/doc/refman/8.0/en/server-configuration-defaults.html

[mysqld]
#
# Remove leading # and set to the amount of RAM for the most important data
# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
# innodb_buffer_pool_size = 128M
#
# Remove leading # to turn on a very important data integrity option: logging
# changes to the binary log between backups.
# log_bin
#
# Remove leading # to set options mainly useful for reporting servers.
# The server defaults are faster for transactions and fast SELECTs.
# Adjust sizes as needed, experiment to find the optimal values.
# join_buffer_size = 128M
# sort_buffer_size = 2M
# read_rnd_buffer_size = 2M

# Remove leading # to revert to previous value for default_authentication_plugin,
# this will increase compatibility with older clients. For background, see:
# https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_default_authentication_plugin
# default-authentication-plugin=mysql_native_password
skip-host-cache
skip-name-resolve
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
secure-file-priv=/var/lib/mysql-files
user=mysql

pid-file=/var/run/mysqld/mysqld.pid

enforce_gtid_consistency=ON
gtid_mode=ON
report-host = 172.8.0.100
server_id=1

再创建自定义docker网络

docker network create --subnet=172.18.0.0/24 my_bridge
部署

使用docker启动3个mysql容器,我这里准备的my.cnf文件在root目录下.另外,由于在使用mysql-shell部署集群中,需要重启mysql,为了保证容器不退出,这里在mysql2和mysql3上手动执行死循环命令。

docker run --name=mysql1 --network=my_bridge --ip 172.18.0.100 -v /root/my1.cnf:/etc/my.cnf -d mysql/mysql-server:8.0.22
docker run --name=mysql2 --network=my_bridge --ip 172.18.0.101 -v /root/my2.cnf:/etc/my.cnf -d mysql/mysql-server:8.0.22 /bin/bash -c "while true;do sleep 10;done"
docker run --name=mysql3 --network=my_bridge --ip 172.18.0.102 -v /root/my3.cnf:/etc/my.cnf -d mysql/mysql-server:8.0.22 /bin/bash -c "while true;do sleep 10;done"

由于改写了mysql2和3的容器命令,这里我们要手动拉起其中的myql,以mysql2为例

$ docker exec -it mysql2 bash
$ /entrypoint.sh mysqld&
$ exit
初始化配置

查看mysql初始密码:

docker logs mysql1 2>&1 | grep GENERATED
mysql2和mysql3的秘密在上一步手动拉起的屏显中

依次登录mysql容器并配置。以mysql1为例

docker exec -it mysql1 bash

mysql -uroot -p{初始密码}

alter user 'root'@'localhost' identified by 'root';
create user 'root'@'%' identified by 'root';
grant all privileges on *.* to 'root'@'%' with grant option;

验证,登录mysql:

mysql -h172.18.0.100 -uroot -proot
集群配置

使用mysql-shell进行配置,由于我的环境没有mysql-shell,直接进入mysql1容器使用自带的mysql-shell

$ docker exec -it mysql1 bash
$ mysqlsh
mysqlsh默认是JS模式,可以使用\py切换到python模式。之前的sandbox章节使用的是python,这是我们就使用JavaScript吧
  1. 检查配置项 以此执行dba.checkInstanceConfiguration("root@{三个容器的IP}:3306"),返回OK
{
    "status": "ok"
}
  1. 连接主节点,创建集群
shell.connect('root@172.18.0.100:3306')
var cluster = dba.createCluster('testCluster')
  1. 新增节点
cluster.addInstance('root@172.18.0.101:3306')
cluster.addInstance('root@172.18.0.102:3306')

注意,在添加节点过程中,mysql会重启,但mysql-shell会拉起服务失败,需要手动上去拉起,另开一个bash:

$ docker exec -ti mysql2 bash
$ mysqld&
$ exit
  1. 检查状态

此时,3节点Innodb Cluster已经部署完成,检查集群状态:

cluster.status()

{
    "clusterName": "testCluster", 
    "defaultReplicaSet": {
        "name": "default", 
        "primary": "172.18.0.100:3306", 
        "ssl": "REQUIRED", 
        "status": "OK", 
        "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", 
        "topology": {
            "172.18.0.100:3306": {
                "address": "172.18.0.100:3306", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "replicationLag": null, 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.0.22"
            }, 
            "172.18.0.101:3306": {
                "address": "172.18.0.101:3306", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "replicationLag": null, 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.0.22"
            }, 
            "172.18.0.102:3306": {
                "address": "172.18.0.102:3306", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "replicationLag": null, 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.0.22"
            }
        }, 
        "topologyMode": "Single-Primary"
    }, 
    "groupInformationSourceMember": "172.18.0.100:3306"
}
  1. Mysql router

mysql官方没有提供mysql router的arm64版本,mysql-server镜像中也没有集成mysql router。因此只能使用非容器化方式,步骤与上一章沙箱方式一样,不再赘述。

使用

Mysql-shell主要提供了两大类命令:dba.xxx和cluster.xxxx。使用cluster的命令要先获取cluster对象,使用dba.get_cluster()命令。通过命令\help dba\help cluster查询命令详情。使用Mysql-shell可以配置、使用Mysql innodb cluster。 当然直接使用mysql client也是可以的。

普通用户直接访问mysql router对外暴露的统一端口即可。