Evernode instances merged into mariadb galera cluster with maria-backup sync

This is the place to discuss your developments on Evernode
Post Reply
wooking
Posts: 2
Joined: Thu Feb 13, 2025 8:29 pm

Evernode instances merged into mariadb galera cluster with maria-backup sync

Post by wooking »

Image
https://i.imgur.com/mdEdsN4.png

Docker Demo (ssh on userport, username: evernode password: default)
evermariagalera/evermariagalera:latest

using up all ports as tcp ports, mesh, user, gptcp1 and gptcp2

Minimum recommended size of cluster: 3 (cuz if one of them dies there's still a majority telling us which data is correct)

dbport is gptcp1

Initial Clustered Maria DB Galera with maria-backup sync project. This code is with SSH, next step is to make a nice frontend.

This is using env.vars to source $GPTCP1 and $GPTCP2, you need to adjust them manually if you use a node without env.vars
config file: /etc/mysql/conf.d/galera.cnf

Dockerfile

Code: Select all

FROM everweb/test:latest

ENV DEBIAN_FRONTEND=noninteractive \
    TZ=UTC

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        curl gnupg ca-certificates software-properties-common && \
    curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | bash -s -- --mariadb-server-version="mariadb-11.4" && \
    apt-get update && \
    apt-get install -y --no-install-recommends \
        mariadb-server \
        mariadb-client \
        mariadb-backup \
        galera-4 \
        rsync \
        openssh-server \
        openssl sudo nano&& \
    apt-get purge -y gnupg software-properties-common && \
    apt-get autoremove -y && \
    rm -rf /var/lib/apt/lists/*

RUN mkdir -p /var/lib/mysql /var/run/mysqld /var/run/sshd /etc/mysql/conf.d && \
    chown -R mysql:mysql /var/lib/mysql /var/run/mysqld && \
    chmod 755 /var/run/mysqld

RUN echo "Port 22" >> /etc/ssh/sshd_config && \
    echo "PermitRootLogin no" >> /etc/ssh/sshd_config && \
    echo "ListenAddress 0.0.0.0" >> /etc/ssh/sshd_config

RUN useradd -m -s /bin/bash evernode && \
    echo "evernode:default" | chpasswd && \
    usermod -aG sudo evernode && \
    echo "evernode ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

COPY galera.cnf /etc/mysql/conf.d/galera.cnf

COPY start.sh /start.sh
RUN chmod +x /start.sh

# Volume
VOLUME /var/lib/mysql

ENTRYPOINT ["/start.sh"]
/etc/mysql/conf.d/galera.cnf

Code: Select all

[mysqld]
# mysql port shared with wsrep_sst_method = mysqldump/mariabackup
port = 3306                    # peer port
bind-address = 0.0.0.0         # Listen on all interfaces (or set to your private IP for security)

# ==================== Galera Settings ====================
wsrep_on                 = ON
wsrep_provider           = /usr/lib/galera/libgalera_smm.so
wsrep_cluster_name       = galeracluster

#you only need a few servers added to this list.
wsrep_cluster_address    = gcomm://1.1.1.1

# SST Method
wsrep_sst_method         = mariabackup
wsrep_sst_auth           = "sst_user:__RANDOMPWD__"
wsrep_sst_receive_address = __PUBLICIP__:3306

# Node identification — change per node
wsrep_node_name          = node1
wsrep_node_address       = __PUBLICIP__

wsrep_provider_options = "gcache.size=4G;gmcast.listen_addr=tcp://0.0.0.0:4567;ist.recv_addr=__PUBLICIP__:4568;ist.recv_bind=0.0.0.0:4568;gmcast.peer_timeout=PT20S;evs.keepalive_period=PT5S;evs.suspect_timeout=PT60S;evs.inactive_timeout=PT2M;evs.inactive_check_period=PT10S;evs.install_timeout=PT2M;evs.send_window=1024;evs.user_send_window=512"

binlog_format            = ROW
default_storage_engine   = InnoDB
innodb_autoinc_lock_mode = 2
innodb_flush_log_at_trx_commit = 0
innodb_doublewrite       = 1
/start.sh

Code: Select all

#!/bin/bash
set -e

echo "=== Container starting ==="

# Generate SSH keys if missing
if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then
    echo "Generating SSH keys..."
    ssh-keygen -A
fi

CFG_FILE="/contract/cfg/hp.cfg"
SSHD_CONFIG="/etc/ssh/sshd_config"

# Extract userport
userport=$(awk '
  $0 ~ /"user"/ { user_block=1 }
  user_block && $0 ~ /"port"/ {
    gsub(/[ ,]/, "", $2);
    gsub(/[^0-9]/, "", $2);
    print $2;
    exit
  }
' "$CFG_FILE")

if [[ -z "$userport" ]]; then
  echo "Could not extract user port from $CFG_FILE"
  exit 1
fi

echo "Using SSH port: $userport"
# Add port if missing
if ! grep -q "^Port $userport" "$SSHD_CONFIG"; then
  echo "Adding Port $userport"
  echo "Port $userport" >> "$SSHD_CONFIG"
fi

# Extract meshport
meshport=$(awk '
  $0 ~ /"mesh"/ { mesh_block=1 }
  mesh_block && $0 ~ /"port"/ {
    gsub(/[ ,]/, "", $2);
    gsub(/[^0-9]/, "", $2);
    print $2;
    exit
  }
' "$CFG_FILE")

if [[ -z "$meshport" ]]; then
  echo "Could not extract user port from $CFG_FILE"
  exit 1
fi


# List of reliable IPv4-only IP resolvers
RESOLVERS=(
    "https://ipv4.icanhazip.com"
    "https://api.ipify.org"
    "https://ifconfig.me/ip"
    "https://ipecho.net/plain"
    "https://checkip.amazonaws.com"
    "https://ipinfo.io/ip"
    "https://myexternalip.com/raw"
    "https://ident.me"
    "https://whatismyip.akamai.com"
)

publicip=""

for resolver in "${RESOLVERS[@]}"; do
    ip=$(curl -s --max-time 5 --connect-timeout 3 -4 "$resolver" 2>/dev/null | tr -d ' \n\r\t')
    if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        IFS='.' read -r a b c d <<< "$ip"
        if (( a <= 255 && b <= 255 && c <= 255 && d <= 255 )); then
            publicip="$ip"
            break
        fi
    fi
done

if [[ -n "$publicip" ]]; then
    echo "Public IPv4: $publicip"
else
    echo "Error: Could not retrieve public IPv4 address from any resolver."
fi

sed -i "s/4567/$meshport/g" /etc/mysql/conf.d/galera.cnf
sed -i "s/__PUBLICIP__/$publicip/g" /etc/mysql/conf.d/galera.cnf

generate_password() {
    tr -dc 'A-Za-z0-9!_¤.?' < /dev/urandom | head -c 10
}

randompassword=$(generate_password)

sed -i "s/__RANDOMPWD__/$randompassword/g" /etc/mysql/conf.d/galera.cnf
sed -i "s/node1/$(hostname)/g" /etc/mysql/conf.d/galera.cnf

sleep 15 #env.vars need time
if [ -f /contract/env.vars ]; then
source /contract/env.vars
sed -i "s/4568/$EXTERNAL_GPTCP2_PORT/g" /etc/mysql/conf.d/galera.cnf
sed -i "s/3306/$EXTERNAL_GPTCP1_PORT/g" /etc/mysql/conf.d/galera.cnf
#sed -i "s/4444/$EXTERNAL_GPTCP2_PORT/g" /etc/mysql/conf.d/galera.cnf
#sed -i "s/node1/$HOSTNAME/g" /etc/mysql/conf.d/galera.cnf
fi

if [ ! -f /contract/env.vars ]; then
echo "Starting SSH..."
/usr/sbin/sshd &
fi

ROOT_USER="root"
ROOT_PASS=""
SST_USER="sst_user"

echo "=== Starting MariaDB ==="
sudo -u mysql mariadbd --wsrep-new-cluster &

MARIADB_PID=$!

echo "MariaDB started with PID ${MARIADB_PID}"
echo "Waiting for MariaDB to become ready..."
until mariadb --socket=/run/mysqld/mysqld.sock -e "SELECT 1" >/dev/null 2>&1; do
    sleep 2
    echo "Waiting for MariaDB..."
done

echo "MariaDB is ready!"
echo "Creating SST user and granting privileges..."
sudo -u mysql mariadb -e "
    CREATE USER IF NOT EXISTS '${SST_USER}'@'%' IDENTIFIED BY '${randompassword}';
    GRANT ALL PRIVILEGES ON *.* TO '${SST_USER}'@'%';
    FLUSH PRIVILEGES;"

echo "SST user created successfully."
echo "Stopping MariaDB"

if sudo -u mysql mariadbd shutdown; then
    echo "MariaDB stopped cleanly via mysqladmin."
else
    echo "mysqladmin shutdown failed, killing process..."
    kill ${MARIADB_PID} 2>/dev/null || true
    sleep 2
    kill -9 ${MARIADB_PID} 2>/dev/null || true
fi

echo "Done."

sed -i "s/1.1.1.1/$publicip:$meshport/g" /etc/mysql/conf.d/galera.cnf


sudo sed -i 's/.*bind-address\s*=\s*127.0.0.1.*/bind-address = 0.0.0.0/' /etc/mysql/mariadb.conf.d/50-server.cnf


if [ -f /contract/env.vars ]; then
# Start SSH FIRST (important)
echo "Starting SSH..."
/usr/sbin/sshd &
fi

# All you need to do to sync this up is to boot up instances, then add their ips to eachothers
# wscom list in /etc/mysql/conf.d/galera.cnf, then pick one to use --wsrep-new-cluster command and the rest just no command.


# A full cluster outage occurs when all nodes shut down or when Quorum is lost completely,
# leaving no Primary Component. In this scenario, you must manually intervene to safely restart the cluster.

# file /var/lib/mysql/grastate.dat (sudo nano) contains bootstrap boolean switch, the one with 1 on it has a healthy db (asfaik)
# Highest seqno is latest version, uuid matters too (so its not blank).
# If your seqnumber is -1 you need to do sudo -u mysql mariadbd --wsrep-recover and then restart 
# cluster sudo -u mysql mariadbd --wsrep-new-cluster &


#run first node with sudo -u mysql mariadbd --wsrep-new-cluster &
#run second nodes with sudo -u mysql mariadbd

# Create WordPress database and user change localhost to % or a specific ip if u need public access.
# sudo mariadb -e "CREATE DATABASE IF NOT EXISTS wordpress;"
# sudo mariadb -e "CREATE USER IF NOT EXISTS 'wordpress'@'%' IDENTIFIED BY 'wordpress_password';"
# sudo mariadb -e "GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'%';"
# sudo mariadb -e "FLUSH PRIVILEGES;"

#Check cluster status
# sudo mariadb -e "SHOW STATUS LIKE 'wsrep_cluster_size';"
# sudo mariadb -e "SHOW STATUS LIKE 'wsrep_cluster_status';"
# sudo mariadb -e "SHOW STATUS LIKE 'wsrep_ready';"

#Create sst user
# CREATE USER 'sst_user'@'%' IDENTIFIED BY 'YourVeryStrongSSTPassword123!';
# GRANT ALL PRIVILEGES ON *.* TO 'sst_user'@'%';
# FLUSH PRIVILEGES;


# Change PWD (doesn't seem to be needed)
# sudo -u mysql mariadbd &
# sudo mariadb --socket=/var/run/mysqld/mysqld.sock -e "ALTER USER 'sst_user'@'%' IDENTIFIED BY 'NewPWD'; FLUSH PRIVILEGES;"
# kill pid.
tail -f /dev/null
I am not sure what the deal with sst_user is, seems like it got synced with the primary cluster (without having same userpassword)...
Post Reply