Skip to content

Commit d9c2595

Browse files
tymonxgrooverdan
authored andcommitted
Support Galera Replication
This patch add support for Galera replication. Features: - It detects if Galera replication was enabled wsrep_on=ON - By default it enables cluster auto bootstrap feature - By default the first cluster node is used for cluster auto bootstrapping based on the wsrep_cluster_address parameter or by setting the `WSREP_CLUSTER_ADDRESS` environment variable - cluster auto bootstrap feature can be disabled by setting the `WSREP_SKIP_AUTO_BOOTSTRAP` environment variable - use the `WSREP_AUTO_BOOTSTRAP_ADDRESS` environment variable to explicitly choice other node for cluster bootstrapping - cluster node hostnames or IP addresses must be valid to enable cluster auto bootstrapping How to use it. 1. Prepare MariaDB configuration file `galera.cnf`: ```plaintext [galera] wsrep_on = ON wsrep_sst_method = mariabackup wsrep_provider = /usr/lib/libgalera_smm.so binlog_format = row default_storage_engine = InnoDB innodb_doublewrite = 1 innodb_autoinc_lock_mode = 2 ``` 2. Make it read-only: ```plaintext chmod 444 galera.cnf ``` 3. Prepare Docker Compose file `docker-compose.yml`: ```yaml services: node: image: mariadb restart: always security_opt: - label=disable environment: WSREP_CLUSTER_ADDRESS: "${WSREP_CLUSTER_ADDRESS:-}" MARIADB_ROOT_PASSWORD: example volumes: - ./galera.cnf:/etc/mysql/conf.d/10-galera.cnf:ro command: - --wsrep-cluster-address=gcomm://db_node_1,db_node_2,db_node_3 deploy: replicas: 3 ``` 4. Start Docker Compose: ```plaintext docker-compose --project-name db up ``` To start N MariaDB instances using environment variable: ```plaintext WSREP_CLUSTER_ADDRESS="gcomm://db_node_1,db_node_2,db_node_3,db_node_4,db_node_5" docker-compose --project-name db up --scale node="$(echo "${WSREP_CLUSTER_ADDRESS}" | tr ',' ' ' | wc -w)" ``` To start N MariaDB instances using MariaDB configuration file: ```plaintext docker-compose --project-name db up --scale node="$(grep -i wsrep_cluster_address <name>.cnf | tr -d ' ' | tr ',' ' ' | wc -w)" ``` Closes: #28
1 parent 5c9b0dd commit d9c2595

File tree

8 files changed

+608
-0
lines changed

8 files changed

+608
-0
lines changed

10.2/docker-entrypoint.sh

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,62 @@ _check_if_upgrade_is_needed() {
438438
return 1
439439
}
440440

