Docker

Docker镜像分享:一键部署MariaDB Galera Cluster集群

Jager · 6月23日 · 2019年 · · 7036次已读

一、特性介绍

1、概述

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

Docker镜像分享:一键部署MariaDB Galera Cluster集群

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 事务不支持,由于在提交上可能回滚。
  • 整个集群的写入吞吐量是由最弱的节点限制,如果有一个节点变得缓慢,那么整个集群将是缓慢的。为了稳定的高性能要求,所有的节点应使用统一的硬件。
  • 集群节点建议最少 3 个。
  • 如果 DDL 语句有问题将破坏集群。

4、技术原理

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

Docker镜像分享:一键部署MariaDB Galera Cluster集群

当客户端发出一个 commit 的指令,在事务被提交之前,所有对数据库的更改都会被 write-set 收集起来,并且将 write-set 记录的内容发送给其他节点。

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

Docker镜像分享:一键部署MariaDB Galera Cluster集群

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

Docker镜像分享:一键部署MariaDB Galera Cluster集群

新加入的节点叫做 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 这个参数来设置。

三种同步方式的特性对比如下:

Docker镜像分享:一键部署MariaDB Galera Cluster集群

所以,在生产环境强烈推荐使用 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.100
  • 节点 2:192.168.1.101
  • 节点 3:192.168.1.102

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

②、快速启动

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

docker run -d \
    --net=host \
    --name=demo-3310 \
    -e cluster_name=demo-3310 \
    -e my_port=3310 \ # 由于使用 Host 网络模式,所以需要给每个集群分配一个独有端口,避免冲突
    -e node1=192.168.1.100 \
    -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 启动日志如下:

[root@192.168.1.100:~]$ docker logs -f demo-3330
==================================== 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.100:3310
cluster_members: 192.168.1.100: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=500
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.100:3310
set server_id=1100
=================================== 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.100 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 的启动日志如下:

[root@192.168.1.101:~]$ docker logs -f demo-3330
==================================== Auto-join Galera Cluster  =======================================
>> 192.168.1.102 is not ready, retry check...
>> 192.168.1.100 is not ready, retry check...
>> 192.168.1.102 is not ready, retry check...
>> 192.168.1.100 is not ready, retry check...
>> 192.168.1.102 is not ready, retry check...
>> 192.168.1.100 is not ready, retry check...
>> 192.168.1.102 is not ready, retry check...
>> 192.168.1.100 is ready, start join cluster 
==================================== Initialization Infomation =======================================
cluster_name: demo
current_node: 192.168.1.101:3310
cluster_members: 192.168.1.100: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=500
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.100 -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             | 049f60a7-90b2-11e9-8363-03f090c3c22c                         |
| wsrep_incoming_addresses     | 192.168.1.100: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 <info@codership.com>                            |
| 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-3310 \
    -e interface=eth1 \ # 请务必改成本地网卡名,否则一直会 retry check!!
    -e mysql_database=demo \ # 预创建 demo 数据库
    -e cluster_name=demo-3310 \
    -e my_port=3310 \
    -e node1=192.168.1.100 \
    -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,若不在缺省顺序内,请务必指定实际网卡名,否则会因为无法识别节点,出现持续的 retry check 日志滚动
  • local_addr 指定本地 IP,用于兼容本地网卡上绑定了 VIP 的特殊情况

②、支持所有 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,这里设计了 2 个锁文件:

  • /data/mariadb-galera/demo-3310/lock/initdb.lock 防止 MySQL db 重复初始化,如果后面要再次初始化,请在重启之前删除即可。
  • /data/mariadb-galera/demo-3310/lock/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 == 200:
        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.100
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=6000
interface=eth1 # 请务必改成服务器本地网卡名,否则会一直 retry check!!
memory=8
my_port=3310
mount_dir=/data/mysql_data

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

三、小结

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

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

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

四、问题收集

这里将目前收集到的问题和解决办法整理出来,方便有需要的读者。

1、启动后,docker logs 一直打印 "xxx.xxx.xxx.xxx is not ready, retry check…"信息,集群创建失败问题。

问题原因:这个问题主要是因为主机的网卡名称不在脚本预设清单(eth0/eth1/br0)之内,导致脚本无法获取到自身的 IP 地址,从而无法关联节点名称。

