├── LICENSE ├── README.md ├── files ├── mysql-ha-cluster.png └── proxysql.repo ├── hosts.ini ├── roles ├── mha_manager │ ├── files │ │ ├── mha_manager.tgz │ │ ├── report_script.sh │ │ └── shutdown_mysql.sh │ ├── tasks │ │ └── main.yml │ └── templates │ │ ├── masterha_default.cnf.j2 │ │ ├── template.tgz │ │ └── template │ │ ├── auto_install.sh │ │ ├── check_status.sh │ │ ├── mha_project.cnf.j2 │ │ ├── mha_project.log │ │ ├── proxysql_failover.sh │ │ ├── start.sh │ │ ├── stop.sh │ │ └── switch_online.sh ├── mha_node │ ├── files │ │ ├── mha_node.tgz │ │ └── mha_node │ │ │ ├── ExtUtils-Constant-0.25.tar.gz │ │ │ ├── README │ │ │ ├── Socket-2.027.tar.gz │ │ │ ├── install_node.sh │ │ │ ├── perl-DBD-MySQL-4.013-3.el6.x86_64.rpm │ │ │ └── v0.58_mha_node.tar.gz │ └── tasks │ │ └── main.yml ├── mysql │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ ├── my.cnf.j2 │ │ └── root_my_cnf.j2 └── proxysql │ ├── handlers │ └── main.yml │ ├── tasks │ └── main.yml │ └── templates │ ├── init_proxysql.sh.j2 │ └── proxysql.j2 └── start_deploy.yml /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kaiyuan Finance 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MySQL HA Cluster 2 | 3 | ## 部署架构 4 | 最终部署架构如下: 5 | ![HA-img](https://github.com/kaiyuan-finance/MySQL-HA-Cluster/raw/dev/files/mysql-ha-cluster.png) 6 | 7 | ## 部署步骤 8 | ### 1. 编辑文件hosts.ini 9 | hosts.ini文件包含了所有的主机信息,是标准ansible inventory文件 10 | 11 | [mysql_servers:vars]中定义了 12 | mysql的数据目录(datadir) 13 | 日志目录(logdir) 14 | 临时文件目录(tmpdir) 15 | 已经所安装的mysql版本(mysql_version,目前支持mysql5.7的各版本安装,若安装mysql8,则需要编辑mysql角色下的相关task) 16 | 17 | 18 | 19 | [proxysql_servers:vars] 下面定义了 20 | proxysql的安装目录(proxysql_datadir) 21 | proxysql的日志目录(proxysql_logdir) 22 | proxysql客户端端口(proxysql_client_port) 23 | proxysql中的sql_mode(mysql_sql_mode) 24 | proxysql监控账号及密码(proxysql_monitor_user、proxysql_monitor_password) 25 | proxysql的版本(proxysql_version) 26 | 27 | [mha_manager_servers:vars] 变量组定义了 28 | MHA manager的目录(mhadir),该目录中会有所有的项目,每个项目以一个文件夹的形式配置 29 | MHA监控mysql server的账号及密码(mysql_mha_user、mysql_mha_password) 30 | SSH账号(ssh_user), **注意:各MHA node节点的SSH 公钥互信需要手工配置** 31 | 32 | 在roles/mysql/defaults/main.yml中定义了几个默认变量,你也可以修改: 33 | ``` 34 | mysql_root_password: pass4.SQL 35 | mysqld_error_log: "{{ datadir }}/mysqld.err" 36 | mysql_repl_user: repl 37 | mysql_repl_password: repl 38 | ``` 39 | 40 | ### 2. 部署 41 | 2.1 部署整个集群 42 | 部署整个集群可以运行一下代码,若想只部署某个部分,见2.2以后 43 | ``` 44 | ansible-playbook -i hosts.ini start_deploy.yml 45 | ``` 46 | 2.2 只部署ProxySQL 47 | ``` 48 | ansible-playbook -i hosts.ini -t install-proxysql start_deploy.yml 49 | ``` 50 | 2.3 只部署MySQL 51 | ``` 52 | ansible-playbook -i hosts.ini -t install-mysql start_deploy.yml 53 | ``` 54 | 2.4 部署MHA node 55 | 所有MySQL节点,以及MHA Manager都需要部署,此步骤应在安装MHA manager之前执行 56 | ``` 57 | ansible-playbook -i hosts.ini -t install-mha-node start_deploy.yml 58 | ``` 59 | 2.5 部署MHA Manager 60 | ``` 61 | ansible-playbook -i hosts.ini -t install-mha-manager start_deploy.yml 62 | ``` 63 | -------------------------------------------------------------------------------- /files/mysql-ha-cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiyuan-finance/MySQL-HA-Cluster/3e77a36763f75a9a4cb308fd9cbfc4ce21f42b0a/files/mysql-ha-cluster.png -------------------------------------------------------------------------------- /files/proxysql.repo: -------------------------------------------------------------------------------- 1 | [proxysql_repo] 2 | name= ProxySQL YUM repository 3 | baseurl=https://repo.proxysql.com/ProxySQL/proxysql-1.4.x/centos/\$releasever 4 | gpgcheck=1 5 | gpgkey=https://repo.proxysql.com/ProxySQL/repo_pub_key 6 | -------------------------------------------------------------------------------- /hosts.ini: -------------------------------------------------------------------------------- 1 | # MySQL servers hosts 2 | [mysql_servers] 3 | 192.168.1.50 ansible_ssh_user="root" ansible_ssh_pass="root" mysql_role=master server_id=1 4 | 192.168.1.51 ansible_ssh_user="root" ansible_ssh_pass="root" mysql_role=slave server_id=2 5 | 192.168.1.52 ansible_ssh_user="root" ansible_ssh_pass="root" mysql_role=slave server_id=3 6 | 7 | [mysql_servers:vars] 8 | master_ip=192.168.1.50 9 | datadir=/dbfiles/mysql_home/data 10 | logdir=/dbfiles/mysql_home/logs 11 | tmpdir=/dbfiles/mysql_home/tmp 12 | mysql_version=5.7.22 13 | 14 | #ProxySQL hosts 15 | [proxysql_servers] 16 | 192.168.1.52 17 | 18 | [proxysql_servers:vars] 19 | proxysql_datadir=/dbfiles/proxysql/data 20 | proxysql_logdir=/dbfiles/proxysql/logs 21 | proxysql_client_port=3306 22 | mysql_sql_mode='NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER' 23 | proxysql_monitor_user='monitor' 24 | proxysql_monitor_password='monitor' 25 | proxysql_version=1.4.14 26 | 27 | #mha manager hosts 28 | [mha_manager_servers] 29 | 192.168.1.52 30 | 31 | [mha_manager_servers:vars] 32 | mhadir=/dbfiles/mha_manager 33 | mysql_mha_user=mha 34 | mysql_mha_password=mha 35 | ssh_user=mha 36 | 37 | [mha_node_servers:children] 38 | mysql_servers 39 | mha_manager_servers 40 | -------------------------------------------------------------------------------- /roles/mha_manager/files/mha_manager.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiyuan-finance/MySQL-HA-Cluster/3e77a36763f75a9a4cb308fd9cbfc4ce21f42b0a/roles/mha_manager/files/mha_manager.tgz -------------------------------------------------------------------------------- /roles/mha_manager/files/report_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Call noah inerface to send messages to dbas 4 | 5 | all=$* 6 | arr=($all) 7 | 8 | #parse the arguments and get the corresponding value 9 | function argValue(){ 10 | para=$1 11 | for a in ${arr[@]} 12 | do 13 | c=`echo "$a" | grep "$para" | wc -l` 14 | if [ $c -eq 1 ];then 15 | echo $a | awk -F '=' '{print $2}' 16 | break; 17 | fi 18 | done 19 | } 20 | 21 | subject=`argValue "subject"` 22 | orig_master=`argValue "orig_master_host"` 23 | new_master=`argValue "new_master_host"` 24 | new_slave_hosts=`argValue "new_slave_hosts"` 25 | body=`argValue "body"` 26 | body="Project-$subject MHA Master Failover!\r\n 新Master:$new_master\r\n 故障Master:$orig_master" 27 | 28 | # send message alert to people relative 29 | -------------------------------------------------------------------------------- /roles/mha_manager/files/shutdown_mysql.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Try to stop all the mysqld processes with -9 , including mysqld and mysqld_safe. 4 | # As it is possible that mysqld process is in progress of restart by mysqld_safe when mha manager detect that master is not reachable. 5 | # /dbfiles/mha_manager/scripts/shutdown_mysql.sh --command=stopssh --ssh_user=root --host=192.168.212.51 --ip=192.168.212.51 --port=3306 6 | # The script will be called twice: 7 | # 1. when start monitoring 8 | # --command=status 9 | # --host=(maser's hostname) 10 | # --ip=(master's ip address) 11 | # 2. during failvoer 12 | # --command=stopssh 13 | # --ssh_user=(ssh username so that you can connect to the master) 14 | # --host=(master's hostname) 15 | # --ip=(master's ip address) 16 | # --port=(master's port number) 17 | # --pid_file=(master's pid file) 18 | 19 | #parse the arguments and get the corresponding value 20 | 21 | all=$* 22 | 23 | arr=($all) 24 | function argValue(){ 25 | para=$1 26 | for a in ${arr[@]} 27 | do 28 | c=`echo "$a" | grep "$para" | wc -l` 29 | if [ $c -eq 1 ];then 30 | echo $a | awk -F '=' '{print $2}' 31 | break; 32 | fi 33 | done 34 | } 35 | 36 | com=`argValue "command"` 37 | masterIP=`argValue "ip"` 38 | sshUser=`argValue "ssh_user"` 39 | echo "command is :$com" 40 | 41 | if [ x"$com" = x"stopssh" ]; then 42 | # kills all mysqld and mysqld_safe processes 43 | ssh -o ConnectTimeout=1 ${sshUser}@${masterIP} "if [ `pgrep mysqld` ];then pgrep mysqld | xargs sudo kill -9 ; fi" 44 | echo " ***** Killed mysqld mysqld_safe *****" 45 | echo "" 46 | exit 10 47 | fi 48 | 49 | # If we reach here, ssh is not reachable 50 | # We can do nothing to power off the VM 51 | if [ x"$com" = x"stop" ];then 52 | : 53 | fi 54 | -------------------------------------------------------------------------------- /roles/mha_manager/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: add mha user 3 | user: 4 | name: mha 5 | group: mysql 6 | 7 | - name: create mha manager diretory 8 | file: 9 | path: '{{ item.dir }}' 10 | state: directory 11 | owner: mha 12 | group: mysql 13 | mode: 0755 14 | with_items: 15 | - { dir: "{{ mhadir }}" } 16 | - { dir: "{{ mhadir }}/scripts" } 17 | - { dir: "{{ mhadir }}/template" } 18 | 19 | - name: copy mha manager package to remote 20 | copy: 21 | src: mha_manager.tgz 22 | dest: /tmp/ 23 | force: yes 24 | 25 | - name: tar xzf mha_manager.tgz 26 | shell: tar xzf mha_manager.tgz 27 | args: 28 | chdir: /tmp/ 29 | 30 | - name: install mha_manager 31 | shell: /bin/bash install_mha_manager.sh >/tmp/install_mha_manager.log 2>&1 32 | args: 33 | chdir: /tmp/mha_manager 34 | 35 | - name: copy script files 36 | copy: 37 | src: '{{ item.file }}' 38 | dest: '{{ mhadir }}/scripts/' 39 | owner: mha 40 | group: mysql 41 | mode: 0755 42 | with_items: 43 | - { file: "report_script.sh" } 44 | - { file: "shutdown_mysql.sh" } 45 | 46 | - name: copy template files 47 | template: 48 | src: '{{ item.file }}' 49 | dest: '{{ item.dest }}' 50 | mode: '{{ item.mode }}' 51 | owner: mha 52 | group: mysql 53 | force: yes 54 | with_items: 55 | - { 'file': 'template/mha_project.cnf.j2', 'dest': '{{ mhadir }}/template/mha_project.cnf','mode':'0644' } 56 | - { 'file': 'template/auto_install.sh', 'dest': '{{ mhadir }}/template/auto_install.sh','mode':'0644' } 57 | - { 'file': 'template/check_status.sh', 'dest': '{{ mhadir }}/template/check_status.sh', 'mode':'0755' } 58 | - { 'file': 'template/mha_project.log', 'dest': '{{ mhadir }}/template/mha_project.log','mode':'0644' } 59 | - { 'file': 'template/proxysql_failover.sh', 'dest': '{{ mhadir }}/template/proxysql_failover.sh', 'mode':'0755' } 60 | - { 'file': 'template/start.sh', 'dest': '{{ mhadir }}/template/start.sh' , 'mode':'0755'} 61 | - { 'file': 'template/stop.sh', 'dest': '{{ mhadir }}/template/stop.sh' , 'mode':'0755'} 62 | - { 'file': 'template/switch_online.sh', 'dest': '{{ mhadir }}/template/switch_online.sh' ,'mode':'0644'} 63 | - { 'file': 'masterha_default.cnf.j2', 'dest': '/etc/masterha_default.cnf' ,'mode':'0644'} 64 | -------------------------------------------------------------------------------- /roles/mha_manager/templates/masterha_default.cnf.j2: -------------------------------------------------------------------------------- 1 | [server default] 2 | user={{ mysql_mha_user }} 3 | password={{ mysql_mha_password }} 4 | ssh_user={{ ssh_user }} 5 | 6 | shutdown_script={{ mhadir }}/scripts/shutdown_mysql.sh 7 | report_script={{ mhadir }}/scripts/report_script.sh 8 | 9 | ping_interval=3 10 | -------------------------------------------------------------------------------- /roles/mha_manager/templates/template.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiyuan-finance/MySQL-HA-Cluster/3e77a36763f75a9a4cb308fd9cbfc4ce21f42b0a/roles/mha_manager/templates/template.tgz -------------------------------------------------------------------------------- /roles/mha_manager/templates/template/auto_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -ne 1 ];then 4 | echo "Please input the project name." 5 | exit 6 | fi 7 | 8 | project=$1 9 | 10 | #cur_dir=$(dirname $(readlink -f "$0")) 11 | 12 | echo "Correct the cnf/log file name..." 13 | mv mha_project.cnf mha_${project}.cnf 14 | mv mha_project.log mha_${project}.log 15 | echo "done" 16 | echo "" 17 | 18 | echo "Correct the parent folder" 19 | #mv ../template ../mha_$project 20 | echo "done" 21 | 22 | echo "Correct the content of cnf " 23 | 24 | sed -i "s#PROJECT#$project#g" mha_${project}.cnf 25 | 26 | echo "done" 27 | 28 | echo "You still need to change the server host part in mha_${project}.cnf" 29 | 30 | cd .. 31 | cd mha_$project 32 | ls -l 33 | -------------------------------------------------------------------------------- /roles/mha_manager/templates/template/check_status.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cur_dir=$(dirname $(readlink -f "$0")) 3 | 4 | #conf=$cur_dir/mha_test.cnf 5 | conf=`find $cur_dir -name "*.cnf" | head -n 1` 6 | /usr/local/bin/masterha_check_status --conf=$conf 7 | -------------------------------------------------------------------------------- /roles/mha_manager/templates/template/mha_project.cnf.j2: -------------------------------------------------------------------------------- 1 | # replace the PROJECT in this file with you real project name 2 | [server default] 3 | 4 | manager_workdir={{ mhadir }}/mha_PROJECT 5 | manager_log={{ mhadir }}/mha_PROJECT/mha_PROJECT.log 6 | 7 | secondary_check_script= masterha_secondary_check -s 192.0.16.12 8 | master_ip_failover_script={{ mhadir }}/mha_PROJECT/proxysql_failover.sh 9 | master_ip_online_change_script={{ mhadir }}/mha_PROJECT/proxysql_failover.sh 10 | 11 | #Make sure the replication account is correct 12 | repl_user=repl 13 | repl_password=repl444 14 | 15 | [server1] 16 | hostname=11.0.3.0 17 | candidate_master=1 18 | 19 | [server2] 20 | hostname=11.0.3.0 21 | no_master=1 22 | ignore_fail=1 23 | 24 | [server3] 25 | hostname=11.0.3.0 26 | candidate_master=1 27 | -------------------------------------------------------------------------------- /roles/mha_manager/templates/template/mha_project.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiyuan-finance/MySQL-HA-Cluster/3e77a36763f75a9a4cb308fd9cbfc4ce21f42b0a/roles/mha_manager/templates/template/mha_project.log -------------------------------------------------------------------------------- /roles/mha_manager/templates/template/proxysql_failover.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # When master failover is needed, this script update the mysql_servers table in ProxySQL and activates the new master. 4 | # MHA manager calls master_ip_failover script three times during failover 5 | # 6 | # - Checking phase 7 | # --command=status 8 | # - Current master shutdown phase 9 | # --command=stop or stopssh 10 | # - New master activation phase 11 | # --command=start 12 | # 13 | # In our use case, we only care about the third phase. The first and the second can be ignored 14 | # And only the argument --command is useful. All the following arguments can be ignored. 15 | # This script(proxy_failover.sh)can not be shared among different projects. 16 | # You need to change the following variables according to the configuration in ProxySQL 17 | # Once a failover happens, the following variables need to be changed before start MHA manager again. 18 | 19 | # the master host group id configured in ProxySQL 20 | # ** THIS IS VERY IMPORTANT! ** 21 | # ** IF THIS IS NOT RIGHT, DANGEROUS CONSEQUENCES WILL BE CAUSED ** 22 | master_hg=0 23 | # the current master IP configured in ProxySQL 24 | #master_ip=192.168.212.50 25 | # the backup master IP 26 | #backup_master_ip=192.168.212.51 27 | 28 | # The proxysql hosts ,this is an array. 29 | # As it's possible to have more than one proxysql instance 30 | # If you have more proxysqls,just add the ip to the array 31 | # eg: proxysql_hosts=("127.0.0.1" "127.0.0.1") 32 | proxysql_hosts=("172.0.1.0" "172.0.1.0") 33 | 34 | #credentials to login proxysql's admin interface 35 | 36 | admin_user=dba 37 | admin_password=dbacom 38 | 39 | 40 | args=( "--command" "--ssh_user" "--orig_master_host" "--orig_master_ip" "--orig_master_port" "--new_master_host" "--new_master_ip" "--new_master_port" "--new_master_user" "new_master_password") 41 | 42 | all=$* 43 | 44 | arr=($all) 45 | 46 | 47 | # check arguments but there's no need 48 | #for a in ${arr[@]} 49 | #do 50 | 51 | #tmp=`echo $a | awk -F '=' '{print $1}'` 52 | #if [[ ! "${args[@]}" =~ $tmp ]]; then 53 | # echo " Unkown parameters: $a" 54 | # usage 55 | # exit 1 56 | #fi 57 | #done 58 | 59 | #parse the arguments and get the corresponding value 60 | function argValue(){ 61 | para=$1 62 | for a in ${arr[@]} 63 | do 64 | c=`echo "$a" | grep "$para" | wc -l` 65 | if [ $c -eq 1 ];then 66 | echo $a | awk -F '=' '{print $2}' 67 | break; 68 | fi 69 | done 70 | } 71 | 72 | com=`argValue "command"` 73 | 74 | # we are interested in this parameter 75 | new_master_ip=`argValue "new_master_ip"` 76 | 77 | backup_master_ip=$new_master_ip 78 | # we can fetch the current master ip from proxysql 79 | #master_ip=`mysql -u$admin_user -p$admin_password -h${proxysql_hosts[0]} -P6032 -NB -e "select hostname from runtime_mysql_servers where hostgroup_id=$master_hg" 2>/dev/null` 80 | master_ip=`argValue "orig_master_ip"` 81 | # Login to the old master 82 | user=`argValue "orig_master_user"` 83 | pass=`argValue "orig_master_password"` 84 | orig_master_port=`argValue "orig_master_port"` 85 | 86 | 87 | if [ ! "$master_ip" ];then 88 | echo "master_ip is empty. Exit!" 89 | exit 1 # FIXME: How MHA manager will handle this exit code ? MHA manager fails to start 90 | fi 91 | 92 | 93 | function proxysql_failover(){ 94 | # this is necessary 95 | proxysql_host=$1 96 | 97 | # do the activation of the new master. 98 | # Since we're here, the original master must be definitely dead. 99 | echo "" 100 | echo "*****Start failover" 101 | 102 | ( 103 | echo "update mysql_servers set status='ONLINE',hostname='$backup_master_ip' where hostgroup_id=$master_hg and hostname='$master_ip';" 104 | echo "LOAD MYSQL SERVERS TO RUNTIME;" 105 | echo "SAVE MYSQL SERVERS TO DISK;" 106 | ) | mysql -u$admin_user -p$admin_password -h$proxysql_host -P6032 107 | echo "*****Done!" 108 | echo "" 109 | 110 | } 111 | 112 | # Online switch over. to make the new master offline gracefully 113 | function proxysql_offline_maser(){ 114 | # this is necessary 115 | proxysql_host=$1 116 | # The original master is forbiden to write. as MHA manager executes `FLUSH TABLES WITH READ LOCK` on it. 117 | # offline the original master 118 | echo "" 119 | echo "*****Off line the original master gracefully!" 120 | ( 121 | echo "update mysql_servers set status='OFFLINE_SOFT' where hostgroup_id=$master_hg and hostname='$master_ip';" 122 | echo "LOAD MYSQL SERVERS TO RUNTIME;" 123 | )| mysql -u$admin_user -p$admin_password -h$proxysql_host -P6032 124 | echo "*****Done!" 125 | echo "" 126 | # Wait for the running transactions to finish 127 | # Fixme: sleep 1s could be a problem 128 | sleep 1 129 | } 130 | 131 | 132 | #if [ x"$com" = x"status" ];then 133 | # echo "command is $com" 134 | # exit 135 | #fi 136 | 137 | # Deactive the original master. If the master is down, it will be shunned by proxysql. 138 | # We MUST take the original master offline in proxysql, this is to be safe 139 | if [ x"$com" = x"stop" ] || [ x"$com" = x"stopssh" ];then 140 | # Offline master on all the ProxySQLs 141 | for p in ${proxysql_hosts[@]} 142 | do 143 | proxysql_offline_maser $p 144 | done 145 | #After the current master is offline, it's necessary to enable 'super_read_only' on it. 146 | # As MHA manager only turned 'read_only' on. 147 | # This step maybe fail because the master has already down. But Just in case, 148 | # the master might has a chance to start when it is the case that the os restarted 149 | echo "Enable super_read_only on the old master, this maybe fail but it doesn't matter." 150 | mysql -u${user} -p${pass} -h${master_ip} -P${orig_master_port} --connect-timeout=1 -e "set global super_read_only = ON;" 151 | echo "Done" 152 | 153 | exit 154 | fi 155 | 156 | 157 | # we only handle 'start' command 158 | if [ x"$com" = x"start" ];then 159 | echo "*start command*" 160 | echo "***do proxysql failover. activate the new master***" 161 | # failover all the ProxySQLs 162 | for p in ${proxysql_hosts[@]} 163 | do 164 | proxysql_failover $p 165 | done 166 | fi 167 | -------------------------------------------------------------------------------- /roles/mha_manager/templates/template/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cur_dir=$(dirname $(readlink -f "$0")) 4 | 5 | statusf=".master_status.health" 6 | n=`ls $cur_dir | grep $statusf | wc -l` 7 | 8 | if [ $n -ne 0 ];then 9 | echo "masterha_manager is already running... Exit!" 10 | exit 11 | fi 12 | 13 | 14 | #conf=$cur_dir/mha_test.cnf 15 | #log=$cur_dir/test.log 16 | 17 | conf=`find $cur_dir -name "*.cnf" | head -n 1` 18 | log=`find $cur_dir -name "*.log" | head -n 1` 19 | nohup masterha_manager --conf=$conf < /dev/null > $log 2>&1 & 20 | 21 | -------------------------------------------------------------------------------- /roles/mha_manager/templates/template/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cur_dir=$(dirname $(readlink -f "$0")) 4 | 5 | #conf=$cur_dir/mha_test.cnf 6 | conf=`find $cur_dir -name "*.cnf" | head -n 1` 7 | masterha_stop --conf=$conf 8 | -------------------------------------------------------------------------------- /roles/mha_manager/templates/template/switch_online.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cur_dir=$(dirname $(readlink -f "$0")) 3 | 4 | #conf=$cur_dir/mha_test.cnf 5 | conf=`find $cur_dir -name "*.cnf" | head -n 1` 6 | 7 | # master_state:dead , alive 8 | # --new_master_host (optional if you want to indicate the new master) 9 | #masterha_master_switch --master_state=alive --conf=$conf 10 | masterha_master_switch --master_state=alive --conf=$conf --orig_master_is_new_slave --interactive=0 11 | 12 | #masterha_master_switch --master_state=dead --conf=$conf --dead_master_host=11.0.1. --interactive=0 13 | 14 | # After switch , the original master is dropped. You can add it as a replica to the new master 15 | # change master to master_host='192.168.212.51',master_user='repl',master_password='repl',master_auto_position=1; 16 | -------------------------------------------------------------------------------- /roles/mha_node/files/mha_node.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiyuan-finance/MySQL-HA-Cluster/3e77a36763f75a9a4cb308fd9cbfc4ce21f42b0a/roles/mha_node/files/mha_node.tgz -------------------------------------------------------------------------------- /roles/mha_node/files/mha_node/ExtUtils-Constant-0.25.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiyuan-finance/MySQL-HA-Cluster/3e77a36763f75a9a4cb308fd9cbfc4ce21f42b0a/roles/mha_node/files/mha_node/ExtUtils-Constant-0.25.tar.gz -------------------------------------------------------------------------------- /roles/mha_node/files/mha_node/README: -------------------------------------------------------------------------------- 1 | For centos6 perl 5.10 2 | install perl-DBD-MySQL-4.013-3.el6.x86_64.rpm 3 | compile and install ExtUtils-Constant-0.25.tar.gz 4 | compile and install Socket-2.027.tar.gz 5 | complie and install v0.58_mha_node.tar.gz 6 | 7 | 8 | -------------------------------------------------------------------------------- /roles/mha_node/files/mha_node/Socket-2.027.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiyuan-finance/MySQL-HA-Cluster/3e77a36763f75a9a4cb308fd9cbfc4ce21f42b0a/roles/mha_node/files/mha_node/Socket-2.027.tar.gz -------------------------------------------------------------------------------- /roles/mha_node/files/mha_node/install_node.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $UID -ne 0 ];then 4 | echo "You're not root! Exit." 5 | exit 6 | fi 7 | 8 | yum localinstall perl-DBD-MySQL-4.013-3.el6.x86_64.rpm -y 9 | 10 | yum install "perl(Module::Install)" -y 11 | 12 | yum install gcc -y 13 | 14 | echo "+++++++++Install mha node..." 15 | tar xzf v0.58_mha_node.tar.gz 16 | 17 | cd mha4mysql-node-0.58 18 | 19 | perl Makefile.PL 20 | make 21 | make install 22 | 23 | echo "+++++++++Done" 24 | echo "" 25 | 26 | cd .. 27 | 28 | echo "+++++++++install ExtUtils-Constant" 29 | 30 | tar xzf ExtUtils-Constant-0.25.tar.gz 31 | 32 | cd ExtUtils-Constant-0.25 33 | perl Makefile.PL 34 | make 35 | make install 36 | 37 | echo "++++++++Done" 38 | echo "" 39 | 40 | cd .. 41 | echo "+++++++++install Socket" 42 | tar xzf Socket-2.027.tar.gz 43 | cd Socket-2.027 44 | perl Makefile.PL 45 | make 46 | make install 47 | 48 | echo "++++++++Done" 49 | echo "" 50 | 51 | echo "++++++ Add to path" 52 | c=`echo $PATH | grep /usr/local/bin | wc -l` 53 | if [ $c -eq 0 ];then 54 | echo "PATH=$PATH:/usr/local/bin" >> /etc/profile; source /etc/profile; 55 | fi 56 | cd .. 57 | #cp purge_relay_log_cron.sh /usr/local/bin/ 58 | #add crontab: purge_relay_log_cron.sh 59 | #TODO: 60 | echo "Finished!" 61 | -------------------------------------------------------------------------------- /roles/mha_node/files/mha_node/perl-DBD-MySQL-4.013-3.el6.x86_64.rpm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiyuan-finance/MySQL-HA-Cluster/3e77a36763f75a9a4cb308fd9cbfc4ce21f42b0a/roles/mha_node/files/mha_node/perl-DBD-MySQL-4.013-3.el6.x86_64.rpm -------------------------------------------------------------------------------- /roles/mha_node/files/mha_node/v0.58_mha_node.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiyuan-finance/MySQL-HA-Cluster/3e77a36763f75a9a4cb308fd9cbfc4ce21f42b0a/roles/mha_node/files/mha_node/v0.58_mha_node.tar.gz -------------------------------------------------------------------------------- /roles/mha_node/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: add mha user 3 | user: 4 | name: mha 5 | group: mysql 6 | 7 | - name: copy mha_node to remote host 8 | copy: 9 | src: mha_node.tgz 10 | dest: /tmp/ 11 | force: yes 12 | 13 | - name: tar -xvf file 14 | shell: tar xzf mha_node.tgz 15 | args: 16 | chdir: /tmp/ 17 | 18 | - name: install mha node 19 | shell: /bin/bash install_node.sh > install_node.log 2>&1 20 | args: 21 | chdir: /tmp/mha_node 22 | -------------------------------------------------------------------------------- /roles/mysql/defaults/main.yml: -------------------------------------------------------------------------------- 1 | 2 | # due to the validate_plugin in mysql5.7, the root password needs to be a liitle complex 3 | mysql_root_password: pass4.SQL 4 | mysqld_error_log: "{{ datadir }}/mysqld.err" 5 | mysql_repl_user: repl 6 | mysql_repl_password: repl 7 | -------------------------------------------------------------------------------- /roles/mysql/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart mysql 3 | service: "name=mysqld state=restarted sleep=5" 4 | 5 | - name: start mysql 6 | service: "name=mysqld state=started sleep=5" 7 | -------------------------------------------------------------------------------- /roles/mysql/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: add mysql user 3 | user: 4 | name: mysql 5 | 6 | - name: create mysql directories 7 | file: 8 | path: '{{ item.dir }}' 9 | state: directory 10 | owner: mysql 11 | group: mysql 12 | mode: 0755 13 | with_items: 14 | - { dir: '{{ datadir }}' } 15 | - { dir: '{{ logdir }}' } 16 | - { dir: '{{ tmpdir }}' } 17 | 18 | #- name: download mysql repository 19 | # get_url: 20 | #url: "https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm" 21 | #dest: "/tmp" 22 | 23 | - name: install repository 24 | yum : 25 | name: https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm 26 | state: present 27 | 28 | - name: copy global my.cnf 29 | template: 30 | src: templates/my.cnf.j2 31 | dest: /etc/my.cnf 32 | 33 | - name: copy my.cnf for root user 34 | template: 35 | src: templates/root_my_cnf.j2 36 | dest: /root/.my.cnf 37 | 38 | - name: install mysql '{{ mysql_version }}' 39 | yum: 40 | name: 'mysql-community-server-{{ mysql_version }}' 41 | state: present 42 | enablerepo: mysql57-community 43 | disablerepo: mysql80-community 44 | 45 | - name: start mysql 46 | service: 47 | name: mysqld 48 | state: started 49 | 50 | - name: Find temporary password 51 | shell: > 52 | echo `grep 'temporary.*root@localhost' "{{ mysqld_error_log }}" | sed 's/.*root@localhost: //'` 53 | register: mysql_root_password_temp 54 | 55 | - name: Set new password for mysql root user 56 | shell: > 57 | mysql -NBe "alter user USER() identified by '{{ mysql_root_password }}';" 58 | --connect-expired-password -uroot -p'{{ mysql_root_password_temp.stdout }}' 59 | 60 | - name: uninstall validate_password plugin 61 | shell: > 62 | mysql -NBe "uninstall plugin validate_password" -uroot -p'{{ mysql_root_password }}' 63 | 64 | - name : install MySQL-python 65 | yum: 66 | name: MySQL-python 67 | state: latest 68 | 69 | 70 | - name: create replication user 71 | mysql_user: 72 | name: "{{ mysql_repl_user }}" 73 | host: "%" 74 | password: "{{ mysql_repl_password }}" 75 | priv: "*.*:REPLICATION SLAVE" 76 | state: present 77 | login_user: root 78 | login_password: "{{ mysql_root_password }}" 79 | when: mysql_role == "master" 80 | tags: 81 | - create-repl-user 82 | 83 | - name: change master to 84 | mysql_replication: 85 | mode: changemaster 86 | master_host: "{{ master_ip }}" 87 | master_user: repl 88 | master_password: repl 89 | master_auto_position: yes 90 | login_user: root 91 | login_password: "{{ mysql_root_password }}" 92 | when: mysql_role == 'slave' 93 | 94 | - name: start slave 95 | mysql_replication: 96 | mode: startslave 97 | login_user: root 98 | login_password: "{{ mysql_root_password }}" 99 | when: mysql_role == "slave" 100 | 101 | 102 | -------------------------------------------------------------------------------- /roles/mysql/templates/my.cnf.j2: -------------------------------------------------------------------------------- 1 | [client] 2 | socket={{ datadir }}/mysql.sock 3 | 4 | [mysqld] 5 | #General server parameters 6 | datadir = {{ datadir }} 7 | log_error={{ mysqld_error_log }} 8 | tmpdir= {{ tmpdir }} 9 | 10 | port = 3306 11 | server_id = {{ server_id }} 12 | socket={{ datadir }}/mysql.sock 13 | pid_file={{ datadir }}/mysqld.pid 14 | #bind_address=172.24.0. 15 | character_set_server='utf8' 16 | skip-name-resolve 17 | 18 | 19 | ## InnoDB specific parameters 20 | innodb_buffer_pool_size = 2G 21 | innodb_file_per_table=ON 22 | innodb_flush_log_at_trx_commit=1 23 | innodb_log_file_size=128M 24 | innodb_flush_method=O_DIRECT 25 | 26 | ## Transaction behavior 27 | autocommit=ON 28 | transaction-isolation=READ-COMMITTED 29 | 30 | 31 | #slow_query 32 | log_queries_not_using_indexes=1 33 | long_query_time=1 34 | slow_query_log=1 35 | 36 | 37 | ## Memory sizes 38 | join_buffer_size = 8M 39 | sort_buffer_size = 8M 40 | read_rnd_buffer_size = 16M 41 | 42 | tmp_table_size = 128M 43 | max_heap_table_size = 128M 44 | 45 | 46 | ## Miscellaneous parameters 47 | sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER 48 | default_storage_engine=InnoDB 49 | 50 | 51 | # max binlog keeps days 52 | expire_logs_days = 7 53 | 54 | max_connections=300 55 | log_timestamps=system 56 | slave_net_timeout=3600 57 | ## Enables binary logging 58 | relay_log={{ logdir }}/mysql-relay-bin.log 59 | max_relay_log_size=512M 60 | binlog_format=ROW 61 | log_bin={{ logdir }}/binlog.log 62 | max_binlog_size=512M 63 | master_info_repository = table 64 | relay_log_info_repository = table 65 | sync_binlog=1 66 | gtid_mode=ON 67 | log_slave_updates=ON 68 | enforce_gtid_consistency=ON 69 | plugin_load = "rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so" 70 | loose_rpl_semi_sync_master_enabled = 1 71 | loose_rpl_semi_sync_slave_enabled = 1 72 | loose_rpl_semi_sync_master_timeout = 5000 73 | ## Disabling symbolic-links is recommended to prevent assorted security risks 74 | symbolic-links=0 75 | 76 | {% if mysql_role == "slave" %} 77 | read_only=ON 78 | {% endif %} 79 | -------------------------------------------------------------------------------- /roles/mysql/templates/root_my_cnf.j2: -------------------------------------------------------------------------------- 1 | [client] 2 | user=root 3 | password={{ mysql_root_password }} 4 | socket={{ datadir }}/mysql.sock 5 | -------------------------------------------------------------------------------- /roles/proxysql/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: restart proxysql 2 | service: 3 | name: proxysql 4 | state: restarted 5 | 6 | -------------------------------------------------------------------------------- /roles/proxysql/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: add proxysql user 3 | user: 4 | name: proxysql 5 | 6 | - name: create data dir for proxysql 7 | file: 8 | path: "{{ item.dir }}" 9 | state: directory 10 | owner: proxysql 11 | group: proxysql 12 | with_items: 13 | - { dir: "{{ proxysql_datadir }}" } 14 | - { dir: "{{ proxysql_logdir }}" } 15 | 16 | - name: install proxysql 17 | yum: 18 | name: 'proxysql-{{ proxysql_version }}' 19 | state: present 20 | 21 | - name: copy proxysql conf files ... 22 | template: 23 | src: "templates/proxysql.j2" 24 | dest: /etc/rc.d/init.d/proxysql 25 | 26 | - name: reload systemd 27 | become: true 28 | shell: "systemctl daemon-reload" 29 | 30 | - name: start proxysql 31 | service: 32 | name: proxysql 33 | state: started 34 | 35 | - name: copy init_proxysql.sh script 36 | template: 37 | src: templates/init_proxysql.sh.j2 38 | dest: /tmp/init_proxysql.sh 39 | mode: 755 40 | 41 | - name: run init script 42 | shell: /bin/bash /tmp/init_proxysql.sh 43 | notify: 44 | - restart proxysql 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /roles/proxysql/templates/init_proxysql.sh.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mysql_admin="mysql -uadmin -padmin -P6032 -h127.0.0.1" 4 | 5 | # configure proxysql 6 | $mysql_admin -e "set mysql-query_digests_lowercase='true';set mysql-default_sql_mode ='{{ mysql_sql_mode }}';" 7 | $mysql_admin -e "SET mysql-eventslog_filename='{{ proxysql_logdir }}/queries.log';" 8 | $mysql_admin -e "set mysql-interfaces='0.0.0.0:{{ proxysql_client_port }}';" 9 | $mysql_admin -e "set mysql-forward_autocommit='true'; set mysql-monitor_slave_lag_when_null=30;" 10 | $mysql_admin -e "set mysql-wait_timeout=20000000 ;set mysql-verbose_query_error='true';set mysql-server_version='5.7.22';" 11 | 12 | $mysql_admin -e "SET mysql-monitor_username='{{ proxysql_monitor_user }}';set mysql-monitor_password='{{ proxysql_monitor_password }}';" 13 | 14 | # persist mysql variables 15 | $mysql_admin -e "save mysql variables to disk;" 16 | -------------------------------------------------------------------------------- /roles/proxysql/templates/proxysql.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # chkconfig: 345 99 01 4 | # description: High Performance and Advanced Proxy for MySQL and forks. \ 5 | # It provides advanced features like connection pool, query routing and rewrite, \ 6 | # firewalling, throttling, real time analysis, error-free failover 7 | ### BEGIN INIT INFO 8 | # Provides: proxysql 9 | # Required-Start: $local_fs 10 | # Required-Stop: $local_fs 11 | # Default-Start: 2 3 4 5 12 | # Default-Stop: 0 1 6 13 | # Short-Description: High Performance Advanced Proxy for MySQL 14 | # Description : High Performance and Advanced Proxy for MySQL and forks. 15 | # It provides advanced features like connection pool, query routing and rewrite, 16 | # firewalling, throttling, real time analysis, error-free failover 17 | ### END INIT INFO 18 | 19 | OLDDATADIR="/var/run/proxysql" 20 | DATADIR="{{ proxysql_datadir }}" 21 | OPTS="-c /etc/proxysql.cnf -D $DATADIR" 22 | PIDFILE="$DATADIR/proxysql.pid" 23 | 24 | ulimit -n 102400 25 | ulimit -c 1073741824 26 | 27 | getpid() { 28 | if [ -f $PIDFILE ] 29 | then 30 | if [ -r $PIDFILE ] 31 | then 32 | pid=`cat $PIDFILE` 33 | if [ "X$pid" != "X" ] 34 | then 35 | # Verify that a process with this pid is still running. 36 | pid=`ps -p $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1` 37 | if [ "X$pid" = "X" ] 38 | then 39 | # This is a stale pid file. 40 | rm -f $PIDFILE 41 | echo "Removed stale pid file: $PIDFILE" 42 | fi 43 | fi 44 | else 45 | echo "Cannot read $PIDFILE." 46 | exit 1 47 | fi 48 | fi 49 | } 50 | 51 | 52 | testpid() { 53 | pid=`ps -p $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1` 54 | if [ "X$pid" = "X" ] 55 | then 56 | # Process is gone so remove the pid file. 57 | rm -f $PIDFILE 58 | fi 59 | } 60 | 61 | initial() { 62 | OPTS="--initial $OPTS" 63 | start 64 | } 65 | 66 | reload() { 67 | OPTS="--reload $OPTS" 68 | start 69 | } 70 | 71 | start() { 72 | echo -n "Starting ProxySQL: " 73 | mkdir $DATADIR 2>/dev/null 74 | getpid 75 | if [ "X$pid" = "X" ] 76 | then 77 | if [ -f $OLDDATADIR/proxysql.db ] 78 | then 79 | if [ ! -f $DATADIR/proxysql.db ] 80 | then 81 | mv -iv $OLDDATADIR/proxysql.db $DATADIR/proxysql.db 82 | fi 83 | fi 84 | proxysql $OPTS 85 | if [ "$?" = "0" ]; then 86 | echo "DONE!" 87 | return 0 88 | else 89 | echo "FAILED!" 90 | return 1 91 | fi 92 | else 93 | echo "ProxySQL is already running." 94 | exit 0 95 | fi 96 | } 97 | 98 | stop() { 99 | echo -n "Shutting down ProxySQL: " 100 | getpid 101 | if [ "X$pid" = "X" ] 102 | then 103 | echo "ProxySQL was not running." 104 | exit 0 105 | else 106 | # Note: we send a kill to all the processes, not just to the child 107 | for i in `pgrep -x proxysql` ; do 108 | if [ "$i" != "$$" ]; then 109 | kill $i 110 | fi 111 | done 112 | # Loop until it does. 113 | savepid=$pid 114 | CNT=0 115 | TOTCNT=0 116 | while [ "X$pid" != "X" ] 117 | do 118 | # Loop for up to 20 second 119 | if [ "$TOTCNT" -lt "200" ] 120 | then 121 | if [ "$CNT" -lt "10" ] 122 | then 123 | CNT=`expr $CNT + 1` 124 | else 125 | echo -n "." 126 | CNT=0 127 | fi 128 | TOTCNT=`expr $TOTCNT + 1` 129 | 130 | sleep 0.1 131 | 132 | testpid 133 | else 134 | pid= 135 | fi 136 | done 137 | pid=$savepid 138 | testpid 139 | if [ "X$pid" != "X" ] 140 | then 141 | echo 142 | echo "Timed out waiting for ProxySQL to exit." 143 | echo " Attempting a forced exit..." 144 | for i in `pgrep proxysql` ; do 145 | if [ "$i" != "$$" ]; then 146 | kill -9 $i 147 | fi 148 | done 149 | fi 150 | 151 | pid=$savepid 152 | testpid 153 | if [ "X$pid" != "X" ] 154 | then 155 | echo "Failed to stop ProxySQL" 156 | exit 1 157 | else 158 | echo "DONE!" 159 | fi 160 | fi 161 | } 162 | 163 | 164 | status() { 165 | getpid 166 | if [ "X$pid" = "X" ] 167 | then 168 | echo "ProxySQL is not running." 169 | exit 1 170 | else 171 | echo "ProxySQL is running ($pid)." 172 | exit 0 173 | fi 174 | } 175 | 176 | case "$1" in 177 | start) 178 | start 179 | ;; 180 | initial) 181 | initial 182 | ;; 183 | reload) 184 | reload 185 | ;; 186 | stop) 187 | stop 188 | ;; 189 | status) 190 | status 191 | ;; 192 | restart) 193 | stop 194 | start 195 | ;; 196 | *) 197 | echo "Usage: ProxySQL {start|stop|status|reload|restart|initial}" 198 | exit 1 199 | ;; 200 | esac 201 | exit $? 202 | 203 | -------------------------------------------------------------------------------- /start_deploy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install MySQL Replication Cluster 3 | hosts: mysql_servers 4 | remote_user: root 5 | become: yes 6 | 7 | tags: 8 | - install-mysql 9 | roles: 10 | - mysql 11 | 12 | - name: "Install ProxySQL" 13 | hosts: proxysql_servers 14 | remote_user: root 15 | pre_tasks: 16 | - name: install proxysql repo 17 | copy: 18 | src: proxysql.repo 19 | dest: /etc/yum.repos.d/proxysql.repo 20 | 21 | tags: 22 | - install-proxysql 23 | roles: 24 | - proxysql 25 | 26 | - name: Install MHA Node 27 | hosts: mha_node_servers 28 | remote_user: root 29 | 30 | tags: 31 | - install-mha-node 32 | roles: 33 | - mha_node 34 | 35 | - name: Install MHA Manager 36 | hosts: mha_manager_servers 37 | remote_user: root 38 | tags: 39 | - install-mha-manager 40 | roles: 41 | - mha_manager 42 | --------------------------------------------------------------------------------