Skip to content

Commit

Permalink
It fixes MariaDB#28 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 using `mysql`
  configuration files or provided `mysqld` command line arguments
- on default it enables cluster auto bootstrap feature
- on default the first cluster node is used for cluster auto bootstrapping
  based on the wsrep_cluster_address parameter from `mysql`
  configuration files, `mysqld` command line arguments 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 `mysql` configuration file `galera.cnf`:

```plaintext
[galera]
wsrep_on                       = ON
wsrep_sst_method               = rsync
wsrep_provider                 = /usr/lib/libgalera_smm.so
bind-address                   = 0.0.0.0
binlog_format                  = row
default_storage_engine         = InnoDB
innodb_doublewrite             = 1
innodb_autoinc_lock_mode       = 2
innodb_flush_log_at_trx_commit = 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:-}"
            MYSQL_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 `mysql` configuration file:

```plaintext
docker-compose --project-name db up --scale node="$(grep -i wsrep_cluster_address <name>.cnf | tr -d ' ' | tr ',' ' ' | wc -w)"
```
  • Loading branch information
tymonx committed May 21, 2021
1 parent 6f5d272 commit 1bc7b80
Show file tree
Hide file tree
Showing 6 changed files with 426 additions and 0 deletions.
71 changes: 71 additions & 0 deletions 10.2/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,57 @@ docker_setup_db() {
fi
}

# 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); 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="$(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="$(mysql_get_config 'wsrep-data-home-dir' "$@")"

# it removes URI schemes like gcomm://
address="${address#[[:graph:]]*://}"

# it replaces commas ',' with spaces ' ' and converts it to array
address=( ${address//,/ } )

[ -n "$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 @@ -388,6 +439,26 @@ _main() {
mysql_note "MariaDB init process done. Ready for start up."
echo
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
71 changes: 71 additions & 0 deletions 10.3/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,57 @@ docker_setup_db() {
fi
}

# 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); 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="$(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="$(mysql_get_config 'wsrep-data-home-dir' "$@")"

# it removes URI schemes like gcomm://
address="${address#[[:graph:]]*://}"

# it replaces commas ',' with spaces ' ' and converts it to array
address=( ${address//,/ } )

[ -n "$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 @@ -388,6 +439,26 @@ _main() {
mysql_note "MariaDB init process done. Ready for start up."
echo
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
71 changes: 71 additions & 0 deletions 10.4/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,57 @@ docker_setup_db() {
fi
}

# 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); 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="$(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="$(mysql_get_config 'wsrep-data-home-dir' "$@")"

# it removes URI schemes like gcomm://
address="${address#[[:graph:]]*://}"

# it replaces commas ',' with spaces ' ' and converts it to array
address=( ${address//,/ } )

[ -n "$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 @@ -388,6 +439,26 @@ _main() {
mysql_note "MariaDB init process done. Ready for start up."
echo
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
71 changes: 71 additions & 0 deletions 10.5/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,57 @@ docker_setup_db() {
fi
}

# 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); 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="$(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="$(mysql_get_config 'wsrep-data-home-dir' "$@")"

# it removes URI schemes like gcomm://
address="${address#[[:graph:]]*://}"

# it replaces commas ',' with spaces ' ' and converts it to array
address=( ${address//,/ } )

[ -n "$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 @@ -388,6 +439,26 @@ _main() {
mysql_note "MariaDB init process done. Ready for start up."
echo
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 1bc7b80

Please sign in to comment.