解决办法:在启动变量里面指定一下 interface 环境变量为实际网卡名称即可,比如 -e interface=enp0s3 ,重新创建容器即可。

2、当三台节点都宕机之后,如果最后退出的节点不是 node1,集群将无法正常启动。

问题原因:galera 集群全部节点宕机后,必须要先启动最后退出的节点。由于本文分享的自动化集群逻辑必须要先启动 node1,所以要临时调整下组件逻辑,让最后退出的节点能正常启动。

解决办法:

  • 找到最后退出的节点,通过查看 /data/mariadb-galera/<cluster_name>/data/grastate.dat 文件,safe_to_bootstrap 值为 1 的节点则是最后一个退出的节点;
  • 在 /data/mariadb-galera/<cluster_name>/conf/custom.cfg 文件里面写入 join_address=tmp,tmp 可以是任何字符串内容,只为占位;
  • 修改 /data/mariadb-galera/<cluster_name>/wsrep.conf ,将里面的 wsrep_cluster_address=xxx,改为 wsrep_cluster_address="gcomm://";
  • 创建一个锁文件暂时阻止初始化:touch /data/mariadb-galera/<cluster_name>/lock/global.lock;
  • 重启这个节点,直到启动完成;
  • 重启其他两个节点,此时集群应该可以正常启动了;
  • 最后,到最后退出的节点上,去掉 custom.cfg 中的 join_address,并删除锁文件,重启就可以全部恢复。
