文章目录

一、各节点文件夹创建 

二、各节点配置静态IP

三、各节点关闭Selinux

四、各节点开启包转发功能和修改内核参数

五、修改linux参数(调大最大文件句柄数)

六、各节点添加YUM的Centos源和Docker镜像源

七、各节点安装Docker

八、各节点Docker与Firewalld冲突解决

九、各节点防火墙配置

十、各节点提前准备MySQL双主复制的配置文件

十一、各节点通过Docker运行MySQL容器,并限制MySQL容器的CPU和内存资源

十二、各节点MySQL添加新账号test,并禁用root账号(生产环境,为了安全,通常禁用root账号)

十三、各节点MySQL创建slave用户,并授权复制权限

十四、各节点MySQL配置主从复制

十五、验证双主复制

十六、部署Keepalived

十七、常见问题

十八、资料参考


MySQL主从复制和双主复制的区别:

  • 主从复制(Master-Slave):指的是一个主MySQL服务器(Master)和一个从MySQL服务器(Slave)。在这种配置下,数据只会从Master流向Slave。Slave是Master的副本,用于读查询、备份等非改变数据的操作。
  • 双主复制(Master-Master,也称为主主复制、双向复制或互为主从):是指两个MySQL服务器互相设置对方为主服务器和从服务器。在这种配置下,任何一个服务器上的数据改变都会实时同步到另一个服务器。可以结合Keepalived,基于双主复制,配置MySQL双活高可用。当一个MySQL实例宕机后,应用可以立即基于VIP切换到另一个MySQL实例。
资源配置
节点名称IP地址CPU内存磁盘
主库01192.168.111.1988核16GB500GB
主库02192.168.111.1998核16GB500GB
拓扑图

前置条件:
1、各节点均已安装操作系统 OpenEuler 20.03 (LTS-SP3)

一、各节点文件夹创建 

##创建文件夹
[root@localhost ~]# mkdir /home/data
 
##创建符号链接文件
[root@localhost ~]# ln -s /home/data /data   

二、各节点配置静态IP

【主库01】修改

##修改参数
[root@localhost ~]# vi /etc/sysconfig/network-scripts/ifcfg-ens33
BOOTPROTO=static
DEFROUTE=yes
ONBOOT=yes
IPADDR=192.168.111.198
PREFIX=24
GATEWAY=192.168.111.1
DNS1=114.114.114.114
#其他参数不用修改...
 
##使配置生效。华为openeuler系统使用如下命令,其他linux系统可以使用systemctl restart network
[root@localhost ~]# nmcli con reload; nmcli con up ens33

【主库02】修改

##修改参数
[root@localhost ~]# vi /etc/sysconfig/network-scripts/ifcfg-ens33
BOOTPROTO=static
DEFROUTE=yes
ONBOOT=yes
IPADDR=192.168.111.199
PREFIX=24
GATEWAY=192.168.111.1
DNS1=114.114.114.114
#其他参数不用修改...
 
##使配置生效。华为openeuler系统使用如下命令,其他linux系统可以使用systemctl restart network
[root@localhost ~]# nmcli con reload; nmcli con up ens33

三、各节点关闭Selinux

## 永久配置
[root@localhost ~]# sed -i 's/enforcing/disabled/' /etc/selinux/config 
 
## 临时配置
[root@localhost ~]# setenforce 0 
 
## 查看结果
[root@localhost ~]# getenforce  

四、各节点开启包转发功能和修改内核参数

参数说明:

  • br_netfilter模块用于将桥接流量转发至iptables链,br_netfilter内核参数需要开启转发。
  • net.ipv4.ip_forward=1 将Linux系统作为路由或者VPN服务就必须要开启IP转发功能。当linux主机有多个网卡时,一个网卡收到的信息是否能够传递给其他的网卡 ,如果设置成1 的话 可以进行数据包转发,可以实现VxLAN 等功能。不开启会导致docker部署应用无法访问。

1、动态地向内核中加载br_netfilter模块

[root@localhost ~]# modprobe br_netfilter

2、配置开机自动加载br_netfilter模块

  • 新建rc.sysinit文件
