Docker镜像分享:一键部署MariaDB Galera Cluster集群 | 张戈博客

  • 时间:
  • 浏览:6
  • 来源:木木娱乐网_提供晓轩资源网技术_技术QQ网资讯

一、底部形态介绍

1、概述

MariaDB Galera Cluster(下文简称MGC集群),是一套在MySQL innodb存储引擎后面 实现多主、数据实时同步以及强一致性的关系存储架构,业务层面不不做读写分离工作,数据库读写压力都能按照既定的规则分发到 各个节点上去,在数据方面完正兼容 MariaDB 和 MySQL。

2、功能底部形态

  • 同步群克隆 Synchronous replication
  • Active-active multi-master 拓扑逻辑
  • 可对集群中任一节点进行数据读写
  • 自动成员控制,故障节点自动从集群中移除
  • 自动节点加入
  • 真正并行的群克隆,基于行级
  • 直接客户端连接,原生的 MySQL 接口
  • 每个节点都中有 完正的数据副本
  • 多台数据库中数据同步由 wsrep 接口实现

3、局限性

  • 目前的群克隆仅仅支持InnoDB存储引擎,任何写入全都引擎的表,包括mysql.*表将不不群克隆,全都DDL语录会被群克隆的,全都创建用户全都被群克隆,全都insert into mysql.user…将不不被群克隆的.
  • DELETE操作不支持那么主键的表,那么主键的表在不同的节点顺序将不同,全都执行SELECT…LIMIT… 将出先不同的结果集.
  • 在多主环境下LOCK/UNLOCK TABLES不支持,以及锁函数GET_LOCK(), RELEASE_LOCK()…
  • 查询日志必须保居于表中。全都开启查询日志,必须保存到文件中。
  • 允许最大的事务大小由wsrep_max_ws_rows和wsrep_max_ws_size定义。任何大型操作将被拒绝。如大型的LOAD DATA操作。
  • 全都集群是乐观的并发控制,事务commit全都在该阶段中止。全都有五个 事务向在集群中不同的节点向同一行写入并提交,失败的节点将中止。对 于集群级别的中止,集群返回死锁错误代码(Error: 1213 SQLSTATE: 40001 (ER_LOCK_DEADLOCK)).
  • XA事务不支持,全都在提交上全都回滚。
  • 整个集群的写入吞吐量是由最弱的节点限制,全都有五个 节点变得缓慢,那么整个集群将是缓慢的。为了稳定的高性能要求,所有的节点应使用统一的硬件。
  • 集群节点建议大慨 五个。
  • 全都DDL语录有难题将破坏集群。

4、技术原理

Galera集群的群克隆功能是基于认证的群克隆,其流程如下:

当客户端发出五个 commit的指令,在事务被提交可是我,所有对数据库的更改也有被write-set分发起来,全都将write-set 记录的内容发送给全都节点。

write-set 将在每个节点上使用搜索到的主键进行确认性认证测试,测试结果决定着节点是是否应用write-set更改数据。全都认证测试失败,节点将丢弃 write-set ;全都认证测试成功,则事务提交,工作原理如下图:

关于新节点的加入,流程如下:

新加入的节点叫做Joiner,给Joiner提供群克隆的节点叫Donor。在该过程中首先会检查本地grastate.dat文件的seqno事务号是是否在远端donor节点galera.cache文件里,全都居于,那么进行Incremental State Transfer(IST)增量同步群克隆,将剩余的事务发送过去;全都不居于那么进行State Snapshot Transfer(SST)全量同步群克隆。SST有一种全量拷贝土法子:mysqldump、rsync和mariabackup(原xtrabackup)。SST的土法子能才能通过wsrep_sst_method相似 参数来设置。

一种同步土法子的底部形态对比如下:

全都有,在生产环境强烈推荐使用mariabackup的同步土法子。

——以上内容均分发自网络。

二、Docker镜像

现在是Docker容器化时代,纯手工编译、配置的部署土法子让人不介绍了,感兴趣的能才能另一方去搜索相关教程。本文主全都分享一下张戈自制的全自动部署MGC集群的Docker镜像(取舍 MariaDB 10.3.12版本,基于Docker Host网络模式),最大程度复杂了MGC集群的部署难度。