441+
# usage: docker_hostname_match <hostname>
442+
# ie: docker_hostname_match node1.cluster.local
443+
# it returns true if provided hostname match with container hostname. Otherwise it returns false
444+
docker_hostname_match() {
445+
for hostname in $(hostname --all-fqdns) $(hostname --alias); do
446+
if [ "$hostname" = "$1" ]; then
447+
return 0
448+
fi
449+
done
450+
451+
return 1
452+
}
453+
454+
# usage: docker_ip_match <ip>
455+
# ie: docker_ip_match 192.168.1.13
456+
# it returns true if provided IP address match with container IP address. Otherwise it returns false
457+
docker_ip_match() {
458+
for ip in $(hostname --all-ip-addresses); do
459+
if [ "$ip" = "$1" ]; then
460+
return 0
461+
fi
462+
done
463+
464+
return 1
465+
}
466+
467+
# usage: docker_address_match <ip|hostname>
468+
# ie: docker_address_match node1
469+
# it returns true if provided value match with container IP address or container hostname. Otherwise it returns false
470+
docker_address_match() {
471+
local resolved
472+
resolved="$(resolveip --silent "$1" 2>/dev/null)" # it converts hostname to ip or vice versa
473+
474+
docker_ip_match "$resolved" || docker_ip_match "$1" || docker_hostname_match "$resolved" || docker_hostname_match "$1"
475+
}
476+
477+
# usage: wsrep_enable_new_cluster <wsrep-cluster-address> [arg [arg [...]]]
478+
# ie: wsrep_enable_new_cluster gcomm://node1,node2,node3 "$@"
479+
# it returns true if we need to set the --wsrep-new-cluster argument for the mysqld. Otherwise it returns false
480+
wsrep_enable_new_cluster() {
481+
local address="${WSREP_AUTO_BOOTSTRAP_ADDRESS:-$1}"
482+
shift
483+
local wsrepdir
484+
wsrepdir="$(mysql_get_config 'wsrep-data-home-dir' "$@")"
485+
486+
# it removes URI schemes like gcomm://
487+
address="${address#[[:graph:]]*://}"
488+
# Removing trailing options after literal "?"
489+
address="${address%%\?*}"
490+
491+
# it replaces commas ',' with spaces ' ' and converts it to array
492+
IFS=" ," read -r -a address <<< "$address"
493+
494+
(( ${#address[@]} )) && [ -z "$WSREP_SKIP_AUTO_BOOTSTRAP" ] && [ ! -s "$wsrepdir/gvwstate.dat" ] && docker_address_match "${address[0]}"
495+
}
496+
441497
# check arguments for an option that would cause mysqld to stop
442498
# return true if there is one
443499
_mysql_want_help() {
@@ -502,6 +558,26 @@ _main() {
502558
elif _check_if_upgrade_is_needed; then
503559
docker_mariadb_upgrade "$@"
504560
fi
561+
562+
# check if Galera replication is enabled from configuration files or command line arguments
563+
if [ "$(mysql_get_config wsrep-on "$@")" = "TRUE" ]; then
564+
mysql_note "Galera replication is enabled"
565+
566+
# determine cluster nodes addresses
567+
if [ -z "${WSREP_CLUSTER_ADDRESS}" ]; then
568+
WSREP_CLUSTER_ADDRESS="$(mysql_get_config wsrep-cluster-address "$@")"
569+
else
570+
set -- "$@" --wsrep-cluster-address="${WSREP_CLUSTER_ADDRESS}"
571+
fi
572+
573+
mysql_note "Galera cluster addresses ${WSREP_CLUSTER_ADDRESS}"
574+
575+
# determine if this node is used for cluster bootstrapping. Skip it when cluster was already bootstrapped
576+
if wsrep_enable_new_cluster "${WSREP_CLUSTER_ADDRESS}" "$@"; then
577+
mysql_note "Enabled Galera cluster bootstrapping for this node"
578+
set -- "$@" --wsrep-new-cluster
579+
fi
580+
fi
505581
fi
506582
exec "$@"
507583
}

10.3/docker-entrypoint.sh

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,62 @@ _check_if_upgrade_is_needed() {
438438
return 1
439439
}
440440

441+
# usage: docker_hostname_match <hostname>
442+
# ie: docker_hostname_match node1.cluster.local
443+
# it returns true if provided hostname match with container hostname. Otherwise it returns false
444+
docker_hostname_match() {
445+
for hostname in $(hostname --all-fqdns) $(hostname --alias); do
446+
if [ "$hostname" = "$1" ]; then
447+
return 0
448+
fi
449+
done
450+
451+
return 1
452+
}
453+
454+
# usage: docker_ip_match <ip>
455+
# ie: docker_ip_match 192.168.1.13
456+
# it returns true if provided IP address match with container IP address. Otherwise it returns false
457+
docker_ip_match() {
458+
for ip in $(hostname --all-ip-addresses); do
459+
if [ "$ip" = "$1" ]; then
460+
return 0
461+
fi
462+
done
463+
464+
return 1
465+
}
466+
467+
# usage: docker_address_match <ip|hostname>
468+
# ie: docker_address_match node1
469+
# it returns true if provided value match with container IP address or container hostname. Otherwise it returns false
470+
docker_address_match() {
471+
local resolved
472+
resolved="$(resolveip --silent "$1" 2>/dev/null)" # it converts hostname to ip or vice versa
473+
474+
docker_ip_match "$resolved" || docker_ip_match "$1" || docker_hostname_match "$resolved" || docker_hostname_match "$1"
475+
}
476+
477+
# usage: wsrep_enable_new_cluster <wsrep-cluster-address> [arg [arg [...]]]
478+
# ie: wsrep_enable_new_cluster gcomm://node1,node2,node3 "$@"
479+
# it returns true if we need to set the --wsrep-new-cluster argument for the mysqld. Otherwise it returns false
480+
wsrep_enable_new_cluster() {
481+
local address="${WSREP_AUTO_BOOTSTRAP_ADDRESS:-$1}"
482+
shift
483+
local wsrepdir
484+
wsrepdir="$(mysql_get_config 'wsrep-data-home-dir' "$@")"
485+
486+
# it removes URI schemes like gcomm://
487+
address="${address#[[:graph:]]*://}"
488+
# Removing trailing options after literal "?"
489+
address="${address%%\?*}"
490+
491+
# it replaces commas ',' with spaces ' ' and converts it to array
492+
IFS=" ," read -r -a address <<< "$address"
493+
494+
(( ${#address[@]} )) && [ -z "$WSREP_SKIP_AUTO_BOOTSTRAP" ] && [ ! -s "$wsrepdir/gvwstate.dat" ] && docker_address_match "${address[0]}"
495+
}
496+
441497
# check arguments for an option that would cause mysqld to stop
442498
# return true if there is one
443499
_mysql_want_help() {
@@ -502,6 +558,26 @@ _main() {
502558
elif _check_if_upgrade_is_needed; then
503559
docker_mariadb_upgrade "$@"
504560
fi
561+
562+
# check if Galera replication is enabled from configuration files or command line arguments
563+
if [ "$(mysql_get_config wsrep-on "$@")" = "TRUE" ]; then
564+
mysql_note "Galera replication is enabled"
565+
566+
# determine cluster nodes addresses
567+
if [ -z "${WSREP_CLUSTER_ADDRESS}" ]; then
568+
WSREP_CLUSTER_ADDRESS="$(mysql_get_config wsrep-cluster-address "$@")"
569+
else
570+
set -- "$@" --wsrep-cluster-address="${WSREP_CLUSTER_ADDRESS}"
571+
fi
572+
573+
mysql_note "Galera cluster addresses ${WSREP_CLUSTER_ADDRESS}"
574+
575+
# determine if this node is used for cluster bootstrapping. Skip it when cluster was already bootstrapped
576+
if wsrep_enable_new_cluster "${WSREP_CLUSTER_ADDRESS}" "$@"; then
577+
mysql_note "Enabled Galera cluster bootstrapping for this node"
578+
set -- "$@" --wsrep-new-cluster
579+
fi
580+
fi
505581
fi
506582
exec "$@"
507583
}

10.4/docker-entrypoint.sh

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,62 @@ _check_if_upgrade_is_needed() {
438438
return 1
439439
}
440440

441+
# usage: docker_hostname_match <hostname>
442+
# ie: docker_hostname_match node1.cluster.local
443+
# it returns true if provided hostname match with container hostname. Otherwise it returns false
444+
docker_hostname_match() {
445+
for hostname in $(hostname --all-fqdns) $(hostname --alias); do
446+
if [ "$hostname" = "$1" ]; then
447+
return 0
448+
fi
449+
done
450+
451+
return 1
452+
}
453+
454+
# usage: docker_ip_match <ip>
455+
# ie: docker_ip_match 192.168.1.13
456+
# it returns true if provided IP address match with container IP address. Otherwise it returns false
457+
docker_ip_match() {
458+
for ip in $(hostname --all-ip-addresses); do
459+
if [ "$ip" = "$1" ]; then
460+
return 0
461+
fi
462+
done
463+
464+
return 1
465+
}
466+
467+
# usage: docker_address_match <ip|hostname>
468+
# ie: docker_address_match node1
469+
# it returns true if provided value match with container IP address or container hostname. Otherwise it returns false
470+
docker_address_match() {
471+
local resolved
472+
resolved="$(resolveip --silent "$1" 2>/dev/null)" # it converts hostname to ip or vice versa
473+
474+
docker_ip_match "$resolved" || docker_ip_match "$1" || docker_hostname_match "$resolved" || docker_hostname_match "$1"
475+
}
476+
477+
# usage: wsrep_enable_new_cluster <wsrep-cluster-address> [arg [arg [...]]]
478+
# ie: wsrep_enable_new_cluster gcomm://node1,node2,node3 "$@"
479+
# it returns true if we need to set the --wsrep-new-cluster argument for the mysqld. Otherwise it returns false
480+
wsrep_enable_new_cluster() {
481+
local address="${WSREP_AUTO_BOOTSTRAP_ADDRESS:-$1}"
482+
shift
483+
local wsrepdir
484+
wsrepdir="$(mysql_get_config 'wsrep-data-home-dir' "$@")"
485+
486+
# it removes URI schemes like gcomm://
487+
address="${address#[[:graph:]]*://}"
488+
# Removing trailing options after literal "?"
489+
address="${address%%\?*}"
490+
491+
# it replaces commas ',' with spaces ' ' and converts it to array
492+
IFS=" ," read -r -a address <<< "$address"
493+
494+
(( ${#address[@]} )) && [ -z "$WSREP_SKIP_AUTO_BOOTSTRAP" ] && [ ! -s "$wsrepdir/gvwstate.dat" ] && docker_address_match "${address[0]}"
495+
}
496+
441497
# check arguments for an option that would cause mysqld to stop
442498
# return true if there is one
443499
_mysql_want_help() {
@@ -502,6 +558,26 @@ _main() {
502558
elif _check_if_upgrade_is_needed; then
503559
docker_mariadb_upgrade "$@"
504560
fi
561+
562+
# check if Galera replication is enabled from configuration files or command line arguments
563+
if [ "$(mysql_get_config wsrep-on "$@")" = "TRUE" ]; then
564+
mysql_note "Galera replication is enabled"
565+
566+
# determine cluster nodes addresses
567+
if [ -z "${WSREP_CLUSTER_ADDRESS}" ]; then
568+
WSREP_CLUSTER_ADDRESS="$(mysql_get_config wsrep-cluster-address "$@")"
569+
else
570+
set -- "$@" --wsrep-cluster-address="${WSREP_CLUSTER_ADDRESS}"
571+
fi
572+
573+
mysql_note "Galera cluster addresses ${WSREP_CLUSTER_ADDRESS}"
574+
575+
# determine if this node is used for cluster bootstrapping. Skip it when cluster was already bootstrapped
576+
if wsrep_enable_new_cluster "${WSREP_CLUSTER_ADDRESS}" "$@"; then
577+
mysql_note "Enabled Galera cluster bootstrapping for this node"
578+
set -- "$@" --wsrep-new-cluster
579+
fi
580+
fi
505581
fi
506582
exec "$@"
507583
}

10.5/docker-entrypoint.sh

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,62 @@ _check_if_upgrade_is_needed() {
438438
return 1
439439
}
440440

441+
# usage: docker_hostname_match <hostname>
442+
# ie: docker_hostname_match node1.cluster.local
443+
# it returns true if provided hostname match with container hostname. Otherwise it returns false
444+
docker_hostname_match() {
445+
for hostname in $(hostname --all-fqdns) $(hostname --alias); do
446+
if [ "$hostname" = "$1" ]; then
447+
return 0
448+
fi
449+
done
450+
451+
return 1
452+
}
453+
454+
# usage: docker_ip_match <ip>
455+
# ie: docker_ip_match 192.168.1.13
456+
# it returns true if provided IP address match with container IP address. Otherwise it returns false
457+
docker_ip_match() {
458+
for ip in $(hostname --all-ip-addresses); do
459+
if [ "$ip" = "$1" ]; then
460+
return 0
461+
fi
462+
done
463+
464+
return 1
465+
}
466+
467+
# usage: docker_address_match <ip|hostname>
468+
# ie: docker_address_match node1
469+
# it returns true if provided value match with container IP address or container hostname. Otherwise it returns false
470+
docker_address_match() {
471+
local resolved
472+
resolved="$(resolveip --silent "$1" 2>/dev/null)" # it converts hostname to ip or vice versa
473+
474+
docker_ip_match "$resolved" || docker_ip_match "$1" || docker_hostname_match "$resolved" || docker_hostname_match "$1"
475+
}
476+
477+
# usage: wsrep_enable_new_cluster <wsrep-cluster-address> [arg [arg [...]]]
478+
# ie: wsrep_enable_new_cluster gcomm://node1,node2,node3 "$@"
479+
# it returns true if we need to set the --wsrep-new-cluster argument for the mysqld. Otherwise it returns false
480+
wsrep_enable_new_cluster() {
481+
local address="${WSREP_AUTO_BOOTSTRAP_ADDRESS:-$1}"
482+
shift
483+
local wsrepdir
484+
wsrepdir="$(mysql_get_config 'wsrep-data-home-dir' "$@")"
485+
486+
# it removes URI schemes like gcomm://
487+
address="${address#[[:graph:]]*://}"
488+
# Removing trailing options after literal "?"
489+
address="${address%%\?*}"
490+
491+
# it replaces commas ',' with spaces ' ' and converts it to array
492+
IFS=" ," read -r -a address <<< "$address"
493+
494+
(( ${#address[@]} )) && [ -z "$WSREP_SKIP_AUTO_BOOTSTRAP" ] && [ ! -s "$wsrepdir/gvwstate.dat" ] && docker_address_match "${address[0]}"
495+
}
496+
441497
# check arguments for an option that would cause mysqld to stop
442498
# return true if there is one
443499
_mysql_want_help() {
@@ -502,6 +558,26 @@ _main() {
502558
elif _check_if_upgrade_is_needed; then
503559
docker_mariadb_upgrade "$@"
504560
fi
561+
562+
# check if Galera replication is enabled from configuration files or command line arguments
563+
if [ "$(mysql_get_config wsrep-on "$@")" = "TRUE" ]; then
564+
mysql_note "Galera replication is enabled"
565+
566+
# determine cluster nodes addresses
567+
if [ -z "${WSREP_CLUSTER_ADDRESS}" ]; then
568+
WSREP_CLUSTER_ADDRESS="$(mysql_get_config wsrep-cluster-address "$@")"
569+
else
570+
set -- "$@" --wsrep-cluster-address="${WSREP_CLUSTER_ADDRESS}"
571+
fi
572+
573+
mysql_note "Galera cluster addresses ${WSREP_CLUSTER_ADDRESS}"
574+
575+
# determine if this node is used for cluster bootstrapping. Skip it when cluster was already bootstrapped
576+
if wsrep_enable_new_cluster "${WSREP_CLUSTER_ADDRESS}" "$@"; then
577+
mysql_note "Enabled Galera cluster bootstrapping for this node"
578+
set -- "$@" --wsrep-new-cluster
579+
fi
580+
fi
505581
fi
506582
exec "$@"
507583
}

0 commit comments

Comments
 (0)