Skip to content

Commit

Permalink
Support Galera Replication
Browse files Browse the repository at this point in the history
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
  • Loading branch information
tymonx authored and grooverdan committed Feb 7, 2022
1 parent 3805a68 commit 5fbf4c6
Show file tree
Hide file tree
Showing 8 changed files with 608 additions and 0 deletions.
76 changes: 76 additions & 0 deletions 10.2/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,62 @@ _check_if_upgrade_is_needed() {
return 1
}

# usage: docker_hostname_match <hostname>
# ie: docker_hostname_match node1.cluster.local
# it returns true if provided hostname match with container hostname. Otherwise it returns false
docker_hostname_match() {
for hostname in $(hostname --all-fqdns) $(hostname --alias); do
if [ "$hostname" = "$1" ]; then
return 0
fi
done

return 1
}

# usage: docker_ip_match <ip>
# ie: docker_ip_match 192.168.1.13
# it returns true if provided IP address match with container IP address. Otherwise it returns false
docker_ip_match() {
for ip in $(hostname --all-ip-addresses); do
if [ "$ip" = "$1" ]; then
return 0
fi
done

return 1
}

# usage: docker_address_match <ip|hostname>
# ie: docker_address_match node1
# it returns true if provided value match with container IP address or container hostname. Otherwise it returns false
docker_address_match() {
local resolved
resolved="$(resolveip --silent "$1" 2>/dev/null)" # it converts hostname to ip or vice versa

docker_ip_match "$resolved" || docker_ip_match "$1" || docker_hostname_match "$resolved" || docker_hostname_match "$1"
}