1、改进说明

相比于官方以及全都第三方Docker镜像,张戈封装的镜像有如下改进:

  • 一键式分发,无人值守创建MGC集群;
  • 支持MySQL(中有 galera)全局任意参数设置;
  • 支持镜像之外的任何自定义脚本,支持sh、sql、gz、env等;
  • 成员节点故障恢复后自动探测,找到可用点后重新加入集群;
  • 支持集群、单点以及普通一种模式,能才能覆盖各种关系存储场景;
  • 支持数据库初始化自定义设置,中有 root、自定义账号、数据库、密码、主机等相关信息;
  • 支持节点恢复时的传输限速(单位KB/s),除理集群节点恢复顶满带宽难题,支持rsync / mariadbbackup 2种同步土法子限速(独有改进)。

2、部署说明

①、环境准备

和上文提到的MGC底部形态一样,集群部署须要3台服务器,比如:

  • 节点1:192.168.1.400
  • 节点2:192.168.1.101
  • 节点3:192.168.1.102

在3台服务器上初始化安装好Docker(安装教程),完成环境准备工作。

②、快速启动

在3台服务器上执行如下命令(未必求先后顺序):

docker run -d \
    --net=host \
    --name=demo \
    -e cluster_name=demo-3310 \
    -e my_port=3310 \ # 全都使用Host网络模式,全都有须要给每个集群分配五个



独有端口,除理冲突
    -e node1=192.168.1.400 \
    -e node2=192.168.1.101 \
    -e node3=192.168.1.102 \
    -e mysql_user=demo \
    -e mysql_user_password=123456 \
    -v /data/mariadb-galera:/data/mariadb-galera \
   jagerzhang/mariadb-galera

执行后,能才能执行 docker logs -f demo-3310 查看启动日志,才能才能执行 tail -f /data/mariadb-galera/logs/error.log 查看运行日志。

节点1的Docker启动日志如下:

[[email protected]:~]$ docker logs -f demo-33400
==================================== Auto-join Galera Cluster  =======================================
>> Can't found any activated node, It's maybe the first one, just start without join_address.
==================================== Initialization Infomation =======================================
cluster_name: demo
current_node: 192.168.1.400:3310
cluster_members: 192.168.1.400:3310,192.168.1.101:3310,192.168.1.102:3310

>> Start initialize configuration (Touch the file /data/mariadb-galera/lock/global.lock can skiped).
set pid_file=/data/mariadb-galera/lock/mysqld.pid
set max_connections=4000
set log_error=/data/mariadb-galera/logs/error.log
set port=3310
set innodb_buffer_pool_size=2867M
set slow_query_log_file=/data/mariadb-galera/logs/slow.log
set datadir=/data/mariadb-galera/data
set innodb_log_file_size=573M
set report_host=192.168.1.400:3310
set server_id=1400
=================================== MySQL Daemon Runing Infomation ===================================
>> initializing database


PLEASE REMEMBER TO SET A PASSWORD FOR THE MariaDB root USER !
To do so, start the server, then issue the following commands:

'/usr/bin/mysqladmin' -u root password 'new-password'
'/usr/bin/mysqladmin' -u root -h 192.168.1.400 password 'new-password'

Alternatively you can run:
'/usr/bin/mysql_secure_installation'

which will also give you the option of removing the test
databases and anonymous user created by default.  This is
strongly recommended for production servers.

See the MariaDB Knowledgebase at http://mariadb.com/kb or the
MySQL manual for more instructions.

Please report any problems at http://mariadb.org/jira

The latest information about MariaDB is available at http://mariadb.org/.
You can find additional information about the MySQL part at:
http://dev.mysql.com
Consider joining MariaDB's strong and vibrant community:
https://mariadb.org/get-involved/

>> database initialized
>> Found the galera configuration, waiting for mysql really start
>> mysql init process in progress...
2019-06-17 11:42:53 0 [Note] mysqld (mysqld 10.3.12-MariaDB-log) starting as process 224 ...
>> WARNNING: password for root is Empty, not recommended!

