今天闲来无事, 打算搭建一个 MySQL 的高可用架构, 采用的是 MySQL 的主主结构, 再外加 Keepalived, 对外统一提供虚 IP. 先来说说背景吧, 现在的项目为了高可用性, 都是避免单节点的存在的, 比如, 我们的应用程序, 都是部署多个节点, 通过 Nginx 做负载均衡, 某个节点出现问题, 并不会影响整体应用. 那么数据库层如何搭建高可用的架构呢? 今天我们就来看看.
整体架构
MySQL 采用主主结构, 我们使用两台机器就够了, 然后再这两台机器上再安装 Keepalived, 使用 vrrp 技术, 虚拟出一个 IP. 两台机器如下:
- 192.168.73.141:MySQL(主 1),Keepalived(MASTER)
- 192.168.73.142:MySQL(主 2),Keepalived(BACKUP)
192.168.73.150: 虚 IP
整体架构图如下:
MySQL 主主搭建
我们分别在两台机器上安装 MySQL, 使用 yum 方式安装, 首先从 MySQL 官网下载 rpm 包, 选择对应的系统, 在这里, 我们选择 CentOS7 的 prm 包, mysql80-community-release-el7-3.noarch.rpm. 然后将 rpm 文件分别上传到两台机器上, 接下来我们就是用 yum 来安装 MySQL.
在 192.168.73.141(主 1) 执行如下命令,
- # 使用 yum 安装 rpm 包
- yum install mysql80-community-release-el7-3.noarch.rpm
- # 安装 MySQL 社区版 时间较长 耐心等待
- yum install MySQL-community-server
- # 启动 MySQL 服务
- service mysqld start
到这里, MySQL 就安装完成, 并且正常启动了. 然后, 我们用 root 账号登录 MySQL, 并创建一个可用的账号.
- # 从 MySQL 的日志中 找到 root 账号的临时密码
- grep 'temporary password' /var/log/mysqld.log
- # 使用 root 账号登录 输入临时密码 登录成功
- MySQL -uroot -p
- # 修改 root 账号的密码 使用 MYSQL_NATIVE_PASSWORD 的加密方式 这种方式大多数客户端都可以连接
- ALTER USER 'root'@'localhost' IDENTIFIED WITH MYSQL_NATIVE_PASSWORD BY 'MyNewPass4!';
- # 创建 MySQL 账号
- CREATE USER 'USER'@'%' IDENTIFIED WITH MYSQL_NATIVE_PASSWORD BY 'USER_PWD';
- # 对 USER 账号授权
- GRANT ALL ON *.* TO 'USER'@'%';
- # 刷新权限
- FLUSH PRIVILEGES;
好了, 到这里, 在 192.168.73.141 上安装 MySQL 成功, 并且创建了 USER 账户, 我们可以使用 NAVICAT 等客户端连接.
在 192.168.73.142(主 2) 上也执行上面的命令, 这样我们在两台机器上都安装了 MySQL. 接下来, 我们就要配置 MySQL 的主主结构了.
首先, 我们修改 192.168.73.141(主 1) 上的 my.cnf 文件.
- VIM /etc/my.cnf
- datadir=/var/lib/MySQL
- socket=/var/lib/MySQL/MySQL.sock
- log-error=/var/log/mysqld.log
- pid-file=/var/run/mysqld/mysqld.pid
- # 配置 server-id 每个 MySQL 实例的 server-id 都不能相同
- server-id=1
- # MySQL 的日志文件的名字
- log-bin=mysql_master
- # 作为从库时 更新操作是否写入日志 on: 写入 其他数据库以此数据库做主库时才能进行同步
- log-slave-updates=on
- # MySQL 系统库的数据不需要同步 我们这里写了 3 个 更加保险
- # 同步数据时忽略一下数据库 但是必须在使用 use db 的情况下才会忽略; 如果没有使用 use db 比如 create user 数据还是会同步的
- replicate-ignore-db=information_schema
- replicate-ignore-db=MySQL
- replicate-ignore-db=performance_schema
- replicate-ignore-db=sys
- # 使用通配符忽略 MySQL 系统库的表 这样在 create user 时也不会进行同步了
- replicate_wild_ignore_table=information_schema.%
- replicate_wild_ignore_table=MySQL.%
- replicate_wild_ignore_table=performance_schema.%
- replicate_wild_ignore_table=sys.%
- # MySQL 系统库的日志不计入 binlog 这样更加保险了
- binlog-ignore-db=information_schema
- binlog-ignore-db=MySQL
- binlog-ignore-db=performance_schema
- binlog-ignore-db=sys
在 192.168.73.142(主 2) 上也修改 my.cnf 文件, 我们直接复制过去, 只需要修改其中的两个地方, 如下:
- # 配置 server-id=2
- server-id=2
- # MySQL 的日志文件的名字 不改名字也可以 这里主要为了区分
- log-bin=mysql_slave
配置文件都已经修改好了, 我们分别在 192.168.73.141(主 1) 和 192.168.73.142(主 2) 上重启 MySQL 服务,
service mysqld restart
下面我们就要配置主从了, 其实主主模式就是配置两个主从, 先配置 192.168.73.141(主 1)->192.168.73.142(主 2) 的主从, 然后再反过来配置 192.168.73.142(主 2)->192.168.73.141(主 1) 的主从, 这样主主的模式就配置好了.
我们先来配置 192.168.73.141(主 1)->192.168.73.142(主 2) 的主从
先登录 192.168.73.141(主 1) 的数据库, 并执行如下命令:
- # 创建备份的账号 使用 MYSQL_NATIVE_PASSWORD 的方式加密
- MySQL> CREATE USER 'repl_master'@'%' IDENTIFIED WITH MYSQL_NATIVE_PASSWORD BY 'password';
- # 对 repl_master 授予备份的权限
- MySQL> GRANT REPLICATION SLAVE ON *.* TO 'repl_master'@'%';
- # 刷新权限
- MySQL> FLUSH PRIVILEGES;
- # 查看 MySQL 主节点的状态
- MySQL> SHOW MASTER STATUS;
- +-------------------+---------+--------------+---------------------------------------------+------------------+
- | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
- +-------------------+---------+--------------+---------------------------------------------+------------------+
- | mysql_master.000001 | 516 | | information_schema,MySQL,performance_schema,sys | |
- +-------------------+---------+--------------+---------------------------------------------+------------------+
- 1 row in set
我们要记住 binlog 文件的名字, 也就是 mysql_master.000001, 和位置, 也就是 516.
然后, 我们再登录到 192.168.73.142(主 2) 的数据库, 执行如下命令:
- MySQL> CHANGE MASTER TO
- # MySQL 主的 IP
- -> MASTER_HOST='192.168.73.141',
- # MySQL 主的端口
- -> MASTER_PORT=3306
- # MySQL 主的备份账号
- -> MASTER_USER='repl_master',
- # MySQL 主的备份账号密码
- -> MASTER_PASSWORD='password',
- # 日志文件 通过 show master status 得到的
- -> MASTER_LOG_FILE='mysql_master.000001',
- # 日志文件位置 通过 show master status 得到的
- -> MASTER_LOG_POS=516;
- # 开启从库
- MySQL> START SLAVE;
- # 查看从库的状态
- MySQL> SHOW SLAVE STATUS;
这样, 192.168.73.141(主 1)->192.168.73.142(主 2) 的主从就搭建好了. 然后, 我们再反过来, 搭建 192.168.73.142(主 2)->192.168.73.141(主 1) 的主从.
先登录 192.168.73.142(主 2) 的数据库, 执行如下命令:
- # 创建备份的账号 使用 MYSQL_NATIVE_PASSWORD 的方式加密
- MySQL> CREATE USER 'repl_slave'@'%' IDENTIFIED WITH MYSQL_NATIVE_PASSWORD BY 'password';
- # 对 repl_slave 授予备份的权限
- MySQL> GRANT REPLICATION SLAVE ON *.* TO 'repl_slave'@'%';
- # 刷新权限
- MySQL> FLUSH PRIVILEGES;
- # 查看 MySQL 主节点的状态
- MySQL> SHOW MASTER STATUS;
- +-------------------+---------+--------------+---------------------------------------------+------------------+
- | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
- +-------------------+---------+--------------+---------------------------------------------+------------------+
- | mysql_slave.000001 | 379 | | information_schema,MySQL,performance_schema,sys | |
- +-------------------+---------+--------------+---------------------------------------------+------------------+
- 1 row in set
再登录到 192.168.73.141(主 1) 的数据库, 执行如下命令:
- MySQL> CHANGE MASTER TO
- # MySQL 主的 IP
- -> MASTER_HOST='192.168.73.142',
- # MySQL 主的端口
- -> MASTER_PORT=3306
- # MySQL 主的备份账号
- -> MASTER_USER='repl_slave',
- # MySQL 主的备份账号密码
- -> MASTER_PASSWORD='password',
- # 日志文件 通过 show master status 得到的
- -> MASTER_LOG_FILE='mysql_slave.000001',
- # 日志文件位置 通过 show master status 得到的
- -> MASTER_LOG_POS=379;
- # 开启从库
- MySQL> START SLAVE;
- # 查看从库的状态
- MySQL> SHOW SLAVE STATUS;
这样, 192.168.73.142(主 2)->192.168.73.141(主 1) 的主从也搭建好了. 我们可以使用 navicat 分别连接 192.168.73.141(主 1) 和 192.168.73.142(主 2), 并执行建表, 插入语句, 验证一下主主同步是否成功, 这里就不给大家演示了.
Keepalived 高可用
MySQL 主主结构已经搭建好了, 无论从哪个 MySQL 插入数据, 都会同步到另外一个 MySQL. 虽然有了 MySQL 主主结构, 但是不能保证高可用, 比如, 我们的应用程序连接的是 192.168.73.141(主 1), 倘若 192.168.73.141(主 1) 的 MySQL 挂掉了, 我们的应用程序并不能自动的切换到 192.168.73.142(主 2), 我们的应用程序也是不可用的状态. 要做到这一点, 就要借助于 Keepalived.
Keepalived 有两个主要的功能:
提供虚 IP, 实现双机热备
通过 LVS, 实现负载均衡
我们这里使用 Keepalived, 只需要使用其中的一个功能, 提供虚 IP, 实现双机热备. 我们需要在 192.168.73.141(主 1) 和 192.168.73.142(主 2) 上都安装 Keepalived, 执行命令如下:
yum install keepalived
我们直接使用 yum 进行安装. 安装完之后, 编辑 keepalived 的配置文件, 首先编辑 192.168.73.141(主 1) 上的配置文件, 如下:
- VIM /etc/keepalived/keepalived.conf
- # 全局配置 不用动 只需注释掉 vrrp_strict
- global_defs {
- notification_email {
- acassen@firewall.loc
- failover@firewall.loc
- sysadmin@firewall.loc
- }
- notification_email_from Alexandre.Cassen@firewall.loc
- smtp_server 192.168.200.1
- smtp_connect_timeout 30
- router_id LVS_DEVEL
- vrrp_skip_check_adv_addr
- #必须注释掉 否则报错
- #vrrp_strict
- vrrp_garp_interval 0
- vrrp_gna_interval 0
- }
- # 检查 MySQL 服务是否存活的脚本
- vrrp_script chk_mysql {
- script "/usr/bin/killall -0 mysqld"
- }
- # vrrp 配置虚 IP
- vrrp_instance VI_1 {
- # 状态: MASTER 另外一台机器为 BACKUP
- state MASTER
- # 绑定的网卡
- interface ens33
- # 虚拟路由 id 两台机器需保持一致
- virtual_router_id 51
- # 优先级 MASTER 的值要大于 BACKUP
- priority 100
- advert_int 1
- authentication {
- auth_type PASS
- auth_pass 1111
- }
- # 虚拟 IP 地址 两台 keepalived 需要一致
- virtual_ipaddress {
- 192.168.73.150
- }
- # 检查脚本 vrrp_script 的名字
- track_script {
- chk_mysql
- }
- }
- ### 后边的 virtual_server 全部注释掉 它是和 LVS 做负载均衡用的 这里用不到
- ###
再编辑 192.168.73.142(主 2) 上的配置文件, 只需要将 state MASTER 改为 state BACKUP, 如下:
state BACKUP
通过 keepalived 的配置, 我们对外提供 192.168.73.150 的 IP, 这个 IP 实际指向是 192.168.73.141(主 1), 因为它的 state 是 MASTER. 当 keepalived 检测到 192.168.73.141(主 1) 上的 MySQL 不可用时, 会自动切换到 192.168.73.142(主 2). 对于外部用户是无感知的, 因为外部统一使用的是 192.168.73.150.
我们再来看看检测的脚本 / usr/bin/killall -0 mysqld,killall 命令不是系统自带的, 需要安装, 我们还是使用 yum 来安装, 如下:
- # 先查询一下 killall
- yum search killall
- # 找到了 psmisc.x86_64
- Loading mirror speeds from cached hostfile
- ===============Matched: killall ================================
- psmisc.x86_64 : Utilities for managing processes on your system
- # 安装 psmisc
- yum install psmisc
这样我们就可以使用 killall 命令了. killall -0 并不是杀掉进程, 而是检查进程是否存在, 如果存在则返回 0, 如果不存在则返回 1. 当返回 1 时, keepalived 就会切换主备状态.
好了, killall 也介绍完了, 我们在两台机器上启动 keepalived, 如下:
- # 启动 keepalived
- service keepalived start
然后, 我们在 192.168.73.141(主 1) 上查看一下 IP 是否有 192.168.73.150, 如下:
- ip addr
- 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
- link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
- .NET 127.0.0.1/8 scope host lo
- valid_lft forever preferred_lft forever
- inet6 ::1/128 scope host
- valid_lft forever preferred_lft forever
- 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
- link/ether 00:0c:29:57:8c:cd brd ff:ff:ff:ff:ff:ff
- .NET 192.168.73.141/24 brd 192.168.73.255 scope global noprefixroute ens33
- valid_lft forever preferred_lft forever
- .NET 192.168.73.150/32 scope global ens33 # 我们看到了 192.168.73.150
- valid_lft forever preferred_lft forever
- inet6 fe80::720b:92b0:7f78:57ed/64 scope link noprefixroute
- valid_lft forever preferred_lft forever
到这里, keepalived 的配置就完成了, 我们通过 navicat 连接 192.168.73.150, 可以正常的连接数据库, 实际上它连接的是 192.168.73.141 的数据库, 我们操作数据库也是正常的.
然后, 我们停掉 192.168.73.141(主 1) 上的 MySQL 服务,
- service mysqld stop
- # 再用 ip addr 查看一下
- ip addr
- 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
- link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
- .NET 127.0.0.1/8 scope host lo
- valid_lft forever preferred_lft forever
- inet6 ::1/128 scope host
- valid_lft forever preferred_lft forever
- 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
- link/ether 00:0c:29:57:8c:cd brd ff:ff:ff:ff:ff:ff
- .NET 192.168.73.141/24 brd 192.168.73.255 scope global noprefixroute ens33
- valid_lft forever preferred_lft forever
- inet6 fe80::720b:92b0:7f78:57ed/64 scope link noprefixroute
- valid_lft forever preferred_lft forever
192.168.73.150 的 IP 找不到了, 我们再去 192.168.73.142(主 2) 上去查看, 可以发现 192.168.73.150 的 IP. 我们在 navicat 上操作数据库, 是可以正常使用的. 但这时实际连接的是 192.168.73.142(主 2) 的数据库. 我们是没有感知的. 如果我们把 192.168.73.141(主 1) 上的 MySQL 服务再启动起来, 192.168.73.150 还会切换到 192.168.73.141(主 1).
总结
我们通过 MySQL 主主结构 + keepalived 双机热备实现了 MySQL 的高可用, 我们应用程序可以连接虚 IP, 具体连接的实际 MySQL, 不需要我们关心. 如果我们再做读写分离的话, 可以将 MySQL(主 2) 作为主, 配置数据库的主从关系. 这时, 虚 IP 连接的是 MySQL(主 1),MySQL(主 1) 将数据同步到 MySQL(主 2), 然后 MySQL(主 2) 再将数据同步到其他从库. 如果 MySQL(主 1) 挂掉, 虚 IP 指向 MySQL(主 2),MySQL(主 2) 再将数据同步到其他从库.
来源: https://www.cnblogs.com/boboooo/p/13891447.html