[root@localhost ~]# vi /etc/rc.sysinit
#!/bin/bash
for file in /etc/sysconfig/modules/*.modules ; do
[ -x $file ] && $file
done
  • 在/etc/sysconfig/modules/目录下新建br_netfilter.modules文件
[root@localhost ~]# vi /etc/sysconfig/modules/br_netfilter.modules
modprobe br_netfilter
  • 配置执行权限
[root@localhost ~]# chmod +x /etc/sysconfig/modules/br_netfilter.modules
  • 查看br_netfilter模块是否加载成功
[root@localhost ~]# lsmod | grep br_netfilter  
br_netfilter          22256  0
bridge                155432  1 br_netfilter

3、修改内核参数

  • 修改/etc/sysctl.conf文件
[root@localhost ~]# vi /etc/sysctl.conf   
####注意:等号=左右不能有空格####
net.ipv4.ip_forward=1 #修改/新增这一行
#当keepalive打开的情况下,TCP发送keepalive消息的频率,默认值是7200(2小时)
net.ipv4.tcp_keepalive_time=1800
#TCP发送keepalive探测以确定该连接已经断开的次数,默认值是9
net.ipv4.tcp_keepalive_probes=5
#当探测没有确认时,重新发送探测的频度,默认值为75 
net.ipv4.tcp_keepalive_intvl=5
#将桥接的IPv4流量传递到 iptables 的链
net.bridge.bridge-nf-call-ip6tables=1
net.bridge.bridge-nf-call-iptables=1
#调小TCP协议的time_wait超时时间,默认是240秒
net.ipv4.tcp_fin_timeout=30
  • 执行生效
##加载指定的文件配置内核参数,-p后通常接一个文件路径,若为空,默认加载/etc/sysctl.conf中的内核参数
[root@localhost ~]# sysctl -p  
    
##显示所有的内核参数      
[root@localhost ~]# sysctl -a    
 
##临时修改内核参数的值,重启后失效        
[root@localhost ~]# sysctl -w net.ipv4.tcp_keepalive_time=1800 
[root@localhost ~]# sysctl -w net.ipv4.tcp_keepalive_probes=5
[root@localhost ~]# sysctl -w net.ipv4.tcp_keepalive_intvl=5
 
##加载/etc/sysctl.d/目录下所有conf文件中的内核参数
[root@localhost ~]# sysctl --system

五、修改linux参数(调大最大文件句柄数)

方法一:

sudo vi /etc/security/limits.conf

在最后追加如下内容:

* soft nproc 65536
* hard nproc 65536
* soft nofile 65536
* hard nofile 65536

含义如下:
1)soft nproc: 可打开的文件描述符的最大数(软限制)
2)hard nproc: 可打开的文件描述符的最大数(硬限制)
3)soft nofile:单个用户可用的最大进程数量(软限制)
4)hard nofile:单个用户可用的最大进程数量(硬限制)

方法二:

sudo vi /etc/profile
#文件末尾添加下面这行,并保存: 
ulimit -n 65535
 
# 重新加载修改的环境变量文件
sudo source /etc/profile

六、各节点添加YUM的Centos源和Docker镜像源

##下载
[root@localhost ~]# wget https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo \
                    -O /etc/yum.repos.d/CentOS-Base.repo
[root@localhost ~]# wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo \
                    -O /etc/yum.repos.d/docker-ce.repo
 
##更改名称
[root@localhost ~]# sed -i 's/\$releasever/8/g' /etc/yum.repos.d/docker-ce.repo 
 
##清除缓存
[root@localhost ~]# yum clean all 
 
##刷新缓存
[root@localhost ~]# yum makecache 

七、各节点安装Docker

  • 安装
[root@localhost ~]# yum install docker-ce docker-compose-plugin -y
  • 启动
[root@localhost ~]# systemctl start docker
  • 配置开机启动
[root@localhost ~]# systemctl enable docker
  • 配置国内镜像地址
[root@localhost ~]# cat > /etc/docker/daemon.json << EOF
{
"registry-mirrors": [
    "http://hub-mirror.c.163.com",
    "https://9ca7kqhd.mirror.aliyuncs.com",
    "https://registry.docker-cn.com"
]
}
EOF
  • 重启
[root@localhost ~]# systemctl restart docker

八、各节点Docker与Firewalld冲突解决

资料参考:docker与firewalld冲突解决_docker firewalld-CSDN博客

说明:Firewall的底层使用iptables进行数据过滤,而Docker也使用iptables来进行网络隔离和管理。这可能导致Firewall与Docker产生冲突。当Firewalld启动或者重启的时候,将会从iptables中移除 Docker的规则,从而影响了 Docker 的正常工作。

##查询iptables链
[root@localhost ~]# iptables -L
 
##修改docker配置,绕过iptables,添加最后2行
[root@localhost ~]# vi /etc/docker/daemon.json
{
   "registry-mirrors": [
       "http://hub-mirror.c.163.com",
       "https://9ca7kqhd.mirror.aliyuncs.com",
       "https://registry.docker-cn.com"
   ],
   "experimental" : true,    #添加此行
   "iptables": false         #添加此行
}
 
##重启docker
[root@localhost ~]# systemctl daemon-reload
[root@localhost ~]# systemctl restart docker
 
##重启防火墙
[root@localhost ~]# systemctl restart firewalld
 
##配置防火墙开机自启
[root@localhost ~]# systemctl enable firewalld
 
##再次重启Docker
[root@localhost ~]# systemctl restart docker

此时,Docker容器的端口只能由本机(运行Docker容器的宿主机)访问,可以通过firewall-cmd命令开放其他主机访问本机的Docker容器。(详见:Linux系统中配置防火墙-CSDN博客

九、各节点防火墙配置

##关闭iptables
[root@localhost ~]# systemctl stop iptables

##禁止开机启动iptables
[root@localhost ~]# systemctl disable iptables

##检查是否允许 NAT 转发,若是yes,表示已开启
[root@localhost ~]# firewall-cmd --query-masquerade

##若未开启,则如下配置
[root@localhost ~]# firewall-cmd --permanent --zone=public --add-masquerade

##开放端口,所有主机都可以访问
[root@localhost ~]# firewall-cmd --zone=public --add-port=3306/tcp --permanent

##配置防火墙,允许Keepalived的VRRP流量通过(注意,根据主机的网卡配置相应修改ens33)
[root@localhost ~]# firewall-cmd --direct --permanent --add-rule ipv4 filter INPUT 0 --in-interface ens33 --destination 224.0.0.18 --protocol vrrp -j ACCEPT
[root@localhost ~]# firewall-cmd --direct --permanent --add-rule ipv4 filter OUTPUT 0 --out-interface ens33 --destination 224.0.0.18 --protocol vrrp -j ACCEPT

##重新载入,使配置生效
[root@localhost ~]# firewall-cmd --reload

##查看配置结果
[root@localhost ~]# firewall-cmd --list-all

##查看防火墙所有开放的端口
[root@localhost ~]# firewall-cmd --zone=public --list-ports

十、各节点提前准备MySQL双主复制的配置文件

##创建文件夹
[root@localhost ~]# mkdir -p /data/docker/mysql/conf

##创建my.cnf文件
[root@localhost ~]# touch /data/docker/mysql/conf/my.cnf

【主库01】配置

[client]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4

[mysqld]
#日志留存时间180天
expire_logs_days = 180

#启用MySQL的通用查询日志,可以在该日志中查看SQL执行记录
general_log = 1
general_log_file = /var/log/mysql/general.log

#MySQL监听的IP地址,如果是127.0.0.1,表示仅本机访问
bind_address=0.0.0.0

#MySQL监听端口
port=3306

#以默认的mysql用户运行
user=mysql

#服务端默认编码(数据库级别)
character-set-server=utf8mb4

#最大可连接数
max_connections = 2000

#开启慢日志
slow_query_log=ON

#设置慢查询时间
long_query_time=10

#慢查询日志保存位置
slow_query_log_file=/var/log/mysql/slowquery.log

#记录所有没有用上索引全表扫描的语句
log_queries_not_using_indexes=1

#记录optimize table,analyze table和alter table等语句引发的慢查询
log_slow_admin_statements=1

#数据库实例唯一标识
#启用主从、双主和集群的时候必须指定,每个节点必须不同
#注意与【主库02】的区别
server-id=1

#设置中继日志位置和名称
relay_log=slave-relay-bin

#设置中继日志索引位置和名称
relay_log_index=slave-relay-bin.index

#从其他主库同步的数据也记录二进制文件
#8.0.23之前,使用log_slave_updates
log_replica_updates=1

#读写模式
read_only=0

#自动清空中继日志
relay_log_purge=1

#中继日志损坏自动恢复同步
relay_log_recovery=1

#二进制文件名称集路径
log_bin=master-bin

#二进制文件索引名称集路径
log_bin_index=master-bin.index

#基于行级别的日志,默认为MIXED混合模式
binlog_format=ROW

#在row模式下开启该参数,将把sql语句打印到binlog日志里面。默认是0(off);
binlog_rows_query_log_events=on

#binlog缓存大小
binlog_cache_size=4M

#binlog日志有效时间(14天)
binlog_expire_logs_seconds=604800

#提交事务刷新数据到磁盘,默认为0
#当设置为“1”的时候,是最安全但是性能损耗最大的设置。
#为“1”时,即使系统Crash,最多丢失binlog_cache中未完成的一个事务,对实际数据没有实质影响。
#对于高并发事务系统,“sync_binlog”设置为0和设置为1的系统写入性能差距可能高达5倍甚至更多。
sync_binlog=1000

#每次事务提交的时候,都把log buffer刷到文件系统中(os buffer)去
#并且调用文件系统的“flush”操作将缓存刷新到磁盘上去
innodb_flush_log_at_trx_commit=2

#指定并行工作线程的数量,可以显著提高复制效率
slave_parallel_workers=4

#最小化日志大小,默认情况下,binlog_row_image 的值是 FULL
binlog_row_image=MINIMAL

#启用全局事务ID,模式为3(默认为0)
gtid_mode=on

#强制启用GTID一致性
enforce_gtid_consistency=on

binlog_gtid_simple_recovery=1

#自增步长
auto_increment_increment=2

#自增起始值
#注意与【主库02】的区别
auto_increment_offset=1

#备份过滤不同步的库
replicate-ignore-db=mysql,information_schema,performance_schema,sys

#数据文件存放的目录
datadir=/var/lib/mysql

#为MySQL客户端程序和服务器之间的本地通讯指定一个套接字文件
socket=/var/run/mysqld/mysqld.sock

#错误日志文件
log-error=/var/log/mysql/mysqld-error.log

#pid文件所在目录
pid-file=/var/run/mysqld/mysqld.pid

#定义了MySQL应该支持的sql语法,用于数据校验,限制不合法操作
#MySQL8是没有 NO_AUTO_CREATE_USER,所有Sql_mode中不能包含这一项
sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'

#跳过域名解析。设置为ON可以提高连接速度,但是会导致无法使用主机名连接服务器。
skip_name_resolve

#定义日志文件的时间戳格式
log_timestamps = SYSTEM

#设置服务器的默认时区
default-time-zone = '+8:00'

#指定哪些数据库不写入二进制日志
binlog-ignore-db=mysql,information_schema,performance_schema,sys

【主库02】配置

[client]
default-character-set=utf8mb4
 
[mysql]
default-character-set=utf8mb4
 
[mysqld]
#日志留存时间180天
expire_logs_days = 180

#启用MySQL的通用查询日志,可以在该日志中查看SQL执行记录
general_log = 1
general_log_file = /var/log/mysql/general.log

#MySQL监听的IP地址,如果是127.0.0.1,表示仅本机访问
bind_address=0.0.0.0

#MySQL监听端口
port=3306

#以默认的mysql用户运行
user=mysql

#服务端默认编码(数据库级别)
character-set-server=utf8mb4

#最大可连接数
max_connections = 2000

#开启慢日志
slow_query_log=ON

#设置慢查询时间
long_query_time=10

#慢查询日志保存位置
slow_query_log_file=/var/log/mysql/slowquery.log

#记录所有没有用上索引全表扫描的语句
log_queries_not_using_indexes=1

#记录optimize table,analyze table和alter table等语句引发的慢查询
log_slow_admin_statements=1
 
#数据库实例唯一标识
#启用主从、双主和集群的时候必须指定,每个节点必须不同
#注意与【主库01】的区别
server-id=2

#设置中继日志位置和名称
relay_log=slave-relay-bin

#设置中继日志索引位置和名称
relay_log_index=slave-relay-bin.index

#从其他主库同步的数据也记录二进制文件
#8.0.23之前,使用log_slave_updates
log_replica_updates=1

#读写模式
read_only=0

#自动清空中继日志
relay_log_purge=1

#中继日志损坏自动恢复同步
relay_log_recovery=1
 
#二进制文件名称集路径
log_bin=master-bin

#二进制文件索引名称集路径
log_bin_index=master-bin.index

#基于行级别的日志,默认为MIXED混合模式
binlog_format=ROW

#在row模式下开启该参数,将把sql语句打印到binlog日志里面。默认是0(off);
binlog_rows_query_log_events=on

#binlog缓存大小
binlog_cache_size=4M

#binlog日志有效时间(14天)
binlog_expire_logs_seconds=604800

#提交事务刷新数据到磁盘,默认为0
#当设置为“1”的时候,是最安全但是性能损耗最大的设置。
#为“1”时,即使系统Crash,最多丢失binlog_cache中未完成的一个事务,对实际数据没有实质影响。
#对于高并发事务系统,“sync_binlog”设置为0和设置为1的系统写入性能差距可能高达5倍甚至更多。
sync_binlog=1000

#每次事务提交的时候,都把log buffer刷到文件系统中(os buffer)去
#并且调用文件系统的“flush”操作将缓存刷新到磁盘上去
innodb_flush_log_at_trx_commit=2

#指定并行工作线程的数量,可以显著提高复制效率
slave_parallel_workers=4

#最小化日志大小,默认情况下,binlog_row_image 的值是 FULL
binlog_row_image=MINIMAL

#启用全局事务ID,模式为3(默认为0)
gtid_mode=on

#强制启用GTID一致性
enforce_gtid_consistency=on

binlog_gtid_simple_recovery=1
 
#自增步长
auto_increment_increment=2

#自增起始值
#注意与【主库01】的区别
auto_increment_offset=2

#备份过滤不同步的库
replicate-ignore-db=mysql,information_schema,performance_schema,sys

#数据文件存放的目录
datadir=/var/lib/mysql

#为MySQL客户端程序和服务器之间的本地通讯指定一个套接字文件
socket=/var/run/mysqld/mysqld.sock

#错误日志文件
log-error=/var/log/mysql/mysqld-error.log

#pid文件所在目录
pid-file=/var/run/mysqld/mysqld.pid

#定义了MySQL应该支持的sql语法,用于数据校验,限制不合法操作
#MySQL8是没有 NO_AUTO_CREATE_USER,所有Sql_mode中不能包含这一项
sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'

#跳过域名解析。设置为ON可以提高连接速度,但是会导致无法使用主机名连接服务器。
skip_name_resolve

#定义日志文件的时间戳格式
log_timestamps = SYSTEM

#设置服务器的默认时区
default-time-zone = '+8:00'

#指定哪些数据库不写入二进制日志
binlog-ignore-db=mysql,information_schema,performance_schema,sys

十一、各节点通过Docker运行MySQL容器,并限制MySQL容器的CPU和内存资源

## 运行MySQL容器,若此时宿主机没有MySQL镜像,会自动从docker hub拉取镜像,请确保能连互联网
[root@localhost ~]# docker run --cpus=4 --memory=7g --memory-swap=1g \
                    --privileged=true --name 容器名 -p 3306:3306 \
                    -e MYSQL_ROOT_HOST=% -e MYSQL_ROOT_PASSWORD=root账号的密码 \
                    -v /etc/localtime:/etc/localtime \
                    -v /data/docker/mysql/conf/:/etc/mysql/conf.d \
                    -v /data/docker/mysql/logs:/var/log/mysql \
                    -v /data/docker/mysql/data:/var/lib/mysql \
                    --restart=always -d mysql:8.0.30

参数说明:

  • -v /data/docker/mysql/conf/:/etc/mysql/conf.d是挂载MySQL的配置文件。当我们进入docker内部查看MySQL配置文件的时候,发现/etc/mysql/conf.d只是一个文件夹。在MySQL中,/etc/my.cnf 是默认配置文件,/etc/mysql/conf.d/ 下的文件为自定义配置文件。由于docker的/etc/mysql/conf.d文件挂载到本地系统的/data/docker/mysql/conf/,因此我们只需在本地系统的/data/docker/mysql/conf/写入自定义配置文件即可修改Docker容器中的MySQL配置。
  • --cpus: 限制容器运行的核数,可以使用小数。等效于--cpu-period和--cpu-quota的设置,直接配置--cpus 即可。三者关系是:--cpus = --cpu-quota / --cpu-period。
  • -m 或 --memory:设置内存的使用限额,格式是数字加单位,单位可以为 b,k,m,g,例如:100m,2g,最小为 4MB
  • --memory-swap:内存+交换分区大小总限制,格式是数字加单位,单位可以为 b,k,m,g。

若宿主机只有一个容器,需要确保容器的CPU和内存限制到宿主机的50%以下。若宿主机上运行多个容器,多个容器之间需要协调配置,确保所有容器的CPU和内存限制到宿主机的50%以下。此例中,宿主机只有一个MySQL容器,宿主机的CPU的总核数是8核,给容器分配了4核,限制了容器运行时最多占用50%的CPU资源。宿主机的总内存是16GB,给容器分配了7GB的内存,另外分配了1GB的swap内存,限制了容器运行时最多占用50%的内存资源。

注意:

1、限制容器的CPU和内存后,可能出现容器频繁重启的问题,这时需要在docker run前,删除之前运行该Docker容器的数据。

2、通过docker run限制CPU和内存后,如果想临时修改CPU和内存的限额,可以通过如下命令:

#临时更新CPU的限额为2核,容器重启后失效
docker update --cpus=2 容器名

#临时更新内存的限额为4GB,容器重启后失效
docker update -m=4G 容器名

#查看是否生效
docker inspect 容器名

扩展知识:

默认情况下,--memory-swap和--memory两组参数默认为-1,即对容器内存和swap的使用没有限制。如果在启动容器时,只指定--memory而不指定--memory-swap, 那么--memory-swap默认为--memory的两倍。swap是交换分区空间,是用磁盘空间交换来的虚拟空间。宿主机的内存由内存空间memory和交换分区空间swap两部分构成,通过free -h可以查看。--memory-swap包含了--memory,--memory-swap减去--memory就得到容器可以占用的swap的空间。--memory-swap通常需要大于等于--memory。若禁用swap,只需确保--memory-swap和--memory的值一样即可。

一般有四种设置方式:
1. 不设置
如果不设置-m,--memory和--memory-swap,两组参数默认是-1,容器默认可以用完宿主机的所有内存和 swap 分区。不过注意,如果容器占用宿主机的所有内存和 swap 分区超过一段时间后,会被宿主机系统杀死(如果没有设置--oom-kill-disable的话)。

2. 设置-m,--memory,不设置--memory-swap
给-m或--memory设置一个不小于 4M 的值,假设为 a,不设置--memory-swap。这种情况下,容器能使用的内存大小为 a,能使用的交换分区大小也为 a,因为 Docker 默认容器交换分区的大小和内存相同。如果在容器中运行一个一直不停申请内存的程序,你会观察到该程序最终能占用的内存大小为 2a。比如$ docker run -m 1G ubuntu:16.04,该容器能使用的内存大小为 1G,能使用的 swap 分区大小也为 1G。容器内的进程能申请到的总内存大小为 2G。

3. 设置-m,--memory=a,--memory-swap=b,且b > a
给-m设置一个参数 a,给--memory-swap设置一个参数 b。a 是容器能使用的内存大小,b是容器能使用的 内存大小 + swap 分区大小。所以 b 必须大于 a。b -a 即为容器能使用的 swap 分区大小。比如$ docker run -m 1G --memory-swap 3G ubuntu:16.04,该容器能使用的内存大小为 1G,能使用的 swap 分区大小为 2G。容器内的进程能申请到的总内存大小为 3G。

4. 设置-m,--memory=a,--memory-swap=-1
给-m参数设置一个正常值,而给--memory-swap设置成 -1。这种情况表示限制容器能使用的内存大小为 a,而不限制容器能使用的 swap 分区大小。这时候,容器内进程能申请到的内存大小为 a + 宿主机的 swap 大小。

十二、各节点MySQL添加新账号test,并禁用root账号(生产环境,为了安全,通常禁用root账号)

## 进入MySQL容器
[root@localhost ~]# docker exec -it 容器名 /bin/bash

## 通过MySQL命令行终端进入MySQL
bash-4.4# mysql –uroot –p

## 输入运行MySQL容器时设置的“root账号的密码”

## 认证成功,进入MySQL命令行终端
mysql >

## 创建test用户,并设置密码为TestDb0101
mysql> CREATE USER 'test'@'%' IDENTIFIED BY 'TestDb0101';
mysql> CREATE USER 'test'@'localhost' IDENTIFIED BY 'TestDb0101';

## 为test用户授权
mysql> GRANT ALL PRIVILEGES ON *.* TO 'test'@'%' WITH GRANT OPTION;
mysql> GRANT ALL PRIVILEGES ON *.* TO 'test'@'localhost' WITH GRANT OPTION;

## 删除root账号
mysql> DROP USER 'root'@'%';
mysql> DROP USER 'root'@'localhost';

## 重新加载MySQL授权表,刷新权限
mysql> FLUSH PRIVILEGES;

## 退出MySQL命令终端
mysql> exit

十三、各节点MySQL创建slave用户,并授权复制权限

【主库01】执行

## 通过MySQL命令行终端进入MySQL
bash-4.4# mysql –utest –pTestDb0101

## 认证成功,进入MySQL命令行终端
mysql >

## 创建slave用户专门用于双主复制,并设置密码
mysql >create user 'slave'@'%' identified with mysql_native_password by 'TestDbSync0101'; 

## 授予复制权限
mysql >grant replication slave on *.* to 'slave'@'%'; 

## 重新加载MySQL授权表,刷新权限
mysql >FLUSH PRIVILEGES;

【主库02】执行

## 通过MySQL命令行终端进入MySQL
bash-4.4# mysql –utest –pTestDb0101

## 认证成功,进入MySQL命令行终端
mysql >

## 创建slave用户专门用于双主复制,并设置密码
mysql >create user 'slave'@'%' identified with mysql_native_password by 'TestDbSync0101'; 

## 授予复制权限
mysql >grant replication slave on *.* to 'slave'@'%'; 

## 重新加载MySQL授权表,刷新权限
mysql >FLUSH PRIVILEGES;

十四、各节点MySQL配置主从复制

【主库01】执行

## 删除所有的binglog日志文件,并将日志索引文件清空,重新开始所有新的日志文件
## 用于第一次进行搭建双主复制库时,进行主库binlog的初始化工作
mysql> reset master;

## 停止复制功能
mysql> stop slave;

## 查看master状态
## 结果中的File对应后面设置【主库02】的MASTER_LOG_FILE
## 结果中的Position对应后面设置【主库02】的MASTER_LOG_POS
mysql> show master status;  
+-------------------+----------+--------------+------------------+-------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master-bin.000001 |      157 |              |                  |                   |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

【主库02】执行

## 删除所有的binglog日志文件,并将日志索引文件清空,重新开始所有新的日志文件
## 用于第一次进行搭建双主复制库时,进行主库binlog的初始化工作
mysql> reset master;

## 停止复制功能
mysql> stop slave;

## 查看master状态
## 结果中的File对应后面设置【主库01】的MASTER_LOG_FILE
## 结果中的Position对应后面设置【主库01】的MASTER_LOG_POS
mysql> show master status;  
+-------------------+----------+--------------+------------------+-------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master-bin.000001 |      157 |              |                  |                   |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

【主库01】执行

## 将【主库02】设置为本节点的master
## MASTER_LOG_FILE对应【主库02】show master status查询结果的File
## MASTER_LOG_POS对应【主库02】show master status查询结果的Position
mysql> CHANGE MASTER TO MASTER_HOST='192.168.111.199',MASTER_PORT=3306,
       MASTER_USER='slave',MASTER_PASSWORD='TestDbSync0101',
       MASTER_AUTO_POSITION=1; 

## 启用复制功能
mysql> start slave;

【主库02】执行

## 将【主库01】设置为本节点的master
## MASTER_LOG_FILE对应【主库01】show master status查询结果的File
## MASTER_LOG_POS对应【主库01】show master status查询结果的Position
mysql> CHANGE MASTER TO MASTER_HOST='192.168.111.198',MASTER_PORT=3306,
       MASTER_USER='slave',MASTER_PASSWORD='TestDbSync0101',
       MASTER_AUTO_POSITION=1; 

## 启用复制功能
mysql> start slave;

MYSQL数据库双主复制已经配置好了,【主库01】和【主库02】上可以通过show slave status\G命令查看slave的状态,只要Slave_IO_Running和Slave_SQL_Running都是Yes,就表示双主复制配置成功。

【主库01】执行

## 查看slave状态
mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for source to send event
                  Master_Host: 192.168.111.199
                  Master_User: slave
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master-bin.000003
          Read_Master_Log_Pos: 197
               Relay_Log_File: slave-relay-bin.000008
                Relay_Log_Pos: 375
        Relay_Master_Log_File: master-bin.000003
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 197
              Relay_Log_Space: 755
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 2
                  Master_UUID: 7397c458-0f68-11ef-a48b-0242ac110002
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Replica has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 5e8fbcfe-0f68-11ef-a4bd-0242ac110002:1-494
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
       Master_public_key_path: 
        Get_master_public_key: 0
            Network_Namespace: 
1 row in set, 1 warning (0.00 sec)

【主库02】执行

## 查看slave状态
mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for source to send event
                  Master_Host: 192.168.111.198
                  Master_User: slave
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master-bin.000003
          Read_Master_Log_Pos: 197
               Relay_Log_File: slave-relay-bin.000008
                Relay_Log_Pos: 375
        Relay_Master_Log_File: master-bin.000003
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 197
              Relay_Log_Space: 755
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 1
                  Master_UUID: 5e8fbcfe-0f68-11ef-a4bd-0242ac110002
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Replica has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 5e8fbcfe-0f68-11ef-a4bd-0242ac110002:1-494
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
       Master_public_key_path: 
        Get_master_public_key: 0
            Network_Namespace: 
1 row in set, 1 warning (0.01 sec)

到这一步,就可以使用MySQL客户端(如:Heidisql、Navicat)连接【主库01】192.168.111.198:3306或【主库02】192.168.111.199:3306访问MySQL数据库了,账号是test,密码是TestDb0101。

十五、验证双主复制

在【主库01】新建一个数据库/表,可以立即在【主库02】查询到。反之亦然。

双主复制验证

十六、部署Keepalived

1、各节点安装Keepalived:

## 创建目录,并进入该目录
[root@localhost ~]# yum install -y keepalived

2、各节点修改配置文件keepalived.conf

说明:采用 keepalived 作为高可用方案时,两个节点最好都设置成 BACKUP模式,避免因为意外情况下(比如:脑裂),相互抢占导致往两个节点写入相同数据而引发冲突;

【主库01】修改

## 修改Keepalived配置文件
[root@localhost ~]# vi /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
    #标识本节点的字符串
    router_id 192.168.111.198
    script_user root
    enable_script_security
}

vrrp_script chk_mysql {
    #检查mysql状态的脚本
    script "/etc/keepalived/chk_mysql.sh"
    #指定脚本执行的间隔。单位是秒。默认为1s。
    interval 1
}


vrrp_instance VI_1 {
    #标识主节点服务(只有MASTER和BACKUP两种,大写)
    state BACKUP
	#根据网卡配置
    interface ens33
	#虚拟路由id,两台服务器配置一样唯一id  
    virtual_router_id 1
	#优先级,高于另一台服务器的值即可  
    priority 100
	#两台服务器配置一样,不抢占ip
    nopreempt
	#MASTER和BACKUP节点之间的同步检查时间间隔,单位为秒
    advert_int 1
	#验证类型和验证密码
    authentication {
	    #PAAS(默认),HA
        auth_type PASS
		#各节点的keepalived使用相同明文才可以互通
        auth_pass 1111
    }
	#有多个vip可在此处继续增加,每个IP一行,不用逗号分隔
    virtual_ipaddress {
        192.168.111.195
    }

    track_script {
        chk_mysql
    }
}

【主库02】修改

## 修改Keepalived配置文件
[root@localhost ~]# vi /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
    #标识本节点的字符串
    router_id 192.168.111.199
    script_user root
    enable_script_security
}

vrrp_script chk_mysql {
    #检查mysql状态的脚本
    script "/etc/keepalived/chk_mysql.sh"
    #指定脚本执行的间隔。单位是秒。默认为1s。
    interval 1
}


vrrp_instance VI_1 {
    #标识主节点服务(只有MASTER和BACKUP两种,大写)
    state BACKUP
	#根据网卡配置
    interface ens33
	#虚拟路由id,两台服务器配置一样唯一id  
    virtual_router_id 1
	#优先级,低于另一台服务器的值即可  
    priority 50
	#两台服务器配置一样,不抢占ip
    nopreempt
	#MASTER和BACKUP节点之间的同步检查时间间隔,单位为秒
    advert_int 1
	#验证类型和验证密码
    authentication {
	    #PAAS(默认),HA
        auth_type PASS
		#各节点的keepalived使用相同明文才可以互通
        auth_pass 1111
    }
	#有多个vip可在此处继续增加,每个IP一行,不用逗号分隔
    virtual_ipaddress {
        192.168.111.195
    }

    track_script {
        chk_mysql
    }
}

3、各节点添加脚本文件check_mysql.sh,内容都一样,如下:

脚本说明:

  • 判断本节点3306端口是否存在。若不存在,就停止本节点的Keepalived进程,让VIP漂移到另一节点;
  • 需要先启动MySQL容器,再启动Keepalived。由于一个节点开机时,MySQL容器可能还未启动,而Keepalived进程可能先启动。这时为了防止因为没有3306端口,导致已启动的Keepalived进程被停止,需要基于系统启动时间是否小于60秒判断是否为刚刚开机。若刚开机,则不检测MySQL的状态。
## 检测mysql状态的脚本
[root@localhost ~]# vi /etc/keepalived/check_mysql.sh
#!/bin/bash

##判断是否刚开机
#获取系统启动时间
up_time=$(uptime | awk '{print $3}' | cut -d',' -f1)

#打印系统启动时间(格式:X小时:Y分,如1:28表示1小时28分钟)
echo 系统已开机(格式:X小时:Y分钟)$up_time

#将开机时间基于冒号分隔转换成秒
up_seconds=$(echo $up_time | awk -F: '{print $1*60*60+$2*60}')

#打印系统启动时间,单位:秒
echo 系统已开机(单位:秒)$up_seconds

# 如果uptime输出小于60秒,则系统可能刚启动
if [ $up_seconds -lt 60 ]; then
    echo "系统刚刚启动"
else
    echo "系统已运行一段时间"

    ##检测mysql的3306端口,若3306端口不存在,则关闭keepalived进程
    count=$(netstat -na|grep "LISTEN"|grep -w "3306"|wc -l)
    echo "count=="${count}
    if [ "${count}" -eq 0 ]; then
       systemctl stop keepalived
       echo "stopped keepalived-------"
    fi
fi

 4、为脚本文件check_mysql.sh赋予执行权限

[root@localhost ~]# chmod 755 /etc/keepalived/chk_mysql.sh

5、启动keepalived,并让其开机启动

## 启动Keepalived
[root@localhost ~]# systemctl start keepalived

## 设置开机自启
[root@localhost ~]# systemctl enable keepalived

到这一步,就可以使用MySQL客户端(如:Heidisql、Navicat)访问VIP:192.168.111.195:3306的MySQL数据库了,账号是test,密码是TestDb0101。

至此,MySQL 双主复制已部署完成。

在两个节点中,可以执行如下命令查看VIP目前漂移到哪个节点:

## 查看192.168.111网段的IP地址
[root@localhost ~]# ip ad | grep 111
查看VIP当前在哪个节点

由上图可见,当前VIP:192.168.111.195 漂移到【主库01】:192.168.111.198上。这时,若将【主库01】主机关机 / 重启,再执行如下命令,将看到VIP:192.168.111.195 又漂移到【主库02】:192.168.111.199上了。

## 查看192.168.111网段的IP地址
[root@localhost ~]# ip ad | grep 111
【主库01】重启后,VIP漂移到了【主库02】节点

用户可以关机/重启任意一个节点(不论VIP当前漂移到哪个节点),依赖MySQL数据库的应用都能正常运行,由此实现了MySQL的高可用HA。


十七、常见问题

问题1描述:使用show slave status\G命令时,发现:Last_IO_Error: error connecting to master

...
Slave_IO_Running: Connecting
Slave_SQL_Running: Yes
...
Last_IO_Error: error connecting to master 'slave@192.168.111.199:3306' - retry-time: 60 retries: 580 message: Can't connect to MySQL server on '192.168.111.199:3306' (113)
...

问题1分析:这是由于七、各节点Docker与Firewalld冲突解决 八、各节点防火墙配置 这2部分设置了防火墙,而后面由于相关原因 执行systemctl stop firewalld命令 关闭了防火墙造成的。再次开启防火墙,并重新执行 十三、各节点MySQL配置主从复制 部分的操作,就解决问题了。

问题2描述:

当一个系统平台的数据库搭建的是双主复制,当VIP漂移到某个节点时,将该节点关机/重启。若此时,某个用户正在操作平台,页面可能报错:CommunicationsException:Communications link failure。

问题2分析:

这是由于Keepalived在进行VIP漂移时,对漂移前的数据库的访问已经发生,而访问响应还未返回造成的。该问题在VIP漂移完成后,可以自动解决。漂移完成后,就不会存在该问题了。

所以,可以在节点关机 / 重启10到15秒后,再在系统平台上进行操作。

另外,为了用户友好性,系统平台服务端可以拦截该异常,并在页面上显示更友好的提示信息。

错误提示

问题3描述:

当停电或手动关机某个节点一段时间后,这时,即使恢复数据库同步,当某个节点的数据过多时,也可能出现数据不一致问题。

问题3分析:

第一步:检查两个节点的数据,查看哪个节点的数据量更多。

第二步:进入该节点的数据库中,备份每一个业务数据库的数据到sql脚本中。

第三步:删除两个节点的所有业务数据库。

第四步:参照 十四、各节点MySQL配置主从复制 进行操作,确保主从复制状态正常。

第五步:使用数据库客户端通过VIP和3306端口连接数据库,导入备份的sql脚本进行数据库还原操作。

第六步:比较两个节点的数据库客户端以及VIP的数据库客户端中,数据量大小是否一致。如某个业务数据库仍然存在数据量不一致的问题,重新在VIP数据库客户端中,删除该业务数据库,然后重新执行第五步操作。

十八、资料参考

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