/docker-entrypoint.sh: ignoring /data/mariadb-galera/initdb.d/*


>> mysql init process done. ready for start up.

190617 11:42:57 mysqld_safe Logging to '/data/mariadb-galera/logs/error.log'.
190617 11:42:57 mysqld_safe Starting mysqld daemon with databases from /data/mariadb-galera/data

全都节点的启动全都自动探测全都节点端口是是否就绪,尝试加入集群,比如节点2的启动日志如下:

[[email protected]:~]$ docker logs -f demo-33400
==================================== Auto-join Galera Cluster  =======================================
>> 192.168.1.102 is not ready, retry check...
>> 192.168.1.400 is not ready, retry check...
>> 192.168.1.102 is not ready, retry check...
>> 192.168.1.400 is not ready, retry check...
>> 192.168.1.102 is not ready, retry check...
>> 192.168.1.400 is not ready, retry check...
>> 192.168.1.102 is not ready, retry check...
>> 192.168.1.400 is ready, start join cluster 
==================================== Initialization Infomation =======================================
cluster_name: demo
current_node: 192.168.1.101:3310
cluster_members: 192.168.1.400:3310,192.168.1.101:3310,192.168.1.102:3310

>> Start initialize configuration (Touch the file /data/mariadb-galera/lock/global.lock can skiped).
set pid_file=/data/mariadb-galera/lock/mysqld.pid
set max_connections=4000
set log_error=/data/mariadb-galera/logs/error.log
set port=3310
set innodb_buffer_pool_size=2867M
set slow_query_log_file=/data/mariadb-galera/logs/slow.log
set datadir=/data/mariadb-galera/data
set innodb_log_file_size=573M
set report_host=192.168.1.101:3310
set server_id=1101
=================================== MySQL Daemon Runing Infomation ===================================
>> initializing database


PLEASE REMEMBER TO SET A PASSWORD FOR THE MariaDB root USER !
To do so, start the server, then issue the following commands:

'/usr/bin/mysqladmin' -u root password 'new-password'
'/usr/bin/mysqladmin' -u root -h 192.168.1.101 password 'new-password'

Alternatively you can run:
'/usr/bin/mysql_secure_installation'

which will also give you the option of removing the test
databases and anonymous user created by default.  This is
strongly recommended for production servers.

See the MariaDB Knowledgebase at http://mariadb.com/kb or the
MySQL manual for more instructions.

Please report any problems at http://mariadb.org/jira

The latest information about MariaDB is available at http://mariadb.org/.
You can find additional information about the MySQL part at:
http://dev.mysql.com
Consider joining MariaDB's strong and vibrant community:
https://mariadb.org/get-involved/

>> database initialized
190617 11:43:04 mysqld_safe Logging to '/data/mariadb-galera/logs/error.log'.
190617 11:43:04 mysqld_safe Starting mysqld daemon with databases from /data/mariadb-galera/data

五个 节点都启动成功后,能才能执行如下命令查看集群清况 (用户名、密码与启动参数一致):

mysql -h192.168.1.400 -P3310 -udemo -p123456 -e "show status like '%wsrep%'"

正常结果如下:

+------------------------------+--------------------------------------------------------------+
| Variable_name                | Value                                                        |
+------------------------------+--------------------------------------------------------------+
| wsrep_apply_oooe             | 0.000000                                                     |
| wsrep_apply_oool             | 0.000000                                                     |
| wsrep_apply_window           | 1.000000                                                     |
| wsrep_causal_reads           | 0                                                            |
| wsrep_cert_deps_distance     | 1.000000                                                     |
| wsrep_cert_index_size        | 5                                                            |
| wsrep_cert_interval          | 0.000000                                                     |
| wsrep_cluster_conf_id        | 3                                                            |
| wsrep_cluster_size           | 3                                                            |
| wsrep_cluster_state_uuid     | fc7361db-90b1-11e9-92f5-b2c06a7dace0                         |
| wsrep_cluster_status         | Primary                                                      |
| wsrep_cluster_weight         | 3                                                            |
| wsrep_commit_oooe            | 0.000000                                                     |
| wsrep_commit_oool            | 0.000000                                                     |
| wsrep_commit_window          | 1.000000                                                     |
| wsrep_connected              | ON                                                           |
| wsrep_desync_count           | 0                                                            |
| wsrep_evs_delayed            |                                                              |
| wsrep_evs_evict_list         |                                                              |
| wsrep_evs_repl_latency       | 0/0/0/0/0                                                    |
| wsrep_evs_state              | OPERATIONAL                                                  |
| wsrep_flow_control_paused    | 0.000000                                                     |
| wsrep_flow_control_paused_ns | 0                                                            |
| wsrep_flow_control_recv      | 0                                                            |
| wsrep_flow_control_sent      | 0                                                            |
| wsrep_gcomm_uuid             | 049f400a7-90b2-11e9-8363-03f090c3c22c                         |
| wsrep_incoming_addresses     | 192.168.1.400:3310,192.168.1.101:3310,192.168.1.102:3310     |
| wsrep_last_committed         | 15                                                           |
| wsrep_local_bf_aborts        | 0                                                            |
| wsrep_local_cached_downto    | 9                                                            |
| wsrep_local_cert_failures    | 0                                                            |
| wsrep_local_commits          | 0                                                            |
| wsrep_local_index            | 2                                                            |
| wsrep_local_recv_queue       | 0                                                            |
| wsrep_local_recv_queue_avg   | 0.000000                                                     |
| wsrep_local_recv_queue_max   | 1                                                            |
| wsrep_local_recv_queue_min   | 0                                                            |
| wsrep_local_replays          | 0                                                            |
| wsrep_local_send_queue       | 0                                                            |
| wsrep_local_send_queue_avg   | 0.000000                                                     |
| wsrep_local_send_queue_max   | 1                                                            |
| wsrep_local_send_queue_min   | 0                                                            |
| wsrep_local_state            | 4                                                            |
| wsrep_local_state_comment    | Synced                                                       |
| wsrep_local_state_uuid       | fc7361db-90b1-11e9-92f5-b2c06a7dace0                         |
| wsrep_open_connections       | 0                                                            |
| wsrep_open_transactions      | 0                                                            |
| wsrep_protocol_version       | 9                                                            |
| wsrep_provider_name          | Galera                                                       |
| wsrep_provider_vendor        | Codership Oy <[email protected]>                            |
| wsrep_provider_version       | 25.3.25(r3836)                                               |
| wsrep_ready                  | ON                                                           |
| wsrep_received               | 10                                                           |
| wsrep_received_bytes         | 5834                                                         |
| wsrep_repl_data_bytes        | 0                                                            |
| wsrep_repl_keys              | 0                                                            |
| wsrep_repl_keys_bytes        | 0                                                            |
| wsrep_repl_other_bytes       | 0                                                            |
| wsrep_replicated             | 0                                                            |
| wsrep_replicated_bytes       | 0                                                            |
| wsrep_thread_count           | 2                                                            |
+------------------------------+--------------------------------------------------------------+
61 rows in set (0.001 sec)

3、自定义参数

自定义参数均使用Docker环境变量的土法子传入,使用土法子为启动容器时,在命令中加入 -e 参数名=参数值即可,比如想预创建五个 demo 数据库,则启动命令如下:

docker run -d \
    --net=host \
    --name=demo \
    -e mysql_database=demo \ # 预创建 demo 数据库
    -e cluster_name=demo \
    -e my_port=3310 \
    -e node1=192.168.1.400 \
    -e node2=192.168.1.101 \
    -e node3=192.168.1.102 \
    -e mysql_user=demo \
    -e mysql_user_password=123456 \
    -v /data/mariadb-galera/demo-3310:/data/mariadb-galera \
   jagerzhang/mariadb-galera

①、当前版本支持的自定义参数

  • mysql_user 预创建数据库用户
  • mysql_user_password 用户密码,指定用户密码(全都脚本用@符号作为分隔符,全都有注意密码后面 未必中有 @符号)
  • mysql_user_database 用户权限DB,指定用户具备哪个数据库的权限
  • mysql_user_grand 是是否赋予grand权限,可选值为1/0,值为1则表示改用户具有创建用户的权限
  • mysql_database 创建数据库,预创建数据库,比如 -e mysql_database=demo,全都创建五个 demo数据库
  • mysql_root_host root可远程的主机,指定root能才能登录的主机
  • mysql_root_password root密码,指定root密码(全都脚本用@符号作为分隔符,全都有注意密码后面 未必中有 @符号)
  • mysql_random_root_password 随机密码,让root密码随机生成,-e mysql_random_root_password=1,与mysql_root_password 参数互斥
  • transfer_limit 传输限速,单位 kb/s ,限制集群恢复时带宽,除理顶满内网带宽上限
  • cluster_name 集群名字
  • cluster_mode 是是否使用集群模式
  • join_address 指定已居于的成员IP,主要用于集群扩容或节点重建
  • interface 指定绑定的网卡,用于集群自动探测时确认容器节点(nodeX),缺省优先级为:br0 > eth1 > eth0,若那么缺省顺序内,请务必指定实际网卡名

②、支持所有MySQL和Galera参数

使用my_${参数名} 形式来自定义MySQL运行参数,比如:-e my_tmp_table_size=512M,能才能改变MySQL的tmp_table_size为512MB;

使用wsrep_${参数名} 形式来自定义Galera参数,比如:-e wsrep_sst_method=rsync,能才能修改Galera集群的同步土法子为rsync模式。

4、高级玩法

①、改变参数

用过Docker的我们 歌词 我们 歌词 应该知道,容器一旦运行,那启动时通过 -e 指定的环境变量也有能修改,除非重建容器。全都,本次封装镜像,考虑到MySQL在后续运维过程中难免会须要修改全都参数的清况 ,比如上文提到的 tmp_table_size参数,启动时指定了,那容器不销毁直接重启也有使用启动时指定的值。为了除理相似 特殊清况 ,镜像还留了五个 相似『后门』的自定义参数配置。

相似 文件居于:/data/mariadb-galera/demo-3310/conf/custom.cfg,集群首次启动自动生成,默认内容如下:

# put some custom configuration in this file can change zhe container by yourself.
# Usage:
#      export cluster_name=new_cluster_name
#      unset node2
# 

我们 歌词 我们 歌词 我们 歌词 着实须要修改容器的某个环境变量,又你后该 重建容器的清况 下,能才能将相似 变量再加到custom.cfg文件,比如须要修改tmp_table_size相似 值,如下修改即可:

# put some custom configuration in this file can change zhe container by yourself.
# Usage:
#      export cluster_name=new_cluster_name
#      unset node2
# 
export my_tmp_table_size=256M

Ps:当然,变量名称依然须要遵循镜像约定形式,说白了全都启动时 -e 参数名是哪几只,这里就export 参数名=参数值,简单吧!

②、初始化锁

我们 歌词 我们 歌词 知道,MySQL安装后首次启动都须要初始化db,本镜像基于Dockerfile过程透明,全都初始化db的过程也在容器首次启动当中实现。为了除理容器在重启时出先重复初始化db,这里设计了五个 锁文件:

  • /data/mariadb-galera/demo-3310/initdb.lock 除理MySQL db重复初始化,全都后面 要再次初始化,请在重启可是我删除即可。
  • /data/mariadb-galera/demo-3310/global.lock 阻止Galera、MySQL参数的初始化,说白了全都让 -e 指定MySQL和Galera环境变量失效,不再修改配置文件,相似 文件默认不居于,全都须要阻止相关初始化,则手工创建相似 空文件即可。

③、使用Docker API

Docker支持API远程控制,全都在生产环境中,我们 歌词 我们 歌词 通过远程调用Docker API来快速拉起MGC集群,实现一键快速创建集群。

这里简单的分享五个 用于快速拉起集群的Python测试Demo,仅供参考:

#-*- coding:utf-8 -*-
# author:jagerzhang
import sys,re,os,time
import ConfigParser
import requests
import json
reload(sys)
sys.setdefaultencoding('utf8')
config  = ConfigParser.ConfigParser()

config.read('%s/config.cfg' % os.path.dirname(os.path.realpath(__file__)))

def gen_create_request(member_address=None):
    env_list=[]
    conf_list={}
    for conf in config.sections():
        for conf_name in config.options(conf):
            conf_value = config.get(conf,conf_name)
            env_list.append("%s=%s" % (conf_name,conf_value))
            conf_list[conf_name] = conf_value
    if member_address is not None:
        env_list.append("WSREP_MEMBER_ADDRESS=%s" % member_address)
    create_json = {
    "Env": env_list,
    "Image": "jagerzhang/mariadb-galera:10.3.12",
    "HostConfig": {
       "Binds": [
           "%s/%s-%s:/data/mariadb-galera" % (conf_list["mount_dir"],conf_list["cluster_name"],conf_list["my_port"]),
            "/etc/localtime:/etc/localtime"
        ],
        "Memory": int(conf_list["memory"])*1024*1024*1024,
        "MemorySwap": -1,
        "CpusetCpus": "",
        "Privileged": True,
        "RestartPolicy": {
            "restart": "always"
        },
        "NetworkMode": "host"
        }
    }
    print create_json
    return conf_list,create_json

def create_container(host,cluster_name,my_port,create_json):
    headers = {'Content-type': 'application/json'}
    print "pulling image..."
    url          =  "http://%s:2375/v1.24/images/create?fromImage=jagerzhang/mariadb-galera&tag=10.3.12" % host
    result       =  requests.post(url)
    print result.text
    if result.status_code == 400:
        print "%s pull image success: %s" % (host,result.status_code)
        url          =  "http://%s:2375/containers/create?name=%s-%s"%(host,cluster_name,my_port)
        print "%s create container..." % host
        result       =  requests.post(url,data=json.dumps(create_json),headers=headers).json()
        try:
            url          =  "http://%s:2375/containers/%s/start" % (host,result['Id'])
            print "%s start container..." % host
            result       =  requests.post(url)
            if result.status_code == 204:
                print "%s start container success: %s" % (host,result.status_code)
                return 0
            else:
                print "%s start container failed: %s" % (host,result.status_code)
        except:
            print "%s create container failed: %s" % (host,result)
    else:
        print "%s pull image failed: %s" % (host,result.status_code)
    return result

def main():
    headers = {'Content-type': 'application/json'}
    conf_list,create_json = gen_create_request()
    node1        =  conf_list["node1"]
    node2        =  conf_list["node2"]
    node3        =  conf_list["node3"]
    cluster_name =  conf_list["cluster_name"]
    my_port         =  conf_list["my_port"]
    print create_container(node1,cluster_name,my_port,create_json)
    print create_container(node2,cluster_name,my_port,create_json)
    print create_container(node3,cluster_name,my_port,create_json)

main()

保存上述代码为 deploy.py,全都在相同的目录新增配置文件 config.cfg,内容如下:

[global]
cluster_name=demo
node1=192.168.1.400
node2=192.168.1.101
node3=192.168.1.102

[custom]
mysql_user=demo
mysql_user_host=%
mysql_user_grant=1
mysql_database=demo
mysql_user_password=123456
mysql_root_password="demo.2019"
transfer_limit=40000
memory=8
my_port=3310
mount_dir=/data/mysql_data

最后,只须要执行 python deploy.py 就能才能快速拉起名为demo的MGC集群,是也有非常简单?当然,实际生产环境能才能将相似 功能集成到自动化运维平台,工具脚本仅仅是为了测试。

三、小结

本次分享的全自动MGC镜像目前已在我们 歌词 我们 歌词 的生产环境推广使用近多日,目前表现稳定。通过本镜像,使MariaDB Galera Cluster集群的可运维性得到了大幅度提升。在镜像的封装设计上,张戈也是投入了非常多的心血,全都文章篇幅有限以及时间关系,全都有细节或实现原理都那么一一介绍到位,比如同步限速、全局参数可自定义、全自动创建集群等底部形态的实现原理,每五个 拿来都能简单写一篇文章了。

目前,本镜像已完正开源,包括Dockerfile和相关自动化脚本均托管在DockerHub和GitHub上,感兴趣的同学能才能自行前往查看:

  • MGC镜像:https://hub.docker.com/r/jagerzhang/mariadb-galera
  • 基础镜像:https://hub.docker.com/r/jagerzhang/mariadb
  • Dockerfile及相关脚本:https://github.com/jagerzhang/dockerfile

最后提一句,MariaDB Galera Cluster集群能才能配合使用数据库代理后面 件ProxySQL或maxscale来实现DB负载均衡、读写分离。目前,我们 歌词 我们 歌词 在生产环境已全面切换到ProxySQL代理,表现也非常稳定,且可运维性良好,后续有时间再来分发分享,敬请期待!