diff --git a/hosts.yml b/hosts.yml new file mode 100644 index 0000000..7fd5e78 --- /dev/null +++ b/hosts.yml @@ -0,0 +1,15 @@ +--- +# file: hosts + +mariadb_cluster: + hosts: + db01: + ansible_host: 10.10.10.11 + db02: + ansible_host: 10.10.10.12 + + vars: + mariadb_cluster_wsrep_cluster_name: "CustomCluster1" + mariadb_cluster_access_ip: "10.10.10.253" + ansible_user: ansible + \ No newline at end of file diff --git a/mariadb_cluster.yml b/mariadb_cluster.yml new file mode 100644 index 0000000..5196b55 --- /dev/null +++ b/mariadb_cluster.yml @@ -0,0 +1,7 @@ +--- +# file: mariadb_cluster.yml + +- hosts: mariadb_cluster + become: true + roles: + - mariadb_cluster diff --git a/roles/mariadb_cluster/defaults/main.yml b/roles/mariadb_cluster/defaults/main.yml new file mode 100644 index 0000000..70b9b51 --- /dev/null +++ b/roles/mariadb_cluster/defaults/main.yml @@ -0,0 +1,5 @@ +--- +# file: roles/mariadb_cluster/defaults/main.yml + +mariadb_cluster_wsrep_cluster_name: "my_wsrep_cluster" +mariadb_cluster_access_ip: "" diff --git a/roles/mariadb_cluster/handlers/main.yml b/roles/mariadb_cluster/handlers/main.yml new file mode 100644 index 0000000..f21ee31 --- /dev/null +++ b/roles/mariadb_cluster/handlers/main.yml @@ -0,0 +1,19 @@ +--- +# file: roles/mariadb_cluster/handlers/main.yml + +- name: Daemon Reload + systemd: + daemon_reload: yes + +- name: Restart mariadb + service: + name: mariadb + state: restarted + +- name: Reload firewalld + service: + name: firewalld + state: reloaded + +- name: Bootstrap Galera + include_tasks: tasks/bootstrap-galera.yml diff --git a/roles/mariadb_cluster/tasks/bootstrap-galera.yml b/roles/mariadb_cluster/tasks/bootstrap-galera.yml new file mode 100644 index 0000000..d625e63 --- /dev/null +++ b/roles/mariadb_cluster/tasks/bootstrap-galera.yml @@ -0,0 +1,14 @@ +--- +# file: roles/mariadb_cluster/tasks/bootstrap-galera.yml + +- name: Stop MariaDB on first MariaDB Galera cluster node + service: name=mariadb state=stopped + when: inventory_hostname == groups['mariadb_cluster'][0] + +- name: Bootstrap first MariaDB Galera cluster node + command: galera_new_cluster + when: inventory_hostname == groups['mariadb_cluster'][0] + +- name: Restart the other MariaDB Galera cluster nodes + service: name=mariadb state=restarted + when: inventory_hostname != groups['mariadb_cluster'][0] diff --git a/roles/mariadb_cluster/tasks/main.yml b/roles/mariadb_cluster/tasks/main.yml new file mode 100644 index 0000000..0b3e10b --- /dev/null +++ b/roles/mariadb_cluster/tasks/main.yml @@ -0,0 +1,76 @@ +--- +# file: roles/mariadb_cluster/tasks/main.yml + +- name: Create variable of other members IPs to be included into the cluster + set_fact: nodelist={%for host in groups['mariadb_cluster']|difference([inventory_hostname])%}{{hostvars[host].ansible_host}}{% if not loop.last %},{% endif %}{% endfor %} + +- name: Install prereq packages + package: + name: + - mariadb-server-galera + - mariadb-server + - galera + state: latest + +- name: Update galera config + template: + src: "galera.cnf.j2" + dest: "/etc/my.cnf.d/galera.cnf" + notify: Bootstrap Galera + +- name: Enable firewall rule for MySQL access + firewalld: + port: 3306/tcp + permanent: yes + immediate: yes + state: enabled + notify: Reload firewalld + when: mariadb_cluster_access_ip == "" + +- name: "Enable firewall rule for MySQL access to Access IP" + firewalld: + rich_rule: 'rule family="ipv4" source address="{{ mariadb_cluster_access_ip }}" port port="3306" protocol="tcp" accept' + permanent: yes + state: enabled + immediate: yes + notify: Reload firewalld + when: mariadb_cluster_access_ip != "" + +- name: Setup access for other servers + include_tasks: setup-server.yml + loop: "{{ groups['mariadb_cluster']|difference([inventory_hostname]) }}" + loop_control: + extended: yes + +- name: Place mariadb-fail script + template: + src: "mariadb-fail.j2" + dest: "/usr/local/sbin/mariadb-fail" + owner: root + mode: 755 + +- name: Deploy mariadb-fail service + template: + src: mariadb-fail.service.j2 + dest: /etc/systemd/system/mariadb-fail.service + notify: Daemon Reload + +- name: Create directory for mariadb override + file: + path: "/etc/systemd/system/mariadb.service.d" + state: directory + +- name: Deploy mariadb override + template: + src: mariadb-override.conf.j2 + dest: /etc/systemd/system/mariadb.service.d/override.conf + notify: Daemon Reload + +- name: Flush handlers + meta: flush_handlers + +- name: Start and enable mariadb + service: + name: mariadb + state: started + enabled: yes diff --git a/roles/mariadb_cluster/tasks/setup-server.yml b/roles/mariadb_cluster/tasks/setup-server.yml new file mode 100644 index 0000000..327fa9f --- /dev/null +++ b/roles/mariadb_cluster/tasks/setup-server.yml @@ -0,0 +1,34 @@ +--- +# file: roles/mariadb_cluster/tasks/setup-server.yml + +- name: "{{ hostvars[item]['ansible_hostname'] }} - Enable firewall rule for Galera cluster replication traffic (TCP)" + firewalld: + rich_rule: 'rule family="ipv4" source address="{{ hostvars[item]["ansible_host"] }}" port port="4567" protocol="tcp" accept' + permanent: yes + state: enabled + immediate: yes + notify: Reload firewalld + +- name: "{{ hostvars[item]['ansible_hostname'] }} - Enable firewall rule for Galera cluster replication traffic (UDP)" + firewalld: + rich_rule: 'rule family="ipv4" source address="{{ hostvars[item]["ansible_host"] }}" port port="4567" protocol="udp" accept' + permanent: yes + state: enabled + immediate: yes + notify: Reload firewalld + +- name: "{{ hostvars[item]['ansible_hostname'] }} - Enable firewall rule for Incremental State Transfer" + firewalld: + rich_rule: 'rule family="ipv4" source address="{{ hostvars[item]["ansible_host"] }}" port port="4568" protocol="tcp" accept' + permanent: yes + state: enabled + immediate: yes + notify: Reload firewalld + +- name: "{{ hostvars[item]['ansible_hostname'] }} - Enable firewall rule for State Snapshot Transfer" + firewalld: + rich_rule: 'rule family="ipv4" source address="{{ hostvars[item]["ansible_host"] }}" port port="4444" protocol="tcp" accept' + permanent: yes + state: enabled + immediate: yes + notify: Reload firewalld diff --git a/roles/mariadb_cluster/templates/galera.cnf.j2 b/roles/mariadb_cluster/templates/galera.cnf.j2 new file mode 100644 index 0000000..acc6f05 --- /dev/null +++ b/roles/mariadb_cluster/templates/galera.cnf.j2 @@ -0,0 +1,125 @@ +# This file contains wsrep-related mysqld options. It should be included +# in the main MySQL configuration file. +# +# Options that need to be customized: +# - wsrep_provider +# - wsrep_cluster_address +# - wsrep_sst_auth +# The rest of defaults should work out of the box. + +## +## mysqld options _MANDATORY_ for correct opration of the cluster +## +[mysqld] + +# (This must be substituted by wsrep_format) +binlog_format=ROW + +# Currently only InnoDB storage engine is supported +default-storage-engine=innodb + +# to avoid issues with 'bulk mode inserts' using autoinc +innodb_autoinc_lock_mode=2 + +# Override bind-address +# In some systems bind-address defaults to 127.0.0.1, and with mysqldump SST +# it will have (most likely) disastrous consequences on donor node +bind-address={{ ansible_host }} + +## +## WSREP options +## + +# Enable wsrep +wsrep_on=1 + +# Full path to wsrep provider library or 'none' +wsrep_provider=/usr/lib64/galera/libgalera_smm.so + +# Provider specific configuration options +#wsrep_provider_options= + +# Logical cluster name. Should be the same for all nodes. +wsrep_cluster_name="{{ mariadb_cluster_wsrep_cluster_name }}" + +# Group communication system handle +wsrep_cluster_address="gcomm://{{ nodelist }}" + +# Human-readable node name (non-unique). Hostname by default. +#wsrep_node_name= + +# Base replication [:port] of the node. +# The values supplied will be used as defaults for state transfer receiving, +# listening ports and so on. Default: address of the first network interface. +#wsrep_node_address= + +# Address for incoming client connections. Autodetect by default. +#wsrep_node_incoming_address= + +# How many threads will process writesets from other nodes +wsrep_slave_threads=1 + +# DBUG options for wsrep provider +#wsrep_dbug_option + +# Generate fake primary keys for non-PK tables (required for multi-master +# and parallel applying operation) +wsrep_certify_nonPK=1 + +# Maximum number of rows in write set +wsrep_max_ws_rows=0 + +# Maximum size of write set +wsrep_max_ws_size=2147483647 + +# to enable debug level logging, set this to 1 +wsrep_debug=0 + +# convert locking sessions into transactions +wsrep_convert_LOCK_to_trx=0 + +# how many times to retry deadlocked autocommits +wsrep_retry_autocommit=1 + +# change auto_increment_increment and auto_increment_offset automatically +wsrep_auto_increment_control=1 + +# retry autoinc insert, which failed for duplicate key error +wsrep_drupal_282555_workaround=0 + +# enable "strictly synchronous" semantics for read operations +wsrep_causal_reads=0 + +# Command to call when node status or cluster membership changes. +# Will be passed all or some of the following options: +# --status - new status of this node +# --uuid - UUID of the cluster +# --primary - whether the component is primary or not ("yes"/"no") +# --members - comma-separated list of members +# --index - index of this node in the list +wsrep_notify_cmd= + +## +## WSREP State Transfer options +## + +# State Snapshot Transfer method +wsrep_sst_method=rsync + +# Address which donor should send State Snapshot to. +# Should be the address of THIS node. DON'T SET IT TO DONOR ADDRESS!!! +# (SST method dependent. Defaults to the first IP of the first interface) +#wsrep_sst_receive_address= + +# SST authentication string. This will be used to send SST to joining nodes. +# Depends on SST method. For mysqldump method it is root: +wsrep_sst_auth=root: + +# Desired SST donor name. +#wsrep_sst_donor= + +# Reject client queries when donating SST (false) +#wsrep_sst_donor_rejects_queries=0 + +# Protocol version to use +# wsrep_protocol_version= diff --git a/roles/mariadb_cluster/templates/mariadb-fail.j2 b/roles/mariadb_cluster/templates/mariadb-fail.j2 new file mode 100644 index 0000000..a178d22 --- /dev/null +++ b/roles/mariadb_cluster/templates/mariadb-fail.j2 @@ -0,0 +1,16 @@ +#!/bin/bash + +if tail /var/log/mariadb/mariadb.log | grep -xq ".*\[ERROR\] WSREP\: wsrep\:\:connect.* failed: 7" +then + if grep -Fxq "safe_to_bootstrap: 1" /var/lib/mysql/grastate.dat + then + echo "Running bootstrap" + systemctl reset-failed mariadb + galera_new_cluster + else + echo "Not safe to bootstrap waiting to restart mariadb" + sleep 120 + systemctl reset-failed mariadb + systemctl restart mariadb + fi +fi diff --git a/roles/mariadb_cluster/templates/mariadb-fail.service.j2 b/roles/mariadb_cluster/templates/mariadb-fail.service.j2 new file mode 100644 index 0000000..717aadf --- /dev/null +++ b/roles/mariadb_cluster/templates/mariadb-fail.service.j2 @@ -0,0 +1,5 @@ +[Unit] +Description=MariaDB Fail Service +[Service] +Type=oneshot +ExecStart="/usr/local/sbin/mariadb-fail" diff --git a/roles/mariadb_cluster/templates/mariadb-override.conf.j2 b/roles/mariadb_cluster/templates/mariadb-override.conf.j2 new file mode 100644 index 0000000..f85cfd6 --- /dev/null +++ b/roles/mariadb_cluster/templates/mariadb-override.conf.j2 @@ -0,0 +1,8 @@ +[Unit] +OnFailure=mariadb-fail.service +StartLimitIntervalSec=120s +StartLimitBurst=2 + +[Service] +Restart=on-failure +RestartSec=10s \ No newline at end of file diff --git a/site.yml b/site.yml new file mode 100644 index 0000000..ce6040b --- /dev/null +++ b/site.yml @@ -0,0 +1,4 @@ +--- +## This playbook deploys the whole application stack in this site. + +- import_playbook: mariadb_cluster.yml