From a90a43fc090aedbd87c57f09242282b20aa3adc6 Mon Sep 17 00:00:00 2001 From: LiJiansheng Date: Fri, 4 May 2018 17:24:57 +0800 Subject: [PATCH] add xenon documents. Signed-off-by: LiJiansheng --- README.md | 36 +++ docs/how_to_build_and_run_xenon.md | 467 +++++++++++++++++++++++++++++ docs/how_xenon_works.md | 459 ++++++++++++++++++++++++++++ docs/images/xenon.png | Bin 0 -> 39196 bytes docs/xenoncli_commands.md | 214 +++++++++++++ 5 files changed, 1176 insertions(+) create mode 100644 README.md create mode 100644 docs/how_to_build_and_run_xenon.md create mode 100644 docs/how_xenon_works.md create mode 100644 docs/images/xenon.png create mode 100644 docs/xenoncli_commands.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..3731356 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +[![Build Status](https://travis-ci.org/radondb/xenon.png)](https://travis-ci.org/radondb/xenon) +[![Go Report Card](https://goreportcard.com/badge/github.com/radondb/xenon)](https://goreportcard.com/report/github.com/radondb/xenon) +[![codecov.io](https://codecov.io/gh/radondb/xenon/graphs/badge.svg)](https://codecov.io/gh/radondb/xenon/branch/master) + +# Xenon + +![](docs/images/xenon.png) + +## Overview + +`Xenon` is a MySQL HA and replication management tool using Raft protocol. + +* Fast Failover with no lost transactions +* Streaming & Speed-Unmatched backup/restore +* Mysql Operation and Maintenance +* No central control and easy-to-deploy +* As a Cloud App + +## Documentation + +- [build_and_run](docs/how_to_build_and_run_xenon.md) : How to build and run Xenon. +- [client_commands](docs/xenoncli_commands.md) : Xenon client commands. +- [how_xenon_works](docs/how_xenon_works.md) : How Xenon works. + +## Status + +Xenon is production ready, it has been used in production like [MySQL Plus](https://www.qingcloud.com/products/mysql-plus/) + +## Issues + +The [integrated github issue tracker](https://github.com/radondb/xenon/issues) +is used for this project. + +## License + +Xenon is released under the GPLv3. See [LICENSE](LICENSE) diff --git a/docs/how_to_build_and_run_xenon.md b/docs/how_to_build_and_run_xenon.md new file mode 100644 index 0000000..9e8c271 --- /dev/null +++ b/docs/how_to_build_and_run_xenon.md @@ -0,0 +1,467 @@ +[TOC] + +# How to build and run xenon + +## Requirements +1. `Xenon` is a self-contained binary that does not require additional system libraries at the operating system level. It is built on Linux. I have no hint about MS Windows and OS/X, and the build is incompatible with Windows and OS/X. It is a standalone application. When configured to run with a `MySQL` backend, so mysqld is required. +2. Xenon use `GTID` parallel replication technology, MySQL version is best `5.7 or higher`. +3. [Go](http://golang.org) version 1.8 or newer is required("sudo apt install golang" for ubuntu or "yum install golang" for centOS/redhat). + +## Step1. Download src code from github + +``` +$ git clone https://github.com/xenon/xenon +``` + +## Step2. Build + +### Step2.1 make build +After download radon src code from github, it will generate a directory named "xenon", execute the following commands: +``` +$ cd xenon +$ make build +``` +The binary executable file is in the "bin" directory, execute command "ls bin/": +``` +$ ls bin/ + +xenon xenoncli +``` + +### Step2.2 make test +``` +$ make test +``` +Next is a simple analysis of the things how we test xenon. (You can jump over `step 2.2` and `step 2.3`, continue reading from `Step 3`) + +In xenon, we developed a distributed test framework that makes distributed testing exceptionally easy. It can do to simulate MySQL Server, network flash, brain crack and other infrastructure failures. So it's easy to build a Raft+ cluster with 511 nodes. Constantly Kill Leader, brain cracker and recovery. After a period of time to confirm the status of the cluster to ensure that the logic is correct. + +For example, to create a 511 nodes Raft+ cluster, let's do the following: + +1. Kill Leader and wait for the birth of a new Leader. + +2. Forces all members of a cluster to Candidate state, and then confirms that the cluster state ultimately has only one Leader. +3. Forces all members of a cluster to be set as Leader state, and then confirm whether the cluster status ultimately has only one Leader. + +The logic of the code is as follows : + +``` +log := xlog.NewStdLog(xlog.Level(xlog.DEBUG)) +_, rafts, cleanup := MockRafts(log, 511) +defer cleanup() + +// Start the Raft+ cluster. +for _, raft := range rafts { + raft.Start() +} + +// Wait the Leader eggs. +MockWaitLeaderEggs(rafts, 1) + +// Case1: Stop the Leader(mock to IDLE). +MockStateTransition(leader, IDLE) + +// Case2: Force all the 511 nodes state to Candidate and then check the cluster. +for _, raft := range rafts { + MockStateTransition(raft, CANDIDATE) +} +MockWaitLeaderEggs(rafts, 1) + +// Case3: Force all the 511 nodes state to Leader and then check the cluster. +for _, raft := range rafts { + MockStateTransition(raft, LEADER) +} +MockWaitLeaderEggs(rafts, 1) +... ... +``` + +### Step2.3 Coverage Test + +``` +$ make coverage +``` + +## Step3. Config +xenon uses a configuration file xenon.conf.json. The repository includes a file called conf/xenon-sample.conf.json with so sic settings. Xenon is so smart that it does not require you to do anything with MySQL. Just need to install the MySQL service. + +Suppose you have already installed mysqld, if not, please reference [[MySQL 5.7 Install]](https://dev.mysql.com/doc/refman/5.7/en/installing.html) + +### Step3.1 Prepare the configuration file +* Copy xenon/conf/xenon-sample.conf.json to /etc/xenon/xenon.json +``` +$ sudo cp xenon/conf/xenon-sample.conf.json /etc/xenon/xenon.json +``` +* Make the following changes to the "${YOUR -....}" section: +``` +$ sudo vi /etc/xenon/xenon.json +``` + +``` +{ + "server": + { + "endpoint":"${YOUR-HOST}:8801" + }, + + "raft": + { + "meta-datadir":"raft.meta", + "leader-start-command":"${YOUR-LEADER-START-COMMAND}", + "leader-stop-command":"${YOUR-LEADER-STOP-COMMAND}" + }, + + "mysql": + { + "admin":"root", + "passwd":"", + "host":"localhost", + "port":${YOUR-MYSQL-PORT}, + "basedir":"${YOUR-MYSQL-BIN-DIR}", + "defaults-file":"${YOUR-MYSQL-CNF-PATH}" + }, + + "replication": + { + "user":"${YOUR-MYSQL-REPL-USER}", + "passwd":"${YOUR-MYSQL-REPL-PWD}" + }, + "backup": + { + "ssh-host":"%{YOUR-HOST}", + "ssh-user":"${YOUR-SSH-USER}", + "ssh-passwd":"${YOUR-SSH-PWD}", + "basedir":"${YOUR-MYSQL-BIN-DIR}", + "backup-dir":"${YOUR-BACKUP-DIR}", + "xtrabackup-bindir":"${YOUR-XTRABACKUP-BIN-DIR}" + }, + + "rpc": + { + "request-timeout":500 + }, + + "log": + { + "level":"INFO" + } +} +``` +Here's a [simple template](config/xenon-simple.conf.json) for your reference. + +### Step3.2 Configuration instructions + +All of the above Fields marked with ${YOUR -...} needs to be replaced with your own parameters before starting. + +These options: +``` +server: + "endpoint":"${YOUR-HOST}:8801" --xenon machine ip + +raft: + "leader-start-command":"${YOUR-START-VIP-CMD}" --start vip + "leader-stop-command":"${YOUR-STOP-VIP-CMD}" --stop vip + +mysql: + "port":${YOUR-MYSQL-PORT} --xenon manages native mysql port. Default is 3306 + "basedir":"${YOUR-MYSQL-BIN-DIR}" --basedir in mysql profile path. + "defaults-file":"${YOUR-MYSQL-CNF-PATH}" --mysql profile path, xenon uses it to start mysql. + +replication: + "user":"${YOUR-MYSQL-REPL-USER}" --mysql replication user. It can be created automatically + "passwd":"${YOUR-MYSQL-REPL-PWD}" --mysql replication password. It can be created automatically + +backup: + "ssh-host":"%{YOUR-HOST}" --current intranet IP, for backup + "ssh-user":"${YOUR-SSH-USER}" --ssh user, for backup. When rebuildme, use it to get backups + "ssh-passwd":"${YOUR-SSH-PWD}" --ssh password, for backup. When rebuildme, use it to get backups + "basedir":"${YOUR-MYSQL-BIN-DIR}" --basedir in mysql profile path. + "backup-dir":"${YOUR-BACKUP-DIR}" --backupdir, it can same as mysql's datadir or others. + "xtrabackup-bindir":"${YOUR-XTRABACKUP-BIN-DIR}" --xtrabackup command path. +``` + +### Step3.3 Account Description + +Here need to be aware that the account running xenon must be consistent with the mysql account, such as the use of ubuntu account to start xenon, it requires ubuntu mysql boot and mysql directory permissions. + +This is not the same with the traditional mysql place, not in need of mysql account, run xenon account colleague is mysql account. + +**Note :** Following is a synopsis of command line samples. For simplicity, we assume `xenon` is in your path. If not, replace `xenon` with `/path/to/xenon`. + + + +## Step4 Start xenon + +``` +# mkdir /data/ + +# echo "/etc/xenon/xenon.json" > xenon/bin/config.path + +# ./xenon -c /etc/xenon/xenon.json > /data/xenon.log 2>&1 & + +# cat /data/xenon.log +``` + +**Note**: +``` +In the xenon command path, you need to have a file called config.path which is the absolute path to the xenon.json file. Be sure to specify the `xenon_config_file` location with `-c` or `--config`. +``` + +If the configuration is no problem, xenon will do after boot: +* Detect mysqld, if the process does not exist then start +* Waiting for the mysql can serve to detect the existence of duplicate accounts, or create + +Now xenon has started successfully, the final step is keepalived configuration. + +## Step5 Keepalived configuration and start + +Keepalived is a routing software written in C. The main goal of this project is to provide simple and robust facilities for loadbalancing and high-availability to Linux system and Linux based infrastructures. + +In the following steps, keepalive is installed by default. If not, you can refer to [Install](http://www.keepalived.org/doc/installing_keepalived.html) for configuration + +For learning more news, please see its [official website](http://www.keepalived.org/). + +**Note**: All of the operation is under root. + +### Step5.1 LVS + +LVS(Linux Virtual Server)is load balancing software for Linux kernel–based operating systems. + +A group of servers are connected to each other via a high-speed LAN(Local Area Network) or a geographically distributed wide area network. At their front end there is a Load Balancer which seamlessly dispatches network requests to real servers. + +Therefore, the structure of the server cluster is transparent to the user. The user accesses the network service provided by the cluster system just as if accessing a high performance and highly available server. + + +Here are some specific operations : +``` +$ sudo su - + +# vip=${{YOUR-VIP}} + +# /sbin/ifconfig lo down; + +# /sbin/ifconfig lo up; + +# echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore; + +# echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce; +# echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore; + +# echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce; + +# /sbin/ifconfig lo:0 ${vip} broadcast ${vip} netmask 255.255.255.255 up; + +# /sbin/route add -host ${vip} dev lo:0; + +# MySQL_port=${{YOU-MYSQL-PORT}} + +# M_MAC=${{YOU-MASTER-MAC}} + +# iptables -t mangle -I PREROUTING -d ${vip} -p tcp -m tcp --dport ${MySQL_port} -m mac ! --mac-source ${M_MAC} -j MARK --set-mark 0x1; + +# S1_MAC=${{YOU-SLAVE1-MAC}} +# iptables -t mangle -I PREROUTING -d ${vip} -p tcp -m tcp --dport ${MySQL_port} -m mac ! --mac-source ${S1_MAC} -j MARK --set-mark 0x1; + +# S2_MAC=${{YOU-SLAVE2-MAC}} + +# iptables -t mangle -I PREROUTING -d ${vip} -p tcp -m tcp --dport ${MySQL_port} -m mac ! --mac-source ${S2_MAC} -j MARK --set-mark 0x1; +``` +### Step5.2 Compile Keepalived.conf + +If you want to see a simple configuration, there is a [template](config/192.168.0.11_keepalived.md). If you want to know more, there are a lot of [keepalived configuration introduction](http://www.keepalived.org/doc/configuration_synopsis.html). + +### Step5.3 Start keepalived + +``` +# /etc/init.d/keepalived start +``` + +After done these, `ipvsadm -ln` can help us check the configure right or wrong. + + +## Step6 An easy example : Xenon starts with mysql + +**Note**: Following is a synopsis of command line samples. For simplicity, we assume `xenon` is in your path. If not, replace `xenon` with `/path/to/xenon`. And the operating system user is root. + +### Step6.1 Machine Condition + +First create three machines (the default version is Ubuntu16.04). They all have mysqld service + +| HostName | IP | Role | +| ------------------ | ------------ | ------ | +| i-lf9g3f5n(Master) | 192.168.0.11 | Master | +| i-0dc5giev(Slave1) | 192.168.0.2 | Slave | +| i-arb90jhc(Slave2) | 192.168.0.3 | Slave | + +### Step6.2 Mutual Trust + +Set up the trust of the three machines configured to reduce the possibility of bugs behind + +* On i-lf9g3f5n(M): + +``` +# vi /etc/hosts + add these at last: + 192.168.0.2 i-0dc5giev + 192.168.0.3 i-arb90jhc +# ssh-keygen +# ssh-copy-id ubuntu@i-0dc5giev +# ssh-copy-id ubuntu@i-arb90jhc +``` + +* On i-0dc5giev(S1): + +``` +# vi /etc/hosts + add these at last: + 192.168.0.3 i-arb90jhc + 192.168.0.11 i-lf9g3f5n + +# ssh-keygen +# ssh-copy-id ubuntu@i-arb90jhc +# ssh-copy-id ubuntu@i-lf9g3f5n +``` + +* On i-arb90jhc(S2): + +``` +# vi /etc/hosts + add these at last: + 192.168.0.2 i-0dc5giev + 192.168.0.11 i-lf9g3f5n + +# ssh-keygen +# ssh-copy-id ubuntu@i-0dc5giev +# ssh-copy-id ubuntu@i-lf9g3f5n +``` + +### Step6.3 Start Mysqld + +Start mysqld on each machine. + +If you want to get my configure, please click [my.cnf](config/MySQL.md) + +``` +# mysqld_safe --defaults-file=/etc/mysql/mysqld.conf.d/mysqld.conf & +``` + +### Step6.4 Start Xenon + +**Note :** Before starting xenon make sure the mysqld service is up and running + +Start xenon on each machine. The three nodes add the other two node `ip:port` to each other. + +If you want to get my configure, please click [192.168.0.11_xenon](config/192.168.0.11_xenon.md), [192.168.0.2_xenon](config/192.168.0.2_xenon.md) and [192.168.0.3_xenon](config/192.168.0.3_xenon.md). + +For more information on start xenon please refer to `Step3` and `Step4`. + +* On each node + +``` +# mkdir -p /etc/xenon/ + +# mkdir -p /data/raft + +# mkdir -p /data/mysql + +# mkdir -p /opt/xtrabackup/ + +# mkdir -p /data/log + +# touch /etc/xenon/xenon.json + +# ./xenon -c /etc/xenon/xenon.json > /data/log/xenon.log 2>&1 & +``` + +* On Master(192.168.0.11) + +``` +./xenoncli cluster add 192.168.0.2:3306,192.168.0.3:3306 +``` + +* On Slave1(192.168.0.2) + +``` +./xenoncli cluster add 192.168.0.11:3306,192.168.0.3:3306 +``` + +* On slave2 (192.168.0.3) + +``` +./xenoncli cluster add 192.168.0.11:3306,192.168.0.2:3306 +``` + +### Step6.5 Start Keepalived + +**Note :** I just configured the keepalived service on `Master` and `Slave1`. You can follow my configuration to operate, you can also follow your train of thought(for more detail about config and start Keepalived, refer to `Step5`). + +If you want to get my configure, please click [192.168.0.11_keepalived](config/192.168.0.11_keepalived.md) and [192.168.0.2_keepalived](config/192.168.0.2_keepalived.md). + +For more information on start xenon please refer to [Keepalived-Configuration](keepalived.md) + +* On each node + +``` +# /sbin/ifconfig lo down; + +# /sbin/ifconfig lo up; + +# echo 1 >/proc/sys/net/ipv4/conf/lo/arp_ignore; + +# echo 2 >/proc/sys/net/ipv4/conf/lo/arp_announce; + +# echo 1 >/proc/sys/net/ipv4/conf/all/arp_ignore; + +# echo 2 >/proc/sys/net/ipv4/conf/all/arp_announc; + +# /sbin/ifconfig lo:0 192.168.0.252 broadcast 192.168.0.252 netmask 255.255.255.255 up; + +# /sbin/route add -host 192.168.0.252 dev lo:0; +``` + +* On Master(192.168.0.11) + +``` +# iptables -t mangle -I PREROUTING -d 192.168.0.252 -p tcp -m tcp --dport 3306 -m mac ! --mac-source 52:54:dc:da:f0:cd -j MARK --set-mark 0x1 + +# iptables -t mangle -I PREROUTING -d 192.168.0.252 -p tcp -m tcp --dport 3306 -m mac ! --mac-source 52:54:dc:da:f0:cd -j MARK --set-mark 0x1 + +# ipvsadm --set 5 4 120 + +# /etc/init.d/keepalived start +``` + +* On Slave1(192.168.0.2) + +``` +# iptables -t mangle -I PREROUTING -d 192.168.0.252 -p tcp -m tcp --dport 3306 -m mac ! --mac-source 52:54:df:87:51:63 -j MARK --set-mark 0x1 + +# iptables -t mangle -I PREROUTING -d 192.168.0.252 -p tcp -m tcp --dport 3306 -m mac ! --mac-source 52:54:77:db:fc:ee -j MARK --set-mark 0x1 + +# ipvsadm --set 5 4 120 + +# /etc/init.d/keepalived start +``` + +* On Master(192.168.0.11) + +``` +# iptables -t mangle -I PREROUTING -d 192.168.0.252 -p tcp -m tcp --dport 3306 -m mac ! --mac-source 52:54:dc:da:f0:cd -j MARK --set-mark 0x1 + +# iptables -t mangle -I PREROUTING -d 192.168.0.252 -p tcp -m tcp --dport 3306 -m mac ! --mac-source 52:54:dc:da:f0:cd -j MARK --set-mark 0x1 + +# ipvsadm --set 5 4 120 + +# /etc/init.d/keepalived start +``` + +* On Slave1(192.168.0.2) + +``` +# iptables -t mangle -I PREROUTING -d 192.168.0.252 -p tcp -m tcp --dport 3306 -m mac ! --mac-source 52:54:df:87:51:63 -j MARK --set-mark 0x1 + +# iptables -t mangle -I PREROUTING -d 192.168.0.252 -p tcp -m tcp --dport 3306 -m mac ! --mac-source 52:54:77:db:fc:ee -j MARK --set-mark 0x1 + +# ipvsadm --set 5 4 120 + +# /etc/init.d/keepalived start +``` diff --git a/docs/how_xenon_works.md b/docs/how_xenon_works.md new file mode 100644 index 0000000..9ff2118 --- /dev/null +++ b/docs/how_xenon_works.md @@ -0,0 +1,459 @@ +[TOC] + +# internal mechanism on xenon + +## overview +MySQL is a very important RDS(Relational Database Service) in the field of cloud computing and has being widely used, but the operation and maintenance of MySQL are very complicated. In order to provide a better service, we developed Xenon. It helps MySQL Cluster be more availability and makes the strong consistency reach a new height. With highly automation and no human intervention, the O&M(Operation and Maintenance) are now easier and cost less. + +Xenon is a decentralized agent with no intrusive access to MySQL sources. A xenon manages a MySQL instance. It doesn't care about the deployment site as long as the network is reachable. + +It uses LVS + Raft + GTID parallel replication for master and data synchronization. More importantly, xenon rescues a number of operation and maintenance personnel. Now their greatest pleasure is in the production of casual master. + +`Xenon` is a MySQL replication topology HA, management and visualization tool, allowing for: + +**Discovery** + +`Xenon` actively crawls through your topologies and maps them. It reads basic MySQL info such as replication status and configuration. + +**Refactoring** + +`Xenon` understands replication rules. It knows about binlog file:position, GTID, Binlog Servers. + +Refactoring replication topologies can be a matter of drag & drop a replica under another master. Moving replicas around is safe: `xenon` will reject an illegal refactoring attempt. + +**Recovery** + +`Xenon` uses a holistic approach to detect master and intermediate master failures. Based on information gained from the topology itself, it recognizes a variety of failure scenarios. + +Optionally, it has the option to restore the node (which also allows the user to specify the recovery node). + +## 1 Xenon Raft+ + +The following describes the mechanism for xenon on raft. + +### 1.1 Highly Available + +In order to make the cluster highly available and the data reliable, we developed a new protocol based on the raft distributed coherency protocol : **`Raft+`** + +`Raft+`is the perfect combination of MySQL GTID parallel replication technology and `distributed conformance protocol raft`. + +If the cluster Master fault, `Raft+` will automatically second-level switch. It can ensure that zero data loss after switching and the cluster is still available. + +### 1.2 Raft+ Introduction + +In `Raft+`, we use the MySQL GTID (Global Transaction Identifier) <200b><200b>as the log index for the `Raft protocol` in conjunction with MySQL's Multi-Threaded Slave (MTS). It can complete the log entry parallel copy, parallel playback, log replay consumes an exceptionally short time, and the external service immediately after the failover. + +At the same time, `Raft+` uses Semi-Sync-Replication to ensure that at least one slave is completely synchronized with the master. After the master fails, the slave whose data is completely synchronized will be selected as the new Master. + +This ensures zero data loss and high availability. + +### 1.3 How Raft+ Works + +Set up a three-node cluster, one master and two slave. + +**The following is a gitd synchronization:** + +``` +{Master, [GTID:{1,2,3,4,5}] +{Slave1, [GTID:{1,2,3,4,5}] +{Slave2, [GTID:{1,2,3}] +``` + +* When the Master is not serviceable, Slave1 and Slave2 immediately start a new winner. + +* Xenon always ensure that the larger GTID has been synchronized to become a new master. Here is `Slave1`. + +* During the `VoteRequest` process, Slave1 directly rejects Slave2's `VoteRequest`, causing Slave2 to directly enter the next round of `VoteRequest` waiting for Slave1 to be elected. Therefore, the new Master data is fully synchronized with the old Master, thus ensuring zero data loss. + +* When Slave2 receives Heartbeat of Slave1. `CHANGE MASTER TO slave1` is automatically changed, and then data is copied according to GTID. + +**At this point, the cluster status changes to :** + +``` +{xxxooo, [GTID:{1,2,3,4,5}] +{Master, [GTID:{1,2,3,4,5}] +{Slave2, [GTID:{1,2,3,4,5}] +``` + +### 1.4 Raft+ Cluster Monitoring + +In order to monitor the cluster status of `Raft+`, we provide `xenoncli cluster` functionality. + +``` +$ xenoncli cluster status ++-------------+-------------------------------+---------+---------------------+----------------+ +| ID | Raft | Mysqld | Mysql | IO/SQL_RUNNING | ++-------------+-------------------------------+---------+---------------------+----------------+ +| 192.168.0.2 | [ViewID:2 EpochID:0]@LEADER | RUNNING | [ALIVE] [READWRITE] | [true/true] | +| | | | | | ++-------------+-------------------------------+---------+---------------------+----------------+ +| 192.168.0.3 | [ViewID:2 EpochID:0]@FOLLOWER | RUNNING | [ALIVE] [READONLY] | [true/true] | +| | | | | | ++-------------+-------------------------------+---------+---------------------+----------------+ +| 192.168.0.4 | [ViewID:2 EpochID:0]@FOLLOWER | RUNNING | [ALIVE] [READONLY] | [true/true] | +| | | | | | ++-------------+-------------------------------+---------+---------------------+----------------+ +``` + +#### 1.4.1 RAFT Status + +``` +type RaftStats struct { + // How many times the Pings called + Pings uint64 + + // How many times the HaEnables called + HaEnables uint64 + + // How many times the candidate promotes to a leader + LeaderPromotes uint64 + + // How many times the leader degrade to a follower + LeaderDegrades uint64 + + // How many times the leader got hb request from other leader + LeaderGetHeartbeatRequests uint64 + + // How many times the leader got vote request from others candidate + LeaderGetVoteRequests uint64 + + // How many times the leader got minority hb-ack + LessHearbeatAcks uint64 + + // How many times the follower promotes to a candidate + CandidatePromotes uint64 + + // How many times the candidate degrades to a follower + CandidateDegrades uint64 + + // How long of the state up + StateUptimes uint64 + + // The state of mysql: READONLY/WRITEREAD/DEAD + RaftMysqlStatus RAFTMYSQL_STATUS +} +``` + +#### 1.4.2 MySQL Status + +``` +type GTID struct { + // Mysql master log file which the slave is reading + Master_Log_File string + + // Mysql master log postion which the slave has read + Read_Master_Log_Pos uint64 + + // Slave IO thread state + Slave_IO_Running bool + + // Slave SQL thread state + Slave_SQL_Running bool + + // The GTID sets which the slave has received + Retrieved_GTID_Set string + + // The GTID sets which the slave has executed + Executed_GTID_Set string + + // Seconds_Behind_Master in 'show slave status' + Seconds_Behind_Master string + + // Slave_SQL_Running_State in 'show slave status' + // The value is identical to the State value of the SQL thread as displayed by SHOW PROCESSLIST + Slave_SQL_Running_State string + + //The Last_Error suggests that there may be more failures + //in the other worker threads which can be seen in the replication_applier_status_by_worker table + //that shows each worker thread's status + Last_Error string +} +``` + +#### 1.4.3 MySQLD Status + +``` +type MysqldStats struct { + // How many times the mysqld have been started by xenon + MysqldStarts uint64 + + // How many times the mysqld have been stopped by xenon + MysqldStops uint64 + + // How many times the monitor have been started by xenon + MonitorStarts uint64 + + // How many times the monitor have been stopped by xenon + MonitorStops uint64 +} +``` + +#### 1.4.4 Backup Status + +``` +type BackupStats struct { + // How many times backup have been called + Backups uint64 + + // How many times backup have failed + BackupErrs uint64 + + // How many times apply-log have been called + AppLogs uint64 + + // How many times apply-log have failed + AppLogErrs uint64 + + // How many times cannel have been taken + Cancels uint64 + + // The last error message of backup/applylog + LastError string + + // The last backup command info we call + LastCMD string +} +``` + +#### 1.4.5 Config Status + +``` +type ConfigStatus struct { + // log + LogLevel string + + // backup + BackupDir string + BackupIOPSLimits int + XtrabackupBinDir string + + // mysqld + MysqldBaseDir string + MysqldDefaultsFile string + + // mysql + MysqlAdmin string + MysqlHost string + MysqlPort int + MysqlReplUser string + MysqlPingTimeout int + + // raft + RaftDataDir string + RaftHeartbeatTimeout int + RaftElectionTimeout int + RaftRPCRequestTimeout int + RaftProtectionMode string + RaftStartVipCommand string + RaftStopVipCommand string +} +``` + +### 1.5 Raft+ Readonly Status + +In addition to `Leader`/`Candidate`/`Follower` three states outside raft + also provides `Idle` state: + +* **Idle state :** Don't participate in election Lord but will perceive Leader changes to change the replication channel. The `Idle` state is suitable for being deployed as a disaster recovery instance in a remote computer room. + +Through the `Idle` settings, different xenon nodes can be reassembled to provide services, which we call `Semi-Raft Group`. + +For example, a computer room A has 3 nodes, forming a `Semi-Raft Group`. The states are: + +``` +[A1:Leader, A2:Follower, A3: Follower] +``` + +Room B has 3 disaster recovery nodes(Semi-Raft Group): + +``` +[B1:Idle, B2:Idle, B3:Idle] +``` + +If room A is powered off and resumes for a long period of time, we can set up three instances of room B from Idle to Follower. + +In this way, Semi-Raft Group of the room B initiates selection of external services to hosts. Combined with `BinlogServer`, A's data exactly the same. + + +## 2 High Availability + +### 2.1 Ways to be HA + +HA is achieved by choosing either: + +* xenon/keepalived setup, where xenon switch VIP for service. + +* xenon/raft setup, where xenon nodes communicate by raft consensus. Each xenon node has a private database backend. + +### 2.2 HA via Keepalived + +HA is achieved by highly available keepalived. Keepalived is a Web service based on VRRP(Virtual Router Redundancy Protocol) agreement to achieve high availability program. + +Keepalived can be used to avoid single points of failure. A WEB service will have at least 2 servers running Keepalived. The one is master server (MASTER), the other is backup server (BACKUP). But the external appearance of a VIP(Virtual IP). The MASTER SERVER sends a specific message to BACKUP SERVER. +When the BACKUP SERVER does not receive this message means that the MAIN SERVER downtime. The BACKUP SERVER takes over the VIP and continues to provide the service. Thus ensuring high availability. + +### 2.3 HA via Raft+ +Xenon nodes will directly communicate via `Raft+` consensus algorithm. Each xenon node has its own private backend MySQL. + +Only one xenon node assumes leadership, and is always a part of a consensus. However all other nodes are independently active and are polling your topologies. + +It is recommended to run a 3-node setup. If there is only two nodes, the replication between the databases is asynchronous + +To access your MySQL service you may only speak to the RVIP/WVIP. + +* Use xenon/bin/xenoncli check for your proxy. + +## 3 Retake Slave + +OLTP high concurrency allows us to choose the master-slave replication architecture. However, in many cases of life, we find it is very troublesome to find that a slave library often causes a copy thread to be false for various reasons, or to add a slave node again. For a variety of reasons, xenon provides the rebuild slave function, which requires just a simple command from the library to solve the problem of copying from the library for quick use. + +### 3.1 Analysis Process + +* Xenon provides streaming backup, directly through the ssh hit the mysql data directory on the end machine, without any additional space, you can quickly complete the standby library re-take. + +* Assuming Slave1 is broken, you need to prepare the library to take a ride: + +``` + Master(A) + / \ +Slave1(B) Slave2(C) +``` + +The following is a simple operation process: + +1. B-xenon select the best backup source(mysql synchronized master data most), the assumption is C-xenon + +2. B-xenon kills B-mysql and empties its data directory + +3. B-xenon initiates a hotbackup request to C-xenon. Transfer B-xenon own ssh-user/ssh-passwd/iops at the same time + +4. C-xenon begins to back up and stream data to data directory under B-mysql which is managed by B-xenon. + +5. B-xenon received a backup of C-xenon. Completed + +6. B-xenon starts to apply log + +7. B-xenon starts the MySQL service + +8. Change the master-slave relationship. Master is current node. + +9. Start replicating. + +10. Re-take slave successed. + +### 3.2 Actual Operation + +In actual production, Master-Slave replication problem may be the most common. + +When a copy problem occurs and the problem is clear, we use `xenoncli mysql rebuildme` for fast rebuild. + +* The following is a complete rebuildme log: + +```plain + $ xenoncli mysql rebuildme + + 2017/10/17 10:59:02.391964 mysql.go:177: [WARNING] =====prepare.to.rebuildme===== + IMPORTANT: Please check that the backup run completes successfully. + At the end of a successful backup run innobackupex + prints "completed OK!". + + 2017/10/17 10:59:02.392296 mysql.go:187: [WARNING] S1-->check.raft.leader + 2017/10/17 10:59:02.399614 callx.go:140: [WARNING] rebuildme.found.best.slave[192.168.0.4:8801].leader[192.168.0.2:8801] + 2017/10/17 10:59:02.399633 mysql.go:203: [WARNING] S2-->prepare.rebuild.from[192.168.0.4:8801].... + 2017/10/17 10:59:02.400324 mysql.go:214: [WARNING] S3-->check.bestone[192.168.0.4:8801].is.OK.... + 2017/10/17 10:59:02.400336 mysql.go:219: [WARNING] S4-->disable.raft + 2017/10/17 10:59:02.400869 mysql.go:227: [WARNING] S5-->stop.monitor + 2017/10/17 10:59:02.402494 mysql.go:233: [WARNING] S6-->kill.mysql + 2017/10/17 10:59:02.443844 mysql.go:250: [WARNING] S7-->check.bestone[192.168.0.4:8801].is.OK.... + 2017/10/17 10:59:03.494280 mysql.go:264: [WARNING] S8-->rm.datadir[/home/mysql/data3306/] + 2017/10/17 10:59:03.494321 mysql.go:269: [WARNING] S9-->xtrabackup.begin.... + 2017/10/17 10:59:03.494837 callx.go:386: [WARNING] rebuildme.backup.from[192.168.0.4:8801] + 2017/10/17 10:59:21.375151 mysql.go:273: [WARNING] S9-->xtrabackup.end.... + 2017/10/17 10:59:21.375184 mysql.go:278: [WARNING] S10-->apply-log.begin.... + 2017/10/17 10:59:22.781295 mysql.go:281: [WARNING] S10-->apply-log.end.... + 2017/10/17 10:59:22.781575 mysql.go:286: [WARNING] S11-->start.mysql.begin... + 2017/10/17 10:59:22.782444 mysql.go:290: [WARNING] S11-->start.mysql.end... + 2017/10/17 10:59:22.782459 mysql.go:295: [WARNING] S12-->wait.mysqld.running.begin.... + 2017/10/17 10:59:25.795803 callx.go:349: [WARNING] wait.mysqld.running... + 2017/10/17 10:59:25.810427 mysql.go:297: [WARNING] S12-->wait.mysqld.running.end.... + 2017/10/17 10:59:25.810470 mysql.go:302: [WARNING] S13-->wait.mysql.working.begin.... + 2017/10/17 10:59:28.811584 callx.go:583: [WARNING] wait.mysql.working... + 2017/10/17 10:59:28.812049 mysql.go:304: [WARNING] S13-->wait.mysql.working.end.... + 2017/10/17 10:59:28.812219 mysql.go:309: [WARNING] S14-->reset.slave.begin.... + 2017/10/17 10:59:28.816761 mysql.go:313: [WARNING] S14-->reset.slave.end.... + 2017/10/17 10:59:28.816797 mysql.go:319: [WARNING] S15-->reset.master.begin.... + 2017/10/17 10:59:28.822253 mysql.go:321: [WARNING] S15-->reset.master.end.... + 2017/10/17 10:59:28.822322 mysql.go:326: [WARNING] S15-->set.gtid_purged[194758cd-b21c-11e7-80b7-5254281e57de:1-9245708].begin.... + 2017/10/17 10:59:28.824089 mysql.go:330: [WARNING] S15-->set.gtid_purged.end.... + 2017/10/17 10:59:28.824112 mysql.go:340: [WARNING] S16-->enable.raft.begin... + 2017/10/17 10:59:28.824680 mysql.go:344: [WARNING] S16-->enable.raft.done... + 2017/10/17 10:59:28.824717 mysql.go:350: [WARNING] S17-->wait[4000 ms].change.to.master... + 2017/10/17 10:59:28.824746 mysql.go:356: [WARNING] S18-->start.slave.begin.... + 2017/10/17 10:59:29.058472 mysql.go:360: [WARNING] S18-->start.slave.end.... + 2017/10/17 10:59:29.058555 mysql.go:364: [WARNING] completed OK! + 2017/10/17 10:59:29.058571 mysql.go:365: [WARNING] rebuildme.all.done.... +``` + +If the problem is not clear and needs to be analyzed in depth, let's delete the node by adding more nodes to ensure that the majority can service. This is very flexible. + +**Note :** +``` +1. Before rebuild, make sure the main library is alive. + Quickly add a new node is also done through the `rebuildme` function. + +2. If there is an error, you need to log according to the prompts to analyze. + The main analysis is to reconstruct the node log and backup node log. +``` + +## 4 Faliover + +## 4.1 Select the main conditions + +xenon master election using the raft protocol, the election basis conditions: + * Master_Log_File + * Read_Master_Log_Pos + * Slave_SQL_Running + +Which slave get the binlog up and no copy error, it is the new master candidate. + +## 4.2 Select the main process + +Suppose we cluster deployment mode 1 main 2 backup (respectively in 3 containers): + +``` + Master(A) + / \ +Slave1(B) Slave2(C) +``` + +* A-xenon (admin A's xenon) periodically sends heartbeats to other B / C-xenons, reports on the health of A-mysql, and maintains master-slave relationships. + +* When A-mysql is unavailable (maybe mysql hangs, even the container hangs up), B / C-xenon triggers a new master election if it does not receive A-xenon heartbeat within a certain period of time (configurable, default 3s). + +* **Suppose C-xenon first initiated the main election, the normal process is as follows:** + +``` +1. At the same time send vote-request for A and B. + +2. Mostly(favor-num > n/2+1) in favor and no objection.(If there is a negative vote, it means that C-mysql has less data than the opponent) + +3. Promoted to master + +4. Call the vip start +``` + +At this point A-xenon receives the heartbeat of C-xenon, you need to do the following: + +``` +1. Change the relationship between master and slave(if mysql is available). Start copying data from C-mysql sync + +2. Call the vip stop +``` + +At this point B-xenon receives the heartbeat of C-xenon, you need to do the following: + +``` +1. Change the relationship between master and slave. Start copying data from C-mysql sync +``` + +**The whole election process is very short, usually `3-6 seconds` to complete.** + diff --git a/docs/images/xenon.png b/docs/images/xenon.png new file mode 100644 index 0000000000000000000000000000000000000000..a0a3633dbc67714d2b76884baea41259ed8fcd4d GIT binary patch literal 39196 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 m|5f|f#J_6)8b~lX;W9ZF$@^qYQXM!RkebRp