29 条回应
  1. 明月登楼的博客 2019-6-25 · 17:00

    太高大上了吧!集群数据库呀!

  2. 叠纸盒 2019-6-28 · 9:56

    小白表示看得脑瓜大~

  3. 绿软吧 2019-7-4 · 0:54

    很强大啊

  4. aaa 2019-7-29 · 10:39

    一眼看下去,觉得有理有序,但是实践后遇到问题,谈一下感受吧~个人感受哈哈哈
    一,docker命名是demo,查看日志用的是demo-3310,可见是实践了很多次,然后资料整理有点小问题
    二,容器有问题,下载镜像,启动容器,容器起来了,但是服务并没有起来--2019年7月29日10:34:48
    三,配置文件没有解释,需要有一定解释,才能让文章看得有条理。我指的不是变量参数,是容器配置。。。
    四,没有bug错误分析。实践过程,错误收集--这是财富。
    最后,还是感谢分享。

    • avatar
      Jager 2019-7-29 · 13:00

      一、笔误笔误,我们生产环境都是用demo-3310这种容器命名方法;
      二、服务没起来可以用docker logs 容器名 看下报错是啥,或者检查下 error.log的内容;
      三、配置文件这个是通用的信息,没做注解,可以看官方文档;
      四、暂时没发现BUG,所以没记录。。。
      最后,还是感谢细读,难得有人看的这么仔细,有空多交流~

  5. 挽回老公 2019-9-2 · 14:21

    厉害了,感谢分享

  6. abo 2019-9-12 · 10:52

    docker run的时候不需要做端口映射吗?

    • avatar
      Jager 2019-9-25 · 19:46

      用的是host模式,不需要做端口映射

  7. zdf188 2019-10-18 · 9:36

    你好,我尝试部署了一下,但是遇到了一些问题,希望大神能解答一下。
    我部署的环境:3台虚拟机,ubuntu16.04
    一,使用host网络模式部署之后一直retry check,查看宿主机的端口也没有被占用。
    二,使用bridge网络模式,暴露13310:13310,3310:3310,但是运行 50秒左右后容器挂掉,查看日志显示:Failed to open channel 'demo-3310' at 'gcomm://192.168.8.197:13310': -110
    三,希望能介绍一下my_port这个端口的作用。
    希望能解答一下我的疑问,谢谢。

    • avatar
      Jager 2019-10-18 · 10:44

      1、只支持host模式
      2、3台都是retry?如果是肯定是参数错误了,这个可以加我QQ看下。

      • zdf188 2019-10-18 · 11:11

        加你qq了

        • avatar
          Jager 2019-11-6 · 14:56

          重加一下 287988783,我看你是用临时会话过来的,我回复你提示失败

        • avatar
          Jager 2019-11-7 · 9:33

          应该是和下面一位朋友同样的问题,因为你们的系统网卡不是用的br0、eth0、eth1这种常见的命名,可能是其他类似 enp0s3 这种命名,导致容器没识别到node节点信息,文章其实有写到的:
          interface 指定绑定的网卡,用于集群自动探测时确认容器节点(nodeX),缺省优先级为:br0 > eth1 > eth0,若不在缺省顺序内,请务必指定实际网卡名
          所以解决问题的办法就是启动时手工指定网卡名,比如 -e interface=enp0s3 就可以了。

      • 我也是遇到这个问题 2019-12-27 · 17:17

        您好! 我也是遇到同样的问题,启动后日志都是retry check

        • avatar
          Jager 2019-12-27 · 17:44

          注意文章中的参数说明:interface 用于指定绑定的网卡,用于集群自动探测时确认容器节点(nodeX),缺省优先级为:br0 > eth1 > eth0,若不在缺省顺序内,请务必指定实际网卡名,否则会因为无法识别节点,出现持续的retry check日志滚动。

  8. will 2019-11-6 · 14:38

    我用的centos7.2,yum安装docker-ce-19.03版本,3台一直报
    >> 192.168.1.235 is not ready, retry check...
    >> 192.168.1.236 is not ready, retry check...
    >> 192.168.1.237 is not ready, retry check...

    • avatar
      Jager 2019-11-6 · 14:57

      看下logs目录里面的error.log内容,或者加我QQ
      287988783,我看下。

      • will 2019-11-6 · 15:13

        /data/mariadb-galera/logs下没有errer.log,只有个tmpdir目录,而且里面也是空的,只能通过docker logs查看日志,信息如下:
        >> 192.168.1.235 is not ready, retry check…
        >> 192.168.1.236 is not ready, retry check…
        >> 192.168.1.237 is not ready, retry check…

        • will 2019-11-6 · 15:32

          已经加你qq了,我的qq99708432

  9. 帝王 2019-12-26 · 21:37

    很久没来看了,大佬博客变化好大啊

  10. goalsword 2020-2-24 · 15:03

    所有节点都挂掉后, 如果第三个节点是最后个退出的, 按理说要先开第三个节点, 但是开了后, 会一直在检测其他节点. 这该怎么解决

    • avatar
      Jager 2020-2-24 · 17:28

      可以读一下这个脚本 162行~165行的逻辑:https://github.com/jagerzhang/dockerfile/blob/master/mariadb-galera/10.3.12/galera-entrypoint.sh
      只需要在第三台的custom.cfg 加入一条配置先启动下:

      unset join_address 
      

      启动成功后,再去启动第一台、第二台,最后把custom.cfg加入的配置去掉,再重启第三台即可。

      • robert 2020-7-16 · 18:24

        这种方法不行,虽然容器启动了,但是服务没有起来,3310也没有监听。 另外我在敲评论的时候你这个页面一直在抖动

        • avatar
          Jager 2020-8-2 · 16:51

          抖动效果已去除。

  11. tyler 2020-5-12 · 15:01

    你好,请问这里的galera是不是用的github上的这个项目?https://github.com/MariaDB/server/tree/5.5-galera
    如果不是的话,请问用的是哪个呢?能给下具体的github地址吗,非常感谢!!

  12. 遗世孤叟 2020-10-12 · 16:48

    请问大师,镜像什么时候升级到mariadb 10.5?

  13. 遗世孤叟 2020-10-12 · 16:52

    请问大师,mariadb-galera镜像什么时候升级到支持maradb 10.5?

  14. 遗世孤叟 2020-11-17 · 18:49

    大师:
    今天升级镜像jagerzhang/mariadb-galera到最新版本,发现第1个节点都运行不起来,经分析需要修改文件10.5.6/galera-entrypoint.sh第55行为:/usr/lib64/galera-4/libgalera_smm.so
    第1个节点运行起来后,第2个节点运行一直报错,主要错误信息如下:

    2020-11-17 18:41:09 0 [ERROR] WSREP: Failed to read 'ready ' from: wsrep_sst_mariabackup --role 'joiner' --address '172.19.0.121:33310' --datadir '/data/mariadb-galera/data/' --parent '503' --binlog 'mysql-bin' --mysqld-args --basedir=/usr --datadir=/data/mariadb-galera/data --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --wsrep_on=on --wsrep_provider=/usr/lib64/galera-4/libgalera_smm.so --log-error=/data/mariadb-galera/logs/error.log --open-files-limit=655350 --pid-file=/data/mariadb-galera/lock/mysqld.pid --port=3310 --wsrep_start_position=00000000-0000-0000-0000-000000000000:-1
    Read: '(null)'
    2020-11-17 18:41:09 0 [ERROR] WSREP: Process completed with error: wsrep_sst_mariabackup --role 'joiner' --address '172.19.0.121:33310' --datadir '/data/mariadb-galera/data/' --parent '503' --binlog 'mysql-bin' --mysqld-args --basedir=/usr --datadir=/data/mariadb-galera/data --plugin-dir=/usr/lib64/mysql/plugin --user=mysql --wsrep_on=on --wsrep_provider=/usr/lib64/galera-4/libgalera_smm.so --log-error=/data/mariadb-galera/logs/error.log --open-files-limit=655350 --pid-file=/data/mariadb-galera/lock/mysqld.pid --port=3310 --wsrep_start_position=00000000-0000-0000-0000-000000000000:-1: 42 (No message of desired type)
    2020-11-17 18:41:09 2 [ERROR] WSREP: Failed to prepare for 'mariabackup' SST. Unrecoverable.
    2020-11-17 18:41:09 2 [ERROR] WSREP: SST request callback failed. This is unrecoverable, restart required.

    然后我用老的镜像文件重新做一遍,是没有问题的。

    • 遗世孤叟 2020-11-20 · 14:49

      加以下两个参数可以解决:
      wsrep_sst_method: rsync
      wsrep_provider: /usr/lib64/galera-4/libgalera_smm.so
      wsrep_sst_method设置为mariabackup报错估计要仔细研究官方文档,以后有空再研究。

      • avatar
        Jager 2020-11-20 · 15:16

        看来之前的docker编译配置不兼容新版本了,得改下才行。

        • jameszz 2020-12-31 · 16:20

          mariabackup报错是因为mariabackup的脚本中用到了which命令,docker中没有这个命令,可以通过把主机的which映射到docker的/usr/bin/which来解决

  15. vega 2022-3-29 · 17:24

    在以上示範內容中, docker 容器命名為 demo, 但是容器啟動後, 要用 docker logs -f demo-3310 查看启动日志
    我是否誤會了? 是不是該用 docker logs -f demo 查看启动日志?

    • avatar
      Jager 2022-3-29 · 17:54

      属于笔误,已修正。

  16. vega 2022-3-29 · 17:40

    docker run -d \
    --name=demo \
    -e cluster_name=demo \
    -e node1=10.100.82.60 \
    -e node2=10.100.82.61 \
    -e node3=10.100.82.62 \
    -e interface=eth0 \
    -e mysql_user=demo \
    -e mysql_user_password=123456 \
    -v /data/mariadb-galera:/data/mariadb-galera \
    jagerzhang/mariadb-galera
    我是在 Win10 Hper-v 建 三台 VM (ubuntu 20) 10.100.82.60, 61, 62
    啟動 Container 沒有問題, 但是每一台 Docker Logs 顯示別的 Node is not ready, retry, 我確定我的網卡是 eth0
    請問該如何做 ?

    • avatar
      Jager 2022-3-29 · 17:58

      可以加我QQ 287988783,提供更详细的截图看看。

  17. haoren 2022-4-8 · 13:12

    谢谢大佬,我有一个问题: 想增加第4个节点,之前的三个节点要怎么修改启动参数,要销毁重新弄新的docker吗?

    • avatar
      Jager 2022-4-8 · 14:09

      短期内只是关联到集群的话,前面3个节点可以先不动,第4个节点启动的环境变量里面把node1-node4都配置下启动即可。长期来看,前面3台节点可以在custom.cfg里面将 node4=x.x.x.x 注入进去,避免容器重启的情况,老的3个容器无需销毁重建也没事。