From 3731993f6a1a2d0d594b13b190039c666c06a14f Mon Sep 17 00:00:00 2001 From: Jens Harbott Date: Fri, 22 Dec 2017 11:14:58 +0000 Subject: [PATCH] Add receipes for mariadb cluster deployment After removal of the percona cluster receipes, add support for deploying a galera-based mariadb cluster instead. Change-Id: I7e4d0fc804be242302d2e7b7202159d4b7ba8c97 --- files/default/clustercheck | 106 ++++++++++++++++++++++++++++++ metadata.rb | 2 + recipes/mariadb-cluster-client.rb | 19 ++++++ recipes/mariadb-cluster-server.rb | 66 +++++++++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 files/default/clustercheck create mode 100644 recipes/mariadb-cluster-client.rb create mode 100644 recipes/mariadb-cluster-server.rb diff --git a/files/default/clustercheck b/files/default/clustercheck new file mode 100644 index 0000000..f37ab45 --- /dev/null +++ b/files/default/clustercheck @@ -0,0 +1,106 @@ +#!/bin/bash +# +# Script to make a proxy (ie HAProxy) capable of monitoring MariaDB Cluster nodes properly +# +# Author: Olaf van Zandwijk +# Author: Raghavendra Prabhu +# +# Documentation and download: https://github.com/olafz/percona-clustercheck +# +# Based on the original script from Unai Rodriguez +# +# Updated for MariaDB by: Jens Harbott +# + +if [[ $1 == '-h' || $1 == '--help' ]];then + echo "Usage: $0 " + exit +fi + +# if the disabled file is present, return 503. This allows +# admins to manually remove a node from a cluster easily. +if [ -e "/var/tmp/clustercheck.disabled" ]; then + # Shell return-code is 1 + echo -en "HTTP/1.1 503 Service Unavailable\r\n" + echo -en "Content-Type: text/plain\r\n" + echo -en "Connection: close\r\n" + echo -en "Content-Length: 51\r\n" + echo -en "\r\n" + echo -en "MariaDB Cluster Node is manually disabled.\r\n" + sleep 0.1 + exit 1 +fi + +MYSQL_USERNAME="${1-clustercheckuser}" +MYSQL_PASSWORD="${2-clustercheckpassword!}" +AVAILABLE_WHEN_DONOR=${3:-0} +ERR_FILE="${4:-/dev/null}" +AVAILABLE_WHEN_READONLY=${5:-1} +DEFAULTS_EXTRA_FILE=${6:-/etc/my.cnf} + +#Timeout exists for instances where mysqld may be hung +TIMEOUT=10 + +EXTRA_ARGS="" +if [[ -n "$MYSQL_USERNAME" ]]; then + EXTRA_ARGS="$EXTRA_ARGS --user=${MYSQL_USERNAME}" +fi +if [[ -n "$MYSQL_PASSWORD" ]]; then + EXTRA_ARGS="$EXTRA_ARGS --password=${MYSQL_PASSWORD}" +fi +if [[ -r $DEFAULTS_EXTRA_FILE ]];then + MYSQL_CMDLINE="mysql --defaults-extra-file=$DEFAULTS_EXTRA_FILE -nNE --connect-timeout=$TIMEOUT \ + ${EXTRA_ARGS}" +else + MYSQL_CMDLINE="mysql -nNE --connect-timeout=$TIMEOUT ${EXTRA_ARGS}" +fi +# +# Perform the query to check the wsrep_local_state +# +WSREP_STATUS=$($MYSQL_CMDLINE -e "SHOW STATUS LIKE 'wsrep_local_state';" \ + 2>${ERR_FILE} | tail -1 2>>${ERR_FILE}) + +if [[ "${WSREP_STATUS}" == "4" ]] || [[ "${WSREP_STATUS}" == "2" && ${AVAILABLE_WHEN_DONOR} == 1 ]] +then + # Check only when set to 0 to avoid latency in response. + if [[ $AVAILABLE_WHEN_READONLY -eq 0 ]];then + READ_ONLY=$($MYSQL_CMDLINE -e "SHOW GLOBAL VARIABLES LIKE 'read_only';" \ + 2>${ERR_FILE} | tail -1 2>>${ERR_FILE}) + + if [[ "${READ_ONLY}" == "ON" ]];then + # MariaDB Cluster node local state is 'Synced', but it is in + # read-only mode. The variable AVAILABLE_WHEN_READONLY is set to 0. + # => return HTTP 503 + # Shell return-code is 1 + echo -en "HTTP/1.1 503 Service Unavailable\r\n" + echo -en "Content-Type: text/plain\r\n" + echo -en "Connection: close\r\n" + echo -en "Content-Length: 43\r\n" + echo -en "\r\n" + echo -en "MariaDB Cluster Node is read-only.\r\n" + sleep 0.1 + exit 1 + fi + fi + # MariaDB Cluster node local state is 'Synced' => return HTTP 200 + # Shell return-code is 0 + echo -en "HTTP/1.1 200 OK\r\n" + echo -en "Content-Type: text/plain\r\n" + echo -en "Connection: close\r\n" + echo -en "Content-Length: 40\r\n" + echo -en "\r\n" + echo -en "MariaDB Cluster Node is synced.\r\n" + sleep 0.1 + exit 0 +else + # MariaDB Cluster node local state is not 'Synced' => return HTTP 503 + # Shell return-code is 1 + echo -en "HTTP/1.1 503 Service Unavailable\r\n" + echo -en "Content-Type: text/plain\r\n" + echo -en "Connection: close\r\n" + echo -en "Content-Length: 44\r\n" + echo -en "\r\n" + echo -en "MariaDB Cluster Node is not synced.\r\n" + sleep 0.1 + exit 1 +fi diff --git a/metadata.rb b/metadata.rb index 4b03ec0..218395c 100755 --- a/metadata.rb +++ b/metadata.rb @@ -11,6 +11,8 @@ recipe 'mysql-client', 'Installs MySQL client packages.' recipe 'mysql-server', 'Installs and configures MySQL server packages.' recipe 'mariadb-client', 'Installs MariaDB client packages.' recipe 'mariadb-server', 'Installs and configures MariaDB server packages.' +recipe 'mariadb-cluster-client', 'Installs MariaDB Cluster client packages.' +recipe 'mariadb-cluster-server', 'Installs and configures MariaDB Cluster server packages.' recipe 'openstack-db', 'Creates necessary tables, users, and grants for OpenStack.' %w(ubuntu redhat centos).each do |os| diff --git a/recipes/mariadb-cluster-client.rb b/recipes/mariadb-cluster-client.rb new file mode 100644 index 0000000..232c43e --- /dev/null +++ b/recipes/mariadb-cluster-client.rb @@ -0,0 +1,19 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-ops-database +# Recipe:: mariadb-cluster-client +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe 'openstack-ops-database::mariadb-client' diff --git a/recipes/mariadb-cluster-server.rb b/recipes/mariadb-cluster-server.rb new file mode 100644 index 0000000..e8be276 --- /dev/null +++ b/recipes/mariadb-cluster-server.rb @@ -0,0 +1,66 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-ops-database +# Recipe:: mariadb-cluster-server +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class ::Chef::Recipe + include ::Openstack +end + +## INFO: to use this recipe, set node['openstack']['db']['service_type'] = 'mariadb-cluster' in your environment + +bind_db = node['openstack']['bind_service']['db'] +if bind_db['interface'] + listen_address = address_for bind_db['interface'] + node.normal['mariadb']['galera']['options']['port'] = bind_db['port'] +else + listen_address = bind_db['host'] + node.normal['mariadb']['galera']['options']['port'] = node['openstack']['endpoints']['db']['port'] +end +node.normal['mariadb']['galera']['options']['bind-address'] = listen_address + +## CLUSTER SPECIFIC CONFIG +node.normal['mariadb']['galera']['cluster_name'] = 'openstack' +node.normal['mariadb']['galera']['wsrep_provider_options']['gmcast.listen_addr'] = "tcp://#{listen_address}:4567" +### find all nodes in the mariadb cluster +cluster_nodes = search(:node, 'recipes:"openstack-ops-database\:\:mariadb-cluster-server"').sort +# if it's the first node make sure that wsrep_cluster_address is set to nothing to be able to bootstrap. +is_first_node = cluster_nodes.empty? || (cluster_nodes.size == 1 && cluster_nodes.first['fqdn'] == node['fqdn']) +if is_first_node + node.normal['mariadb']['galera']['gcomm_address'] = 'gcomm://' +else + # otherwise set the correct cluster address with all cluster nodes + family = node['openstack']['endpoints']['family'] + cluster_nodes_addresses = [] + cluster_nodes.each do |cluster_node| + address = address_for bind_db['interface'], family, cluster_node + cluster_nodes_addresses << address + end + cluster_address = cluster_nodes_addresses.join(',') + node.normal['mariadb']['galera']['gcomm_address'] = "gcomm://#{cluster_address}" +end + +include_recipe 'openstack-ops-database::mariadb-client' +include_recipe 'mariadb::galera' + +# Install clustercheck tool +cookbook_file '/usr/bin/clustercheck' do + source 'clustercheck' + owner 'root' + group 'root' + mode '0755' + action :create_if_missing +end