# usage: wsrep_enable_new_cluster <wsrep-cluster-address> [arg [arg [...]]]
# ie: wsrep_enable_new_cluster gcomm://node1,node2,node3 "$@"
# it returns true if we need to set the --wsrep-new-cluster argument for the mysqld. Otherwise it returns false
wsrep_enable_new_cluster() {
local address="${WSREP_AUTO_BOOTSTRAP_ADDRESS:-$1}"
shift
local wsrepdir
wsrepdir="$(mysql_get_config 'wsrep-data-home-dir' "$@")"

# it removes URI schemes like gcomm://
address="${address#[[:graph:]]*://}"
# Removing trailing options after literal "?"
address="${address%%\?*}"

# it replaces commas ',' with spaces ' ' and converts it to array
IFS=" ," read -r -a address <<< "$address"

(( ${#address[@]} )) && [ -z "$WSREP_SKIP_AUTO_BOOTSTRAP" ] && [ ! -s "$wsrepdir/gvwstate.dat" ] && docker_address_match "${address[0]}"
}

# check arguments for an option that would cause mysqld to stop
# return true if there is one
_mysql_want_help() {
Expand Down Expand Up @@ -513,6 +569,26 @@ _main() {
elif _check_if_upgrade_is_needed; then
docker_mariadb_upgrade "$@"
fi

# check if Galera replication is enabled from configuration files or command line arguments
if [ "$(mysql_get_config wsrep-on "$@")" = "TRUE" ]; then
mysql_note "Galera replication is enabled"

# determine cluster nodes addresses
if [ -z "${WSREP_CLUSTER_ADDRESS}" ]; then
WSREP_CLUSTER_ADDRESS="$(mysql_get_config wsrep-cluster-address "$@")"
else
set -- "$@" --wsrep-cluster-address="${WSREP_CLUSTER_ADDRESS}"
fi

mysql_note "Galera cluster addresses ${WSREP_CLUSTER_ADDRESS}"

# determine if this node is used for cluster bootstrapping. Skip it when cluster was already bootstrapped
if wsrep_enable_new_cluster "${WSREP_CLUSTER_ADDRESS}" "$@"; then
mysql_note "Enabled Galera cluster bootstrapping for this node"
set -- "$@" --wsrep-new-cluster
fi
fi
fi
exec "$@"
}
Expand Down
76 changes: 76 additions & 0 deletions 10.3/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,62 @@ _check_if_upgrade_is_needed() {
return 1
}

# usage: docker_hostname_match <hostname>
# ie: docker_hostname_match node1.cluster.local
# it returns true if provided hostname match with container hostname. Otherwise it returns false
docker_hostname_match() {
for hostname in $(hostname --all-fqdns) $(hostname --alias); do
if [ "$hostname" = "$1" ]; then
return 0
fi
done

return 1
}

# usage: docker_ip_match <ip>
# ie: docker_ip_match 192.168.1.13
# it returns true if provided IP address match with container IP address. Otherwise it returns false
docker_ip_match() {
for ip in $(hostname --all-ip-addresses); do
if [ "$ip" = "$1" ]; then
return 0
fi
done

return 1
}

# usage: docker_address_match <ip|hostname>
# ie: docker_address_match node1
# it returns true if provided value match with container IP address or container hostname. Otherwise it returns false
docker_address_match() {
local resolved
resolved="$(resolveip --silent "$1" 2>/dev/null)" # it converts hostname to ip or vice versa

docker_ip_match "$resolved" || docker_ip_match "$1" || docker_hostname_match "$resolved" || docker_hostname_match "$1"
}

# usage: wsrep_enable_new_cluster <wsrep-cluster-address> [arg [arg [...]]]
# ie: wsrep_enable_new_cluster gcomm://node1,node2,node3 "$@"
# it returns true if we need to set the --wsrep-new-cluster argument for the mysqld. Otherwise it returns false
wsrep_enable_new_cluster() {
local address="${WSREP_AUTO_BOOTSTRAP_ADDRESS:-$1}"
shift
local wsrepdir
wsrepdir="$(mysql_get_config 'wsrep-data-home-dir' "$@")"

# it removes URI schemes like gcomm://
address="${address#[[:graph:]]*://}"
# Removing trailing options after literal "?"
address="${address%%\?*}"

# it replaces commas ',' with spaces ' ' and converts it to array
IFS=" ," read -r -a address <<< "$address"

(( ${#address[@]} )) && [ -z "$WSREP_SKIP_AUTO_BOOTSTRAP" ] && [ ! -s "$wsrepdir/gvwstate.dat" ] && docker_address_match "${address[0]}"
}

# check arguments for an option that would cause mysqld to stop
# return true if there is one
_mysql_want_help() {
Expand Down Expand Up @@ -513,6 +569,26 @@ _main() {
elif _check_if_upgrade_is_needed; then
docker_mariadb_upgrade "$@"
fi

# check if Galera replication is enabled from configuration files or command line arguments
if [ "$(mysql_get_config wsrep-on "$@")" = "TRUE" ]; then
mysql_note "Galera replication is enabled"

# determine cluster nodes addresses
if [ -z "${WSREP_CLUSTER_ADDRESS}" ]; then
WSREP_CLUSTER_ADDRESS="$(mysql_get_config wsrep-cluster-address "$@")"
else
set -- "$@" --wsrep-cluster-address="${WSREP_CLUSTER_ADDRESS}"
fi

mysql_note "Galera cluster addresses ${WSREP_CLUSTER_ADDRESS}"

# determine if this node is used for cluster bootstrapping. Skip it when cluster was already bootstrapped
if wsrep_enable_new_cluster "${WSREP_CLUSTER_ADDRESS}" "$@"; then
mysql_note "Enabled Galera cluster bootstrapping for this node"
set -- "$@" --wsrep-new-cluster
fi
fi
fi
exec "$@"
}
Expand Down
76 changes: 76 additions & 0 deletions 10.4/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,62 @@ _check_if_upgrade_is_needed() {
return 1
}

# usage: docker_hostname_match <hostname>
# ie: docker_hostname_match node1.cluster.local
# it returns true if provided hostname match with container hostname. Otherwise it returns false
docker_hostname_match() {
for hostname in $(hostname --all-fqdns) $(hostname --alias); do
if [ "$hostname" = "$1" ]; then
return 0
fi
done

return 1
}

# usage: docker_ip_match <ip>
# ie: docker_ip_match 192.168.1.13
# it returns true if provided IP address match with container IP address. Otherwise it returns false
docker_ip_match() {
for ip in $(hostname --all-ip-addresses); do
if [ "$ip" = "$1" ]; then
return 0
fi
done

return 1
}

# usage: docker_address_match <ip|hostname>
# ie: docker_address_match node1
# it returns true if provided value match with container IP address or container hostname. Otherwise it returns false
docker_address_match() {
local resolved
resolved="$(resolveip --silent "$1" 2>/dev/null)" # it converts hostname to ip or vice versa

docker_ip_match "$resolved" || docker_ip_match "$1" || docker_hostname_match "$resolved" || docker_hostname_match "$1"
}

# usage: wsrep_enable_new_cluster <wsrep-cluster-address> [arg [arg [...]]]
# ie: wsrep_enable_new_cluster gcomm://node1,node2,node3 "$@"
# it returns true if we need to set the --wsrep-new-cluster argument for the mysqld. Otherwise it returns false
wsrep_enable_new_cluster() {
local address="${WSREP_AUTO_BOOTSTRAP_ADDRESS:-$1}"
shift
local wsrepdir
wsrepdir="$(mysql_get_config 'wsrep-data-home-dir' "$@")"

# it removes URI schemes like gcomm://
address="${address#[[:graph:]]*://}"
# Removing trailing options after literal "?"
address="${address%%\?*}"

# it replaces commas ',' with spaces ' ' and converts it to array
IFS=" ," read -r -a address <<< "$address"

(( ${#address[@]} )) && [ -z "$WSREP_SKIP_AUTO_BOOTSTRAP" ] && [ ! -s "$wsrepdir/gvwstate.dat" ] && docker_address_match "${address[0]}"
}

# check arguments for an option that would cause mysqld to stop
# return true if there is one
_mysql_want_help() {
Expand Down Expand Up @@ -513,6 +569,26 @@ _main() {
elif _check_if_upgrade_is_needed; then
docker_mariadb_upgrade "$@"
fi

# check if Galera replication is enabled from configuration files or command line arguments
if [ "$(mysql_get_config wsrep-on "$@")" = "TRUE" ]; then
mysql_note "Galera replication is enabled"

# determine cluster nodes addresses
if [ -z "${WSREP_CLUSTER_ADDRESS}" ]; then
WSREP_CLUSTER_ADDRESS="$(mysql_get_config wsrep-cluster-address "$@")"
else
set -- "$@" --wsrep-cluster-address="${WSREP_CLUSTER_ADDRESS}"
fi

mysql_note "Galera cluster addresses ${WSREP_CLUSTER_ADDRESS}"

# determine if this node is used for cluster bootstrapping. Skip it when cluster was already bootstrapped
if wsrep_enable_new_cluster "${WSREP_CLUSTER_ADDRESS}" "$@"; then
mysql_note "Enabled Galera cluster bootstrapping for this node"
set -- "$@" --wsrep-new-cluster
fi
fi
fi
exec "$@"
}
Expand Down
76 changes: 76 additions & 0 deletions 10.5/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,62 @@ _check_if_upgrade_is_needed() {
return 1
}

# usage: docker_hostname_match <hostname>
# ie: docker_hostname_match node1.cluster.local
# it returns true if provided hostname match with container hostname. Otherwise it returns false
docker_hostname_match() {
for hostname in $(hostname --all-fqdns) $(hostname --alias); do
if [ "$hostname" = "$1" ]; then
return 0
fi
done

return 1
}

# usage: docker_ip_match <ip>
# ie: docker_ip_match 192.168.1.13
# it returns true if provided IP address match with container IP address. Otherwise it returns false
docker_ip_match() {
for ip in $(hostname --all-ip-addresses); do
if [ "$ip" = "$1" ]; then
return 0
fi
done

return 1
}

# usage: docker_address_match <ip|hostname>
# ie: docker_address_match node1
# it returns true if provided value match with container IP address or container hostname. Otherwise it returns false
docker_address_match() {
local resolved
resolved="$(resolveip --silent "$1" 2>/dev/null)" # it converts hostname to ip or vice versa

docker_ip_match "$resolved" || docker_ip_match "$1" || docker_hostname_match "$resolved" || docker_hostname_match "$1"
}

# usage: wsrep_enable_new_cluster <wsrep-cluster-address> [arg [arg [...]]]
# ie: wsrep_enable_new_cluster gcomm://node1,node2,node3 "$@"
# it returns true if we need to set the --wsrep-new-cluster argument for the mysqld. Otherwise it returns false
wsrep_enable_new_cluster() {
local address="${WSREP_AUTO_BOOTSTRAP_ADDRESS:-$1}"
shift
local wsrepdir
wsrepdir="$(mysql_get_config 'wsrep-data-home-dir' "$@")"

# it removes URI schemes like gcomm://
address="${address#[[:graph:]]*://}"
# Removing trailing options after literal "?"
address="${address%%\?*}"

# it replaces commas ',' with spaces ' ' and converts it to array
IFS=" ," read -r -a address <<< "$address"

(( ${#address[@]} )) && [ -z "$WSREP_SKIP_AUTO_BOOTSTRAP" ] && [ ! -s "$wsrepdir/gvwstate.dat" ] && docker_address_match "${address[0]}"
}

# check arguments for an option that would cause mysqld to stop
# return true if there is one
_mysql_want_help() {
Expand Down Expand Up @@ -513,6 +569,26 @@ _main() {
elif _check_if_upgrade_is_needed; then
docker_mariadb_upgrade "$@"
fi

# check if Galera replication is enabled from configuration files or command line arguments
if [ "$(mysql_get_config wsrep-on "$@")" = "TRUE" ]; then
mysql_note "Galera replication is enabled"

# determine cluster nodes addresses
if [ -z "${WSREP_CLUSTER_ADDRESS}" ]; then
WSREP_CLUSTER_ADDRESS="$(mysql_get_config wsrep-cluster-address "$@")"
else
set -- "$@" --wsrep-cluster-address="${WSREP_CLUSTER_ADDRESS}"
fi

mysql_note "Galera cluster addresses ${WSREP_CLUSTER_ADDRESS}"

# determine if this node is used for cluster bootstrapping. Skip it when cluster was already bootstrapped
if wsrep_enable_new_cluster "${WSREP_CLUSTER_ADDRESS}" "$@"; then
mysql_note "Enabled Galera cluster bootstrapping for this node"
set -- "$@" --wsrep-new-cluster
fi
fi
fi
exec "$@"
}
Expand Down
Loading

0 comments on commit 5fbf4c6

Please sign in to comment.