├── vars └── main.yml ├── files ├── usr │ └── local │ │ └── bin │ │ ├── mysql_run.sh │ │ └── start_services.sh └── etc │ └── init.d │ └── mysql ├── tasks ├── misc.yml ├── main.yml ├── configure_galera.yml ├── repo.yml ├── grants.yml ├── clustercheck.yml └── install_galera.yml ├── handlers └── main.yml ├── meta └── main.yml ├── defaults └── main.yml ├── LICENSE ├── README.md └── templates ├── usr ├── bin │ └── clustercheck.j2 └── local │ └── bin │ └── pyclustercheck.j2 └── etc └── mysql └── my.cnf.j2 /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # file: vars/main.yml 3 | -------------------------------------------------------------------------------- /files/usr/local/bin/mysql_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | /usr/local/bin/pyclustercheck -p 9200 & 4 | /usr/bin/mysqld_safe $@ & 5 | -------------------------------------------------------------------------------- /files/usr/local/bin/start_services.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if test -d /etc/mysql; 4 | then 5 | /etc/init.d/mysql start 6 | /usr/local/bin/pyclustercheck -p 9200 & 7 | fi 8 | /usr/sbin/sshd -D $@ 9 | -------------------------------------------------------------------------------- /tasks/misc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # file: tasks/misc.yml 3 | 4 | - name: Install things I like 5 | apt: pkg={{ item }} 6 | state=present 7 | with_items: 8 | - vim 9 | - git 10 | - ansible 11 | - telnet 12 | 13 | 14 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # restart mysql 3 | 4 | - name: start mysql 5 | command: /etc/init.d/mysql start 6 | 7 | - name: restart mysql 8 | command: /etc/init.d/mysql restart 9 | 10 | - name: stop mysql 11 | command: /etc/init.d/mysql stop 12 | 13 | - name: bootstrap node 14 | command: /etc/init.d/mysql bootstrap-pxc 15 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Patrick Galbraith 4 | description: An ansible role for Galera/Percona XtraDB Cluster 5 | company: Hewlett Packard 6 | license: Apache 2.0 7 | min_ansible_version: 1.2 8 | platforms: 9 | - name: Ubuntu 10 | versions: 11 | - precise 12 | - raring 13 | categories: 14 | - database 15 | - packaging 16 | dependencies: [] 17 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # file: tasks/main.yml 3 | 4 | - debug: msg="hostvars={{hostvars}}" 5 | - debug: msg="address {{ hostvars[inventory_hostname].ansible_default_ipv4.address }} {{ ansible_eth0.ipv4.address }}" 6 | - debug: msg="vars={{vars}}" 7 | - debug: msg="groups={{groups}}" 8 | #- include: misc.yml 9 | - include: repo.yml 10 | - include: install_galera.yml 11 | - include: grants.yml 12 | - include: configure_galera.yml 13 | - include: clustercheck.yml 14 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # file: vars/main.yml 3 | # these values are default - change for security! 4 | galera_conf: 5 | bind_address: 0.0.0.0 6 | dbusers: 7 | xtrabackup: 8 | username: xtrabackup 9 | password: xtrabackup 10 | docker: 11 | username: docker 12 | password: docker 13 | host: 172.17.% 14 | clustercheck: 15 | username: clustercheck 16 | password: clustercheck 17 | -------------------------------------------------------------------------------- /tasks/configure_galera.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Copy the modified init script 4 | copy: src=etc/init.d/mysql 5 | dest=/etc/init.d/mysql 6 | mode=0755 7 | 8 | - name: Copy the helper script 9 | copy: src=usr/local/bin/start_services.sh 10 | dest=/usr/local/bin/entrypoint.sh 11 | mode=0755 12 | 13 | - name: Configure Percona XtraDB Cluster server 14 | template: src=etc/mysql/my.cnf.j2 15 | dest=/etc/mysql/my.cnf 16 | mode=0640 owner=mysql group=root 17 | notify: restart mysql 18 | when: bootstrap_check.stdout == "bootstrap" 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 Patrick "CaptTofu" Galbraith 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /tasks/repo.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # file: tasks/repo.yml 3 | 4 | - name: Obtain Percona public key 5 | # apt_key: url=http://keys.gnupg.net/pks/lookup?op=get&search=0x1C4CBDCDCD2EFD2A 6 | apt_key: url=http://www.percona.com/downloads/RPM-GPG-KEY-percona 7 | state=present 8 | 9 | - name: Add Percona repository 10 | apt_repository: repo='deb http://repo.percona.com/apt precise main' 11 | state=present 12 | 13 | - name: Add Percona source repository 14 | apt_repository: repo='deb-src http://repo.percona.com/apt precise main' 15 | state=present 16 | 17 | - name: Update apt cache 18 | apt: update_cache=yes 19 | 20 | -------------------------------------------------------------------------------- /tasks/grants.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # file: tasks/grants.yml 3 | 4 | - name: Add Docker database user 5 | mysql_user: user={{ galera_conf['dbusers']['docker']['username'] }} host={{ galera_conf['dbusers']['docker']['host'] }} password={{ galera_conf['dbusers']['docker']['password'] }} priv=*.*:"all privileges" 6 | 7 | - name: Add xtrabackup database user (for Galera SST) 8 | mysql_user: user={{ galera_conf['dbusers']['xtrabackup']['username'] }} host="localhost" password={{ galera_conf['dbusers']['xtrabackup']['password'] }} priv=*.*:"grant,reload,replication client" 9 | 10 | - name: Add clustercheck database user (for clustercheck/xinetd -> haproxy) 11 | mysql_user: user={{ galera_conf['dbusers']['clustercheck']['username'] }} host="localhost" password={{ galera_conf['dbusers']['clustercheck']['password'] }} priv=*.*:"grant,reload,replication client" 12 | 13 | -------------------------------------------------------------------------------- /tasks/clustercheck.yml: -------------------------------------------------------------------------------- 1 | # file: tasks/clustercheck.yml 2 | 3 | - name: Copy clustercheck script 4 | template: src=usr/bin/clustercheck.j2 5 | dest=/usr/bin/clustercheck 6 | owner=root 7 | group=root 8 | mode=0700 9 | 10 | # restarting mysql because this is the very last thing that is done 11 | - name: Copy clustercheck script 12 | template: src=usr/local/bin/pyclustercheck.j2 13 | dest=/usr/local/bin/pyclustercheck 14 | owner=root 15 | group=root 16 | mode=0700 17 | 18 | # Create link if not already exists. Upstart for mysql needs this! 19 | # - command: /usr/local/bin/mysql_run.sh 20 | # when: bootstrap_check.stdout == "bootstrap" 21 | # unable to get working on Docker 22 | #- name: Append clustercheck to services 23 | # lineinfile: dest=/etc/services 24 | # regexp="^mysqlchk 9200/tcp" 25 | # line="mysqlchk 9200/tcp" 26 | # state=present 27 | -------------------------------------------------------------------------------- /tasks/install_galera.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # check if mtab is there 3 | - name: check if mtab exists 4 | action: shell test -f /etc/mtab && echo "aqui" || echo "nada" 5 | register: mtab_check 6 | 7 | # Create link if not already exists. Upstart for mysql needs this! 8 | - name: create mtab if not exists 9 | action: file src=/proc/mounts dest=/etc/mtab state=link 10 | when: mtab_check.stdout == "nada" 11 | 12 | # file: tasks/install_galera.yml 13 | # check if galera is already installed 14 | - name: check to determine if galera is aready installed 15 | action: shell test -d /etc/mysql && echo "installed" || echo "bootstrap" 16 | register: bootstrap_check 17 | 18 | # Create link if not already exists. Upstart for mysql needs this! 19 | # - name: create /etc/mysql if not exists 20 | # action: file path=/etc/mysql state=directory 21 | # when: bootstrap_check.stdout == "bootstrap" 22 | 23 | - name: Install Percona XtraDB Cluster server 24 | apt: pkg={{ item }} 25 | state=present 26 | with_items: 27 | - percona-xtradb-cluster-server-5.6 28 | - python-mysqldb 29 | - xinetd 30 | - git 31 | 32 | - name: Start MySQL 33 | command: /etc/init.d/mysql restart 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Role Name 2 | ======== 3 | 4 | ansible-galera -- build a galera cluser node 5 | 6 | Requirements 7 | ------------ 8 | 9 | None 10 | 11 | Role Variables 12 | -------------- 13 | 14 | percona -- see main.yml in ./defaults 15 | 16 | Dependencies 17 | ------------ 18 | 19 | None 20 | 21 | License 22 | ------- 23 | 24 | Apache 2.0 25 | 26 | Author Information 27 | ------------------ 28 | 29 | Patrick "CaptTofu" Galbraith 30 | 31 | How do I use this? 32 | ------------------ 33 | 34 | Very simple. 35 | 36 | 1. ansible-galaxy install --force --roles-path=/where/ever/you/want/your/roles CaptTofu.galera 37 | 38 | 2. Add three galera hosts to your ansible hosts file 39 | 40 | [galera_cluster] 41 | host1 42 | host2 43 | host3 44 | 45 | 2. Use it in your playbook: 46 | 47 | 48 | - hosts: 49 | - galera_cluster 50 | roles: 51 | - CaptTofu.ansible-galera 52 | 53 | 3. Run your playbook (this is for Docker) 54 | 55 | ansible-playbook -i hosts -u root cluster.yml 56 | 57 | Other 58 | ------ 59 | 60 | See: 61 | 62 | - http://github.com/CaptTofu/ansible-galera-haproxy.git - the role for setting up the haproxy setup that would utilize this cluster 63 | 64 | - http://github.com/CaptTofu/docker-galera.git - scripts for building the hosts file and launching/deleting containers used in testing this 65 | 66 | - http://github.com/CaptTofu/cluster-install.git - the top-level playbook that uses both the ansible-galera and ansible-galera-haproxy roles refered to here 67 | -------------------------------------------------------------------------------- /templates/usr/bin/clustercheck.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Script to make a proxy (ie HAProxy) capable of monitoring Percona XtraDB Cluster nodes properly 4 | # 5 | # Authors: 6 | # Raghavendra Prabhu 7 | # Olaf van Zandwijk 8 | # 9 | # Based on the original script from Unai Rodriguez and Olaf (https://github.com/olafz/percona-clustercheck) 10 | # 11 | # Grant privileges required: 12 | # GRANT PROCESS ON *.* TO 'clustercheckuser'@'localhost' IDENTIFIED BY 'clustercheckpassword!'; 13 | 14 | if [[ $1 == '-h' || $1 == '--help' ]];then 15 | echo "Usage: $0 " 16 | exit 17 | fi 18 | 19 | MYSQL_USERNAME="{{ galera_conf['dbusers']['clustercheck']['username'] }}" 20 | MYSQL_PASSWORD="{{ galera_conf['dbusers']['clustercheck']['password'] }}" 21 | AVAILABLE_WHEN_DONOR=${3:-0} 22 | ERR_FILE="${4:-/dev/null}" 23 | AVAILABLE_WHEN_READONLY=${5:-1} 24 | DEFAULTS_EXTRA_FILE=${6:-/etc/mysql/my.cnf} 25 | #Timeout exists for instances where mysqld may be hung 26 | TIMEOUT=10 27 | 28 | MYSQL_CMDLINE="mysql --defaults-extra-file=$DEFAULTS_EXTRA_FILE -nNE --connect-timeout=$TIMEOUT \ 29 | --user=${MYSQL_USERNAME} --password=${MYSQL_PASSWORD}" 30 | # 31 | # Perform the query to check the wsrep_local_state 32 | # 33 | WSREP_STATUS=$($MYSQL_CMDLINE -e "SHOW STATUS LIKE 'wsrep_local_state';" \ 34 | 2>${ERR_FILE} | tail -1 2>>${ERR_FILE}) 35 | 36 | if [[ "${WSREP_STATUS}" == "4" ]] || [[ "${WSREP_STATUS}" == "2" && ${AVAILABLE_WHEN_DONOR} == 1 ]] 37 | then 38 | 39 | # Check only when set to 0 to avoid latency in response. 40 | if [[ $AVAILABLE_WHEN_READONLY -eq 0 ]];then 41 | READ_ONLY=$($MYSQL_CMDLINE -e "SHOW GLOBAL VARIABLES LIKE 'read_only';" \ 42 | 2>${ERR_FILE} | tail -1 2>>${ERR_FILE}) 43 | 44 | if [[ "${READ_ONLY}" == "ON" ]];then 45 | echo -en "HTTP/1.1 503 Service Unavailable\r\n" 46 | echo -en "Content-Type: text/plain\r\n" 47 | echo -en "Connection: close\r\n" 48 | echo -en "Content-Length: 43\r\n" 49 | echo -en "\r\n" 50 | echo -en "Percona XtraDB Cluster Node is read-only.\r\n" 51 | exit 1 52 | fi 53 | 54 | fi 55 | # Percona XtraDB Cluster node local state is 'Synced' => return HTTP 200 56 | # Shell return-code is 0 57 | echo -en "HTTP/1.1 200 OK\r\n" 58 | echo -en "Content-Type: text/plain\r\n" 59 | echo -en "Connection: close\r\n" 60 | echo -en "Content-Length: 40\r\n" 61 | echo -en "\r\n" 62 | echo -en "Percona XtraDB Cluster Node is synced.\r\n" 63 | exit 0 64 | else 65 | # Percona XtraDB Cluster node local state is not 'Synced' => return HTTP 503 66 | # Shell return-code is 1 67 | echo -en "HTTP/1.1 503 Service Unavailable\r\n" 68 | echo -en "Content-Type: text/plain\r\n" 69 | echo -en "Connection: close\r\n" 70 | echo -en "Content-Length: 44\r\n" 71 | echo -en "\r\n" 72 | echo -en "Percona XtraDB Cluster Node is not synced.\r\n" 73 | exit 1 74 | fi 75 | -------------------------------------------------------------------------------- /templates/etc/mysql/my.cnf.j2: -------------------------------------------------------------------------------- 1 | [client] 2 | port = 3306 3 | socket = /var/run/mysqld/mysqld.sock 4 | 5 | [mysqld_safe] 6 | socket = /var/run/mysqld/mysqld.sock 7 | nice = 0 8 | 9 | [mysqld] 10 | 11 | bind_address = {{ galera_conf['bind_address'] }} 12 | user = mysql 13 | pid-file = /var/run/mysqld/mysqld.pid 14 | socket = /var/run/mysqld/mysqld.sock 15 | port = 3306 16 | basedir = /usr 17 | datadir = /var/lib/mysql 18 | default_storage_engine = InnoDB 19 | tmpdir = /tmp 20 | 21 | skip-name-resolve 22 | 23 | lc-messages-dir = /usr/share/mysql 24 | skip-external-locking 25 | key_buffer = 16M 26 | max_connections = 100 27 | max_allowed_packet = 16M 28 | thread_stack = 192K 29 | thread_cache_size = 8 30 | 31 | myisam-recover = BACKUP 32 | query_cache_limit = 1M 33 | query_cache_size = 16M 34 | 35 | log_error = /var/log/mysql/err.log 36 | slow_query_log = 1 37 | long_query_time = 2 38 | log-queries-not-using-indexes 39 | 40 | # server-id = 1 41 | log_bin = /var/log/mysql/mysql-bin.log 42 | sync_binlog = 1 43 | binlog_format = ROW 44 | expire_logs_days = 2 45 | max_binlog_size = 100M 46 | log_slave_updates 47 | 48 | wsrep_provider = /usr/lib/libgalera_smm.so 49 | wsrep_slave_threads = 4 50 | wsrep_sst_method = xtrabackup 51 | wsrep_sst_auth = {{ galera_conf['dbusers']['xtrabackup']['username'] }}:{{ galera_conf['dbusers']['xtrabackup']['password'] }} 52 | wsrep_cluster_name = percona-cluster 53 | 54 | {# list of cluster members #} 55 | {% set cluster_members = [] -%} 56 | 57 | {# flag to bootstrap #} 58 | {% set bootstrap_cluster = 0 -%} 59 | 60 | {# if this is the first node AND bootstrap #} 61 | {%- if groups['galera_cluster'].index(inventory_hostname) == 0 and "bootstrap" in bootstrap_check.stdout_lines[0] %} 62 | {%- set bootstrap_cluster = 1 %} 63 | {% endif -%} 64 | 65 | {%- if not bootstrap_cluster %} 66 | {%- for node in groups['galera_cluster'] %} 67 | {%- if hostvars[node].ansible_default_ipv4.address != ansible_eth0.ipv4.address %} 68 | {%- set _ = cluster_members.append(hostvars[node].ansible_default_ipv4.address) -%} 69 | {% endif -%} 70 | {% endfor -%} 71 | {% endif -%} 72 | wsrep_cluster_address = gcomm://{{ ",".join(cluster_members) }} 73 | wsrep_node_name = {{ ansible_eth0.ipv4.address }} 74 | wsrep_provider_options = gcache.size=2G; 75 | 76 | default_storage_engine = InnoDB 77 | innodb_autoinc_lock_mode = 2 78 | # innodb_locks_unsafe_for_binlog = 1 79 | innodb_file_per_table 80 | innodb_thread_concurrency = 0 81 | innodb_buffer_pool_size = 50M 82 | innodb_log_buffer_size = 64M 83 | # innodb_flush_method = O_DIRECT 84 | innodb_log_file_size = 500M 85 | 86 | [mysqldump] 87 | quick 88 | quote-names 89 | max_allowed_packet = 16M 90 | 91 | [mysql] 92 | #no-auto-rehash # faster start of mysql but no tab completition 93 | 94 | [isamchk] 95 | key_buffer = 16M 96 | 97 | 98 | 99 | 100 | !includedir /etc/mysql/conf.d/ 101 | -------------------------------------------------------------------------------- /templates/usr/local/bin/pyclustercheck.j2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from SimpleHTTPServer import SimpleHTTPRequestHandler 4 | import BaseHTTPServer 5 | import MySQLdb 6 | import MySQLdb.cursors 7 | import optparse 8 | import time 9 | import socket 10 | 11 | ''' 12 | __author__="See AUTHORS.txt at https://github.com/Oneiroi/clustercheck" 13 | __copyright__="David Busby, Percona Ireland Ltd" 14 | __license__="GNU v3 + section 7: Redistribution/Reuse of this code is permitted under the GNU v3 license, as an additional term ALL code must carry the original Author(s) credit in comment form." 15 | __dependencies__="MySQLdb (python26-mysqldb (el5) / MySQL-python (el6) / python-mysqldb (ubuntu))" 16 | __description__="Provides a stand alone http service, evaluating wsrep_local_state intended for use with HAProxy. Listens on 8000" 17 | ''' 18 | 19 | class opts: 20 | available_when_donor = 0 21 | cache_time = 1 22 | last_query_time = 0 23 | last_query_result = 0 24 | cnf_file = '~/.my.cnf' 25 | being_updated = False 26 | # Overriding the connect timeout so that status check doesn't hang 27 | c_timeout = 10 28 | 29 | class clustercheck(BaseHTTPServer.BaseHTTPRequestHandler): 30 | def do_OPTIONS(self): 31 | self.do_GET() 32 | 33 | def do_GET(self): 34 | ctime = time.time() 35 | if ((ctime - opts.last_query_time) > opts.cache_time) and opts.being_updated == False: 36 | #cache expired 37 | opts.being_updated = True 38 | opts.last_query_time = ctime 39 | 40 | conn = None 41 | try: 42 | conn = MySQLdb.connect( 43 | read_default_file = opts.cnf_file, 44 | connect_timeout = opts.c_timeout, 45 | cursorclass = MySQLdb.cursors.DictCursor) 46 | except MySQLdb.OperationalError: 47 | opts.being_updated = False #corrects a bug where the flag is never reset on communication failiure 48 | 49 | if conn: 50 | curs = conn.cursor() 51 | curs.execute("SHOW STATUS LIKE 'wsrep_local_state'") 52 | res = curs.fetchall() 53 | else: 54 | res = '' 55 | 56 | 57 | if len(res) == 0: 58 | opts.last_query_result = 0 59 | self.send_response(503) 60 | self.send_header("Content-type", "text/html") 61 | self.end_headers() 62 | self.wfile.write("Percona XtraDB Cluster Node state could not be retrieved.") 63 | 64 | elif res[0]['Value'] == '4' or (int(opts.available_when_donor) == 1 and res[0]['Value'] == '2'): 65 | opts.last_query_result = res[0]['Value'] 66 | self.send_response(200) 67 | self.send_header("Content-type", "text/html") 68 | self.end_headers() 69 | self.wfile.write("Percona XtraDB Cluster Node is synced.") 70 | else: 71 | opts.last_query_result = res[0]['Value'] 72 | self.send_response(503) 73 | self.send_header("Content-type", "text/html") 74 | self.end_headers() 75 | self.wfile.write("Percona XtraDB Cluster Node is not synced.") 76 | 77 | if conn: 78 | conn.close() 79 | opts.being_updated = False 80 | else: 81 | #use cache result 82 | if opts.last_query_result == '4' or (int(opts.available_when_donor) == 1 and opts.last_query_result == '2'): 83 | self.send_response(200) 84 | self.send_header("Content-type", "text/html") 85 | self.end_headers() 86 | self.wfile.write("CACHED: Percona XtraDB Cluster Node is synced.") 87 | else: 88 | self.send_response(503) 89 | self.send_header("Content-type", "text/html") 90 | self.end_headers() 91 | self.wfile.write("CACHED: Percona XtraDB Cluster Node is not synced.") 92 | 93 | """ 94 | Usage: 95 | pyclustercheck &>$LOGFILE & 96 | To test: 97 | curl http://127.0.0.1:8000 98 | 99 | """ 100 | if __name__ == '__main__': 101 | parser = optparse.OptionParser() 102 | parser.add_option('-a','--available-when-donor', dest='awd', default=0, help="Available when donor [default: %default]") 103 | parser.add_option('-c','--cache-time', dest='cache', default=1, help="Cache the last response for N seconds [default: %default]") 104 | parser.add_option('-f','--conf', dest='cnf', default='~/.my.cnf', help="MySQL Config file to use [default: %default]") 105 | parser.add_option('-p','--port', dest='port', default=8000, help="Port to listen on [default: %default]") 106 | parser.add_option('-6','--ipv6', action="store_true", dest='ipv6', default=False, help="Listen on ipv6 [default: %default]") 107 | 108 | options, args = parser.parse_args() 109 | opts.available_when_donor = options.awd 110 | opts.cnf_file = options.cnf 111 | opts.cache_time = options.cache 112 | 113 | server_class = BaseHTTPServer.HTTPServer 114 | 115 | if options.ipv6: 116 | server_class.address_family = socket.AF_INET6 117 | 118 | httpd = server_class(('',int(options.port)),clustercheck) 119 | httpd.serve_forever() 120 | -------------------------------------------------------------------------------- /files/etc/init.d/mysql: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | ### BEGIN INIT INFO 4 | # Provides: mysql 5 | # Required-Start: $remote_fs $syslog 6 | # Required-Stop: $remote_fs $syslog 7 | # Should-Start: $network $named $time 8 | # Should-Stop: $network $named $time 9 | # Default-Start: 2 3 4 5 10 | # Default-Stop: 0 1 6 11 | # Short-Description: Start and stop the mysql (Percona XtraDB Cluster) daemon 12 | # Description: Controls the main MySQL (Percona XtraDB Cluster) daemon "mysqld" 13 | # and its wrapper script "mysqld_safe". 14 | ### END INIT INFO 15 | # 16 | set -e 17 | set -u 18 | ${DEBIAN_SCRIPT_DEBUG:+ set -v -x} 19 | PERCONA_PREFIX=/usr 20 | startup_timeout=900 21 | stop_timeout=300 22 | startup_sleep=1 23 | 24 | test -x "${PERCONA_PREFIX}"/sbin/mysqld || exit 0 25 | 26 | . /lib/lsb/init-functions 27 | 28 | SELF=$(cd $(dirname $0); pwd -P)/$(basename $0) 29 | CONF=/etc/mysql/my.cnf 30 | 31 | # priority can be overriden and "-s" adds output to stderr 32 | ERR_LOGGER="logger -p daemon.err -t /etc/init.d/mysql -i" 33 | 34 | # Safeguard (relative paths, core dumps..) 35 | cd / 36 | umask 077 37 | 38 | # mysqladmin likes to read /root/.my.cnf. This is usually not what I want 39 | # as many admins e.g. only store a password without a username there and 40 | # so break my scripts. 41 | export HOME=/etc/mysql/ 42 | 43 | ## Fetch a particular option from mysql's invocation. 44 | # 45 | # Usage: void mysqld_get_param option 46 | mysqld_get_param() { 47 | "${PERCONA_PREFIX}"/sbin/mysqld --print-defaults \ 48 | | tr " " "\n" | awk -F= '{if ($1 ~ /_/) { gsub(/_/,"-",$1); print $1"="$2 } else { print $0 }}' \ 49 | | grep -- "--$1=" \ 50 | | tail -n 1 \ 51 | | cut -d= -f2 52 | } 53 | 54 | ## Do some sanity checks before even trying to start mysqld. 55 | sanity_checks() { 56 | # check for config file 57 | # DISABLED: We do not install my.cnf 58 | #if [ ! -r /etc/mysql/my.cnf ]; then 59 | # log_warning_msg "$0: WARNING: /etc/mysql/my.cnf cannot be read. See README.Debian.gz" 60 | # echo "WARNING: /etc/mysql/my.cnf cannot be read. See README.Debian.gz" | $ERR_LOGGER 61 | #fi 62 | 63 | # check for diskspace shortage 64 | datadir=`mysqld_get_param datadir` 65 | if LC_ALL=C BLOCKSIZE= df --portability $datadir/. | tail -n 1 | awk '{ exit ($4>4096) }'; then 66 | log_failure_msg "$0: ERROR: The partition with $datadir is too full!" 67 | echo "ERROR: The partition with $datadir is too full!" | $ERR_LOGGER 68 | exit 1 69 | fi 70 | 71 | if test -e $sst_progress_file;then 72 | log_daemon_msg "Stale sst_in_progress file in datadir" "mysqld" 73 | fi 74 | } 75 | 76 | # Get the pid file 77 | mysql_data_dir=$(mysqld_get_param datadir) 78 | [ ! $mysql_data_dir ] && mysql_data_dir="/var/lib/mysql" 79 | 80 | pid_file=$(mysqld_get_param pid-file) 81 | 82 | if test ! -f "$pid_file" -a $1 == 'stop'; 83 | then 84 | pid_file="$mysql_data_dir/$(hostname).pid" 85 | else 86 | case "$pid_file" in 87 | /* ) ;; 88 | * ) pid_file="$mysql_data_dir/$pid_file" ;; 89 | esac 90 | fi 91 | 92 | sst_progress_file=$mysql_data_dir/sst_in_progress 93 | ## Checks if there is a server running and if so if it is accessible. 94 | # 95 | # check_alive insists on a pingable server 96 | # check_dead also fails if there is a lost mysqld in the process list 97 | # 98 | # Usage: boolean mysqld_status [check_alive|check_dead] [warn|nowarn] 99 | mysqld_status () { 100 | mysqld_pid=$(cat $pid_file 2>/dev/null) 101 | 102 | if [ ! $mysqld_pid ];then 103 | echo "MySQL PID not found, pid_file detected/guessed: $pid_file" | $ERR_LOGGER 104 | if [ "$1" = "check_dead" ]; then 105 | return 0 106 | fi 107 | return 1 108 | fi 109 | 110 | ping_alive=1 111 | ps_alive=0 112 | 113 | soutput=$(mysql -u root -pabcd -e 'select 1;' 2>&1 | grep 'ERROR 2002') 114 | [ "$soutput" ] && ping_alive=0 115 | 116 | /bin/kill -0 $mysqld_pid &>/dev/null && ps_alive=1 117 | 118 | if [ "$1" = "check_alive" -a $ping_alive = 1 ] || 119 | [ "$1" = "check_dead" -a $ping_alive = 0 -a $ps_alive = 0 ]; then 120 | return 0 # EXIT_SUCCESS 121 | else 122 | if [ "$2" = "warn" ]; then 123 | warn_msg= 124 | [ $ping_alive = 0 ] && warn_msg+="mysql ping failed with $soutput" 125 | [ $ps_alive = 0 ] && \ 126 | warn_msg+=" and/or mysqld with pid $mysqld_pid is not alive" 127 | echo -e "$warn_msg" | $ERR_LOGGER -p daemon.debug 128 | fi 129 | return 1 # EXIT_FAILURE 130 | fi 131 | } 132 | 133 | log_startup_failure() { 134 | local msg="$@" 135 | if test -e $sst_progress_file;then 136 | msg+=" However, SST is still in progress. Please check if mysqld is running." 137 | fi 138 | log_failure_msg "$msg" 139 | } 140 | # 141 | # main() 142 | # 143 | 144 | case "${1:-''}" in 145 | 'start') 146 | sanity_checks; 147 | # Start daemon 148 | log_daemon_msg "Starting MySQL (Percona XtraDB Cluster) database server" "mysqld" 149 | if mysqld_status check_alive nowarn; then 150 | log_progress_msg "already running" 151 | log_end_msg 0 152 | else 153 | "${PERCONA_PREFIX}"/bin/mysqld_safe > /dev/null 2>&1 & 154 | safe_pid=$! 155 | avoid_race_condition="by checking again" 156 | 157 | for i in `seq 1 $startup_timeout`; do 158 | test -s $pid_file && break 159 | 160 | if test -e $sst_progress_file && [ $startup_sleep -ne 10 ];then 161 | log_daemon_msg "SST in progress, setting sleep higher" "mysqld" 162 | startup_sleep=10 163 | fi 164 | # if server isn't running, then pid-file will never be updated 165 | if test -n "$safe_pid"; then 166 | if kill -0 "$safe_pid" 2>/dev/null; then 167 | : # the server still runs 168 | else 169 | # The server may have exited between the last pid-file check and now. 170 | if test -n "$avoid_race_condition"; then 171 | avoid_race_condition="" 172 | continue # Check again. 173 | fi 174 | 175 | log_failure_msg "The server quit without updating PID file ($pid_file)." 176 | log_end_msg 1 177 | exit 1 # not waiting any more. 178 | fi 179 | fi 180 | sleep $startup_sleep 181 | log_progress_msg "." 182 | done 183 | sleep 1 184 | if mysqld_status check_alive warn; then 185 | log_end_msg 0 186 | # Now start mysqlcheck or whatever the admin wants. 187 | # only if the file /etc/mysql/DEBIAN-START is present. 188 | if test -e /etc/mysql/DEBIAN-START;then 189 | output=$(/etc/mysql/debian-start) 190 | [ "$output" ] && log_action_msg "$output" 191 | fi 192 | else 193 | log_startup_failure "Please take a look at the syslog." 194 | log_end_msg 1 195 | fi 196 | fi 197 | ;; 198 | 199 | 'stop') 200 | # * As a passwordless mysqladmin (e.g. via ~/.my.cnf) must be possible 201 | # at least for cron, we can rely on it here, too. (although we have 202 | # to specify it explicit as e.g. sudo environments points to the normal 203 | # users home and not /root) 204 | log_daemon_msg "Stopping MySQL (Percona XtraDB Cluster)" "mysqld" 205 | if ! mysqld_status check_dead nowarn; then 206 | set +e 207 | shutdown_out=`kill $mysqld_pid 2>&1`; r=$? 208 | set -e 209 | if [ "$r" -ne 0 ]; then 210 | log_end_msg 1 211 | log_daemon_msg "MySQLd already dead" "mysqld" 212 | else 213 | server_down= 214 | for i in `seq 1 $stop_timeout`; do 215 | sleep 1 216 | if mysqld_status check_dead nowarn; then server_down=1; break; fi 217 | done 218 | #Better to not kill in cases of large buffer pools 219 | #if test -z "$server_down"; then 220 | #kill -9 $mysqld_pid 221 | #fi 222 | fi 223 | fi 224 | 225 | if ! mysqld_status check_dead warn; then 226 | log_end_msg 1 227 | log_failure_msg "Please stop MySQL (Percona XtraDB Cluster) manually and \ 228 | read /usr/share/doc/percona-xtradb-cluster-server-5.6/README.Debian.gz!" 229 | exit -1 230 | else 231 | log_end_msg 0 232 | fi 233 | ;; 234 | 235 | 'restart') 236 | set +e; $SELF stop; set -e 237 | $SELF start 238 | ;; 239 | 240 | 'reload'|'force-reload') 241 | log_daemon_msg "Reloading MySQL (Percona XtraDB Cluster)" "mysqld" 242 | mysqld_pid=$(cat $pid_file 2>/dev/null) 243 | 244 | if [ ! $mysqld_pid ];then 245 | log_failure_msg "MySQL PID not found, pid_file detected/guessed: $pid_file" 246 | log_end_msg 1 247 | exit 4 248 | fi 249 | 250 | if kill -HUP $mysqld_pid 2>/dev/null;then 251 | log_daemon_msg "Percona XtraDB Cluster reload complete" "mysqld" 252 | log_end_msg 0 253 | else 254 | log_failure_msg "Percona XtraDB Cluster with PID $mysqld_pid \ 255 | is not running or unknown error" 256 | log_end_msg 1 257 | fi 258 | ;; 259 | 260 | 'status') 261 | if mysqld_status check_alive nowarn; then 262 | log_action_msg "Percona XtraDB Cluster up and running" 263 | else 264 | log_action_msg "MySQL (Percona XtraDB Cluster) is stopped. Check log" 265 | exit 3 266 | fi 267 | ;; 268 | 'bootstrap-pxc') 269 | startup_sleep=10 270 | sanity_checks; 271 | # Start daemon 272 | log_daemon_msg "Bootstrapping Percona XtraDB Cluster database server" "mysqld" 273 | if mysqld_status check_alive nowarn; then 274 | log_progress_msg "already running" 275 | log_end_msg 0 276 | else 277 | "${PERCONA_PREFIX}"/bin/mysqld_safe --wsrep-new-cluster > /dev/null 2>&1 & 278 | safe_pid=$! 279 | avoid_race_condition="by checking again" 280 | 281 | for i in `seq 1 $startup_timeout`; do 282 | test -s $pid_file && break 283 | 284 | if test -e $sst_progress_file && [ $startup_sleep -ne 10 ];then 285 | log_daemon_msg "SST in progress, setting sleep higher" "mysqld" 286 | startup_sleep=10 287 | fi 288 | # if server isn't running, then pid-file will never be updated 289 | if test -n "$safe_pid"; then 290 | if kill -0 "$safe_pid" 2>/dev/null; then 291 | : # the server still runs 292 | else 293 | # The server may have exited between the last pid-file check and now. 294 | if test -n "$avoid_race_condition"; then 295 | avoid_race_condition="" 296 | continue # Check again. 297 | fi 298 | 299 | log_failure_msg "The server quit without updating PID file ($pid_file)." 300 | log_end_msg 1 301 | exit 1 # not waiting any more. 302 | fi 303 | fi 304 | sleep $startup_sleep 305 | log_progress_msg "." 306 | done 307 | sleep 1 308 | if mysqld_status check_alive warn; then 309 | log_end_msg 0 310 | # Now start mysqlcheck or whatever the admin wants. 311 | # only if the file /etc/mysql/DEBIAN-START is present. 312 | if test -e /etc/mysql/DEBIAN-START;then 313 | output=$(/etc/mysql/debian-start) 314 | [ "$output" ] && log_action_msg "$output" 315 | fi 316 | else 317 | log_startup_failure "Please take a look at the syslog." 318 | log_end_msg 1 319 | fi 320 | fi 321 | ;; 322 | *) 323 | echo "Usage: $SELF start|stop|restart|reload|force-reload|status|bootstrap-pxc" 324 | exit 1 325 | ;; 326 | esac 327 | 328 | --------------------------------------------------------------------------------