From cd75df6ce06b12c01af0eeb9578d2ca80af4f0cf Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Mon, 13 Oct 2025 15:17:27 +0100 Subject: Drop support for building MariaDB clustercheck MariaDB clustercheck script has not been maintained and it will not work with MariaDB 11.4 (the next LTS version). This is because clustercheck.sh uses ``mysql*`` commands but from MariaDB 11.4, those commands are not supported. The health check of MariaDB server containers will be done by new script which comes with change [1]. Without MariaDB clustercheck, MariaDB server becomes only container image in the family, so mariadb-base and mariadb-server dockerfiles are merged. To avoid breaking changes, the name 'mariadb-server' is retained. Follow-up to rename references in Kolla and K-A from 'mariadb-server' to 'mariadb' would be nice. [1] https://review.opendev.org/c/openstack/kolla/+/962657 Change-Id: I01278f76ee82b018a854b5862f3caaa8c78a9a30 Signed-off-by: Seunghun Lee --- docker/mariadb-server/Dockerfile.j2 | 96 +++++++++++++ docker/mariadb-server/backup.sh | 73 ++++++++++ docker/mariadb-server/backup_replica.sh | 148 +++++++++++++++++++++ docker/mariadb-server/extend_start.sh | 61 +++++++++ docker/mariadb-server/healthcheck_mariadb | 19 +++ docker/mariadb-server/mariadb_sudoers | 1 + docker/mariadb-server/security_reset.expect | 67 ++++++++++ docker/mariadb/mariadb-base/Dockerfile.j2 | 44 ------ docker/mariadb/mariadb-clustercheck/Dockerfile.j2 | 19 --- .../mariadb/mariadb-clustercheck/extend_start.sh | 11 -- .../mariadb/mariadb-clustercheck/socat_wrapper.sh | 19 --- docker/mariadb/mariadb-server/Dockerfile.j2 | 88 ------------ docker/mariadb/mariadb-server/backup.sh | 73 ---------- docker/mariadb/mariadb-server/backup_replica.sh | 148 --------------------- docker/mariadb/mariadb-server/extend_start.sh | 61 --------- docker/mariadb/mariadb-server/healthcheck_mariadb | 19 --- docker/mariadb/mariadb-server/mariadb_sudoers | 1 - .../mariadb/mariadb-server/security_reset.expect | 67 ---------- 18 files changed, 465 insertions(+), 550 deletions(-) create mode 100644 docker/mariadb-server/Dockerfile.j2 create mode 100644 docker/mariadb-server/backup.sh create mode 100644 docker/mariadb-server/backup_replica.sh create mode 100644 docker/mariadb-server/extend_start.sh create mode 100755 docker/mariadb-server/healthcheck_mariadb create mode 100644 docker/mariadb-server/mariadb_sudoers create mode 100644 docker/mariadb-server/security_reset.expect delete mode 100644 docker/mariadb/mariadb-base/Dockerfile.j2 delete mode 100644 docker/mariadb/mariadb-clustercheck/Dockerfile.j2 delete mode 100644 docker/mariadb/mariadb-clustercheck/extend_start.sh delete mode 100644 docker/mariadb/mariadb-clustercheck/socat_wrapper.sh delete mode 100644 docker/mariadb/mariadb-server/Dockerfile.j2 delete mode 100644 docker/mariadb/mariadb-server/backup.sh delete mode 100644 docker/mariadb/mariadb-server/backup_replica.sh delete mode 100644 docker/mariadb/mariadb-server/extend_start.sh delete mode 100755 docker/mariadb/mariadb-server/healthcheck_mariadb delete mode 100644 docker/mariadb/mariadb-server/mariadb_sudoers delete mode 100644 docker/mariadb/mariadb-server/security_reset.expect (limited to 'docker') diff --git a/docker/mariadb-server/Dockerfile.j2 b/docker/mariadb-server/Dockerfile.j2 new file mode 100644 index 000000000..ddb045fbb --- /dev/null +++ b/docker/mariadb-server/Dockerfile.j2 @@ -0,0 +1,96 @@ +FROM {{ namespace }}/{{ image_prefix }}base:{{ tag }} +{% block labels %} +LABEL maintainer="{{ maintainer }}" name="{{ image_name }}" build-date="{{ build_date }}" +{% endblock %} + +{% block mariadb_header %}{% endblock %} + +{% import "macros.j2" as macros with context %} + +{{ macros.configure_user(name='mysql') }} + +{# NOTE(mnasiadka): Use AppStream version of MariaDB for now in CentOS #} + +{% if base_package_type == 'rpm' %} + +{# NOTE(mgoddard): EPEL required for pv package #} +{{ macros.enable_extra_repos(['epel']) }} + + {% set mariadb_packages = [ + 'expect', + 'mariadb', + 'mariadb-backup', + 'mariadb-server', + 'mariadb-server-galera', + 'pv', + 'rsync', + 'tar' + ] %} + +{# NOTE(yoctozepto): this is to ensure Kolla Ansible can configure MariaDB to find it #} +RUN ln -s /usr/lib64/galera-4 /usr/lib64/galera + +{% elif base_package_type == 'deb' %} +{{ macros.enable_extra_repos(['mariadb']) }} + {% set mariadb_packages = [ + 'expect', + 'mariadb-backup', + 'mariadb-client', + 'mariadb-server' + ] %} +{% endif %} + +{{ macros.install_packages(mariadb_packages | customizable("packages")) }} + +{% block mariadb_healthcheck %} +ENV MARIADB_VERSION=10.11 +ADD plugins-archive / +RUN install -m 755 /plugins/mariadb-server-plugin-mariadb-docker-archive*/$MARIADB_VERSION/healthcheck.sh /usr/bin/healthcheck.sh +{% endblock %} + +COPY mariadb_sudoers /etc/sudoers.d/kolla_mariadb_sudoers +COPY extend_start.sh /usr/local/bin/kolla_extend_start +COPY security_reset.expect /usr/local/bin/kolla_security_reset +COPY backup.sh /usr/local/bin/kolla_mariadb_backup.sh +COPY backup_replica.sh /usr/local/bin/kolla_mariadb_backup_replica.sh + +RUN chmod 644 /usr/local/bin/kolla_extend_start \ + && chmod 755 /usr/local/bin/kolla_security_reset \ + /usr/local/bin/kolla_mariadb_backup.sh \ + /usr/local/bin/kolla_mariadb_backup_replica.sh \ + && chmod 750 /etc/sudoers.d \ + && chmod 440 /etc/sudoers.d/kolla_mariadb_sudoers \ + && rm -rf /var/lib/mysql/* + +{% if base_package_type == 'deb' %} +RUN mkdir -p /var/run/mysqld && chown mysql /var/run/mysqld && chmod 755 /var/run/mysqld +{% endif %} + +{% if docker_healthchecks %} +{% block healthcheck_installation %} + +COPY healthcheck_mariadb /usr/local/bin/healthcheck_mariadb +RUN chmod 755 /usr/local/bin/healthcheck_mariadb + +{% endblock %} +{% endif %} + +{% if use_dumb_init %} +{% block mariadb_entrypoint %} +# NOTE(mgoddard): Override the dumb-init arguments to avoid passing +# --single-child. This does not play well with mysqld_safe, which ignores +# SIGTERM, meaning Docker needs to forcibly kill the container to stop it. +# Without --single-child, the TERM signal is sent to all subprocesses, +# including mysqld. + +ENTRYPOINT ["dumb-init", "--"] +CMD ["kolla_start"] +{% endblock %} +{% endif %} + +{{ macros.kolla_patch_sources() }} + +{% block mariadb_footer %}{% endblock %} +{% block footer %}{% endblock %} + +USER mysql diff --git a/docker/mariadb-server/backup.sh b/docker/mariadb-server/backup.sh new file mode 100644 index 000000000..be63cd938 --- /dev/null +++ b/docker/mariadb-server/backup.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash + +set -eu +set -o pipefail + +# Execute a full backup +backup_full() { + echo "Taking a full backup" + LAST_FULL_DATE=$(date +%d-%m-%Y-%s) + BACKUP_FILE="mysqlbackup-${LAST_FULL_DATE}.qp.xbc.xbs.gz" + BACKUP_PATH="$BACKUP_DIR/full-${LAST_FULL_DATE}" + mkdir -p "$BACKUP_PATH" + + mariabackup \ + --defaults-file=/etc/mysql/my.cnf \ + --backup \ + --stream=xbstream \ + --history=$LAST_FULL_DATE | gzip > \ + "$BACKUP_PATH/$BACKUP_FILE" + + echo "$BACKUP_PATH/$BACKUP_FILE" > "$BACKUP_DIR/last_full_file" +} + +# Execute an incremental backup +backup_incremental() { + echo "Taking an incremental backup" + + if [ ! -r "$BACKUP_DIR/last_full_file" ]; then + echo "Error: No full backup file found." + exit 1 + fi + + FULL_BACKUP_FILE=$(cat "$BACKUP_DIR/last_full_file") + LAST_FULL_DATE=$(basename "$(dirname "$FULL_BACKUP_FILE")" | sed 's/^full-//') + NOW=$(date +%H-%M-%S-%d-%m-%Y) + INCR_DIR="$BACKUP_DIR/incr-${NOW}-since-${LAST_FULL_DATE}" + mkdir -p "$INCR_DIR" + + TMP_BASEDIR=$(mktemp -d) + echo "Decompressing full backup to temp dir: $TMP_BASEDIR" + gunzip -c "$FULL_BACKUP_FILE" | mbstream -x -C "$TMP_BASEDIR" + + mariabackup \ + --defaults-file=/etc/mysql/my.cnf \ + --backup \ + --stream=xbstream \ + --incremental-basedir="$TMP_BASEDIR" \ + --history="incr-${NOW}" | gzip > \ + "$INCR_DIR/incremental-${NOW}-mysqlbackup-${LAST_FULL_DATE}.qp.xbc.xbs.gz" + + rm -rf "$TMP_BASEDIR" +} + +BACKUP_DIR=/backup/ +cd "$BACKUP_DIR" + +if [ -n "${BACKUP_TYPE:-}" ]; then + case "$BACKUP_TYPE" in + "full") + backup_full + ;; + "incremental") + backup_incremental + ;; + *) + echo "Only full or incremental options are supported." + exit 1 + ;; + esac +else + echo "You need to specify either full or incremental backup options." + exit 1 +fi diff --git a/docker/mariadb-server/backup_replica.sh b/docker/mariadb-server/backup_replica.sh new file mode 100644 index 000000000..ebdcbf6b0 --- /dev/null +++ b/docker/mariadb-server/backup_replica.sh @@ -0,0 +1,148 @@ +#!/usr/bin/env bash + +set -eu +set -o pipefail + +BACKUP_DIR=/backup/ +DEFAULT_MY_CNF="/etc/mysql/my.cnf" +REPLICA_MY_CNF="$(mktemp)" +RETRY_INTERVAL=5 # Interval between retries (in seconds) +MAX_RETRIES=12 # Max retries (12 retries * 5 seconds = 60 seconds) + +# Cleanup function to remove the REPLICA_MY_CNF file +cleanup() { + rm -f "${REPLICA_MY_CNF}" +} + +# Set trap to ensure cleanup occurs on exit or error +trap cleanup EXIT + +cd "${BACKUP_DIR}" + +# Execute a full backup +backup_full() { + echo "Taking a full backup" + LAST_FULL_DATE=$(date +%d-%m-%Y-%s) + BACKUP_FILE="backup-full-${LAST_FULL_DATE}.mbs.gz" + BACKUP_PATH="${BACKUP_DIR}/full-${LAST_FULL_DATE}" + mkdir -p "${BACKUP_PATH}" + + mariabackup \ + --defaults-file="${REPLICA_MY_CNF}" \ + --backup \ + --stream=mbstream \ + --history="${LAST_FULL_DATE}" \ + | gzip > "${BACKUP_PATH}/${BACKUP_FILE}" + + echo "${BACKUP_PATH}/${BACKUP_FILE}" > "${BACKUP_DIR}/last_full_file" +} + +# Execute an incremental backup +backup_incremental() { + if [ ! -r "${BACKUP_DIR}/last_full_file" ]; then + echo "Error: No full backup file found." + exit 1 + fi + + FULL_BACKUP_FILE=$(cat "${BACKUP_DIR}/last_full_file") + LAST_FULL_DATE=$(basename "$(dirname "${FULL_BACKUP_FILE}")" | sed 's/^full-//') + NOW=$(date +%H-%M-%S-%d-%m-%Y) + INCR_DIR="${BACKUP_DIR}/incr-${NOW}-since-${LAST_FULL_DATE}" + mkdir -p "${INCR_DIR}" + + # Temp dir for full base restore + TMP_BASEDIR=$(mktemp -d) + + echo "Decompressing full backup to temp dir: ${TMP_BASEDIR}" + gunzip -c "${FULL_BACKUP_FILE}" | mbstream -x -C "${TMP_BASEDIR}" + + mariabackup \ + --defaults-file="${REPLICA_MY_CNF}" \ + --backup \ + --stream=mbstream \ + --incremental-basedir="${TMP_BASEDIR}" \ + --history="incr-${NOW}" \ + | gzip > "${INCR_DIR}/backup-incremental-${NOW}.mbs.gz" + + rm -rf "${TMP_BASEDIR}" +} + +# Retry logic for database queries +retry_mysql_query() { + local query="$1" + local result="" + local attempt=1 + + while [ ${attempt} -le ${MAX_RETRIES} ]; do + result=$(mysql -h "${HOST}" -u "${USER}" -p"${PASS}" -s -N -e "${query}" 2>/dev/null || true) + if [ -n "${result}" ]; then + echo "${result}" + return 0 + fi + echo "Attempt ${attempt}/${MAX_RETRIES} failed. Retrying in ${RETRY_INTERVAL} seconds..." + sleep "${RETRY_INTERVAL}" + attempt=$((attempt + 1)) + done + + echo "Error: Failed to execute the query after ${MAX_RETRIES} attempts." + return 1 +} + +get_and_set_replica_server() { + HOST="$(grep '^host' "${DEFAULT_MY_CNF}" | awk -F '=' '{print $2}' | xargs)" + USER="$(grep '^user' "${DEFAULT_MY_CNF}" | awk -F '=' '{print $2}' | xargs)" + PASS="$(grep '^password' "${DEFAULT_MY_CNF}" | awk -F '=' '{print $2}' | xargs)" + + ALL_HOSTS_SELECT="SELECT REGEXP_REPLACE(VARIABLE_VALUE, ':[0-9]*','') FROM information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_incoming_addresses';" + ALL_HOSTS=$(retry_mysql_query "${ALL_HOSTS_SELECT}") + if [ -z "${ALL_HOSTS}" ]; then + echo "Backup failed due to inability to fetch a list of servers." + exit 1 + fi + + ACTIVE_HOST_SELECT='SELECT @@hostname;' + ACTIVE_HOST=$(retry_mysql_query "${ACTIVE_HOST_SELECT}" | xargs getent hosts | awk '{print $1}') + if [ -z "${ACTIVE_HOST}" ]; then + echo "Backup failed due to inability to fetch active host." + exit 1 + fi + + # Multinode + if echo "${ALL_HOSTS}" | grep -q ','; then + for server in $(echo "${ALL_HOSTS}" | tr ',' '\n'); do + if [[ "${server}" != "${ACTIVE_HOST}" ]]; then + REPLICA_HOST="${server}" + break + fi + done + # Single node + else + REPLICA_HOST="${ALL_HOSTS}" + fi + if [ -z "${REPLICA_HOST}" ]; then + echo "Backup failed due to inability to determine replica host." + exit 1 + fi + + cp "${DEFAULT_MY_CNF}" "${REPLICA_MY_CNF}" + sed -i "s/${HOST}/${REPLICA_HOST}/g" "${REPLICA_MY_CNF}" +} + +if [ -n "${BACKUP_TYPE}" ]; then + get_and_set_replica_server + case "${BACKUP_TYPE}" in + "full") + backup_full + ;; + "incremental") + backup_incremental + ;; + *) + echo "Only full or incremental options are supported." + exit 1 + ;; + esac +else + echo "You need to specify either full or incremental backup options." + exit 1 +fi diff --git a/docker/mariadb-server/extend_start.sh b/docker/mariadb-server/extend_start.sh new file mode 100644 index 000000000..53a54557d --- /dev/null +++ b/docker/mariadb-server/extend_start.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +: ${MARIADB_LOG_DIR:=/var/log/kolla/mariadb} + +function bootstrap_db { + mysqld_safe --wsrep-new-cluster --skip-networking --wsrep-on=OFF --pid-file=/var/lib/mysql/mariadb.pid & + # Wait for the mariadb server to be "Ready" before starting the security reset with a max timeout + # NOTE(huikang): the location of mysql's socket file varies depending on the OS distributions. + # Querying the cluster status has to be executed after the existence of mysql.sock and mariadb.pid. + TIMEOUT=${DB_MAX_TIMEOUT:-60} + while [[ ! -S /var/lib/mysql/mysql.sock ]] && \ + [[ ! -S /var/run/mysqld/mysqld.sock ]] || \ + [[ ! -f /var/lib/mysql/mariadb.pid ]]; do + if [[ ${TIMEOUT} -gt 0 ]]; then + let TIMEOUT-=1 + sleep 1 + else + exit 1 + fi + done + + sudo -E kolla_security_reset + + set +o xtrace + mysql -u root --password="${DB_ROOT_PASSWORD}" -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY '${DB_ROOT_PASSWORD}' WITH GRANT OPTION;" + mysql -u root --password="${DB_ROOT_PASSWORD}" -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '${DB_ROOT_PASSWORD}' WITH GRANT OPTION;" + mysqladmin -uroot -p"${DB_ROOT_PASSWORD}" shutdown + set -o xtrace +} + +# Create log directory, with appropriate permissions +if [[ ! -d "${MARIADB_LOG_DIR}" ]]; then + mkdir -p ${MARIADB_LOG_DIR} +fi +if [[ $(stat -c %a ${MARIADB_LOG_DIR}) != "755" ]]; then + chmod 755 ${MARIADB_LOG_DIR} +fi + +# This catches all cases of the BOOTSTRAP variable being set, including empty +if [[ "${!KOLLA_BOOTSTRAP[@]}" ]]; then + mysql_install_db 2>&1 | tee -a ${MARIADB_LOG_DIR}/mariadb-bootstrap.log + bootstrap_db 2>&1 | tee -a ${MARIADB_LOG_DIR}/mariadb-bootstrap.log + exit 0 +fi + +# This catches all cases of the KOLLA_UPGRADE variable being set, including empty +if [[ "${!KOLLA_UPGRADE[@]}" ]]; then + # The mysql_upgrade command treats any directories under /var/lib/mysql as + # databases. Somehow we can end up with a .pki directory, which causes the + # command to fail with this error: + # Incorrect database name '#mysql50#.pki' when selecting the database + # There doesn't seem to be anything in the directory, so remove it. + rm -rf /var/lib/mysql/.pki + + mysql_upgrade --host=${DB_HOST} --port=${DB_PORT} --user=root --password="${DB_ROOT_PASSWORD}" 2>&1 | tee -a ${MARIADB_LOG_DIR}/mariadb-upgrade.log + exit 0 +fi + +if [[ "${!BOOTSTRAP_ARGS[@]}" ]]; then + ARGS="${BOOTSTRAP_ARGS}" +fi diff --git a/docker/mariadb-server/healthcheck_mariadb b/docker/mariadb-server/healthcheck_mariadb new file mode 100755 index 000000000..0d9f59a95 --- /dev/null +++ b/docker/mariadb-server/healthcheck_mariadb @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e + +MYSQL_USERNAME="${MYSQL_USERNAME:=-haproxy}" +MYSQL_TIMEOUT=10 + +MYSQL_CMDLINE="mysql -sN --connect-timeout=${MYSQL_TIMEOUT} -u ${MYSQL_USERNAME}" + +WSREP_STATUS=$($MYSQL_CMDLINE -e "SHOW STATUS LIKE 'wsrep_local_state_comment';" | awk '{print $2}') + +if [[ "${WSREP_STATUS}" == "Synced" ]] +then + echo "MariaDB Galera Cluster Node is synced." + exit 0 +else + echo "MariaDB Galera Cluster Node is NOT synced" + exit 1 +fi diff --git a/docker/mariadb-server/mariadb_sudoers b/docker/mariadb-server/mariadb_sudoers new file mode 100644 index 000000000..150534e16 --- /dev/null +++ b/docker/mariadb-server/mariadb_sudoers @@ -0,0 +1 @@ +%kolla ALL=(root) NOPASSWD: /usr/local/bin/kolla_security_reset diff --git a/docker/mariadb-server/security_reset.expect b/docker/mariadb-server/security_reset.expect new file mode 100644 index 000000000..06f1d5c8e --- /dev/null +++ b/docker/mariadb-server/security_reset.expect @@ -0,0 +1,67 @@ +#!/usr/bin/expect -f + +if [catch {set timeout $env(DB_MAX_TIMEOUT)}] {set timeout 10} +spawn mariadb-secure-installation +expect { + timeout { send_user "\nFailed to get 'Enter current password for root (enter for none):' prompt\n"; exit 1 } + eof { send_user "\nFailed to get 'Enter current password for root (enter for none):' prompt\n"; exit 1 } + "Enter current password for root (enter for none):" +} +send "\r" + +expect { + timeout { send_user "\nFailed to get 'Switch to unix_socket authentication [Y/n] ' prompt\n"; exit 1 } + eof { send_user "\nFailed to get 'Switch to unix_socket authentication [Y/n]' prompt\n"; exit 1 } + "Switch to unix_socket authentication \\\[Y/n\\\] " +} +send "n\r" + +expect { + timeout { send_user "\nFailed to get 'Change the root password? [Y/n]' prompt\n"; exit 1 } + eof { send_user "\nFailed to get 'Change the root password? [Y/n]' prompt\n"; exit 1 } + "Change the root password? \\\[Y/n\\\] " +} +send "y\r" + +expect { + timeout { send_user "\nFailed to get 'New password:' prompt\n"; exit 1 } + eof { send_user "\nFailed to get 'New password:' prompt\n"; exit 1 } + "New password:" +} +send "$env(DB_ROOT_PASSWORD)\r" + +expect { + timeout { send_user "\nFailed to get 'Re-enter new password:' prompt\n"; exit 1 } + eof { send_user "\nFailed to get 'Re-enter new password:' prompt\n"; exit 1 } + "Re-enter new password:" +} +send "$env(DB_ROOT_PASSWORD)\r" + +expect { + timeout { send_user "\nFailed to get 'Remove anonymous users?' prompt\n"; exit 1 } + eof { send_user "\nFailed to get 'Remove anonymous users?' prompt\n"; exit 1 } + "Remove anonymous users?" +} +send "y\r" + +expect { + timeout { send_user "\nFailed to get 'Disallow root login remotely?' prompt\n"; exit 1 } + eof { send_user "\nFailed to get 'Disallow root login remotely?' prompt\n"; exit 1 } + "Disallow root login remotely?" +} +send "n\r" + +expect { + timeout { send_user "\nFailed to get 'Remove test database and access to it?' prompt\n"; exit 1 } + eof { send_user "\nFailed to get 'Remove test database and access to it?' prompt\n"; exit 1 } + "Remove test database and access to it?" +} +send "y\r" + +expect { + timeout { send_user "\nFailed to get 'Reload privilege tables now?' prompt\n"; exit 1 } + eof { send_user "\nFailed to get 'Reload privilege tables now?' prompt\n"; exit 1 } + "Reload privilege tables now?" +} +send "y\r" +expect eof diff --git a/docker/mariadb/mariadb-base/Dockerfile.j2 b/docker/mariadb/mariadb-base/Dockerfile.j2 deleted file mode 100644 index 51cdadb29..000000000 --- a/docker/mariadb/mariadb-base/Dockerfile.j2 +++ /dev/null @@ -1,44 +0,0 @@ -FROM {{ namespace }}/{{ image_prefix }}base:{{ tag }} -{% block labels %} -LABEL maintainer="{{ maintainer }}" name="{{ image_name }}" build-date="{{ build_date }}" -{% endblock %} - -{% block mariadb_base_header %}{% endblock %} - -{% import "macros.j2" as macros with context %} - -{{ macros.configure_user(name='mysql') }} - -{# NOTE(mnasiadka): Using AppStream packages on CS10 for now #} -{% if base_package_type == 'rpm' %} - {% set mariadb_base_packages = [ - 'mariadb', - ] %} - -{% elif base_package_type == 'deb' %} -{{ macros.enable_extra_repos(['mariadb']) }} - {% set mariadb_base_packages = [ - 'mariadb-client', - ] %} -{% endif %} - -{{ macros.install_packages(mariadb_base_packages | customizable("packages")) }} - -{# NOTE(mnasiadka): Latest clustercheck version as of May 2023 is 10.9 #} -{% block mariadb_clustercheck_version %} -ARG mariadb_clustercheck_version=10.9 -ARG mariadb_clustercheck_url=https://src.fedoraproject.org/rpms/mariadb/raw/${mariadb_clustercheck_version}/f/clustercheck.sh -{% endblock %} - -RUN curl -o /usr/bin/clustercheck ${mariadb_clustercheck_url} \ - && chmod 755 /usr/bin/clustercheck - -{% block mariadb_healthcheck %} -ENV MARIADB_VERSION=10.11 -ADD plugins-archive / -RUN install -m 755 /plugins/mariadb-base-plugin-mariadb-docker-archive*/$MARIADB_VERSION/healthcheck.sh /usr/bin/healthcheck.sh -{% endblock %} - -{{ macros.kolla_patch_sources() }} - -{% block mariadb_base_footer %}{% endblock %} diff --git a/docker/mariadb/mariadb-clustercheck/Dockerfile.j2 b/docker/mariadb/mariadb-clustercheck/Dockerfile.j2 deleted file mode 100644 index 7aee4ef05..000000000 --- a/docker/mariadb/mariadb-clustercheck/Dockerfile.j2 +++ /dev/null @@ -1,19 +0,0 @@ -FROM {{ namespace }}/{{ image_prefix }}mariadb-base:{{ tag }} -{% block labels %} -LABEL maintainer="{{ maintainer }}" name="{{ image_name }}" build-date="{{ build_date }}" -{% endblock %} - -{% block mariadb_clustercheck_header %}{% endblock %} - -{% import "macros.j2" as macros with context %} - -COPY extend_start.sh /usr/local/bin/kolla_extend_start -COPY socat_wrapper.sh /usr/local/bin/socat_wrapper - -RUN chmod 644 /usr/local/bin/kolla_extend_start && \ - chmod 755 /usr/local/bin/socat_wrapper - -{{ macros.kolla_patch_sources() }} - -{% block mariadb_clustercheck_footer %}{% endblock %} -{% block footer %}{% endblock %} diff --git a/docker/mariadb/mariadb-clustercheck/extend_start.sh b/docker/mariadb/mariadb-clustercheck/extend_start.sh deleted file mode 100644 index 7379abddc..000000000 --- a/docker/mariadb/mariadb-clustercheck/extend_start.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -: ${MARIADB_LOG_DIR:=/var/log/kolla/mariadb} - -# Create log directory, with appropriate permissions -if [[ ! -d "${MARIADB_LOG_DIR}" ]]; then - mkdir -p ${MARIADB_LOG_DIR} -fi -if [[ $(stat -c %a ${MARIADB_LOG_DIR}) != "755" ]]; then - chmod 755 ${MARIADB_LOG_DIR} -fi diff --git a/docker/mariadb/mariadb-clustercheck/socat_wrapper.sh b/docker/mariadb/mariadb-clustercheck/socat_wrapper.sh deleted file mode 100644 index bf562e3d5..000000000 --- a/docker/mariadb/mariadb-clustercheck/socat_wrapper.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -catch_term() { - kill -TERM "$child" 2>/dev/null - exit 0 -} - -catch_quit() { - kill -QUIT "$child" 2>/dev/null - exit 0 -} - -trap catch_term SIGTERM -trap catch_quit SIGQUIT - -socat "$@" & - -child=$! -wait "$child" diff --git a/docker/mariadb/mariadb-server/Dockerfile.j2 b/docker/mariadb/mariadb-server/Dockerfile.j2 deleted file mode 100644 index 15b9e48a4..000000000 --- a/docker/mariadb/mariadb-server/Dockerfile.j2 +++ /dev/null @@ -1,88 +0,0 @@ -FROM {{ namespace }}/{{ image_prefix }}mariadb-base:{{ tag }} -{% block labels %} -LABEL maintainer="{{ maintainer }}" name="{{ image_name }}" build-date="{{ build_date }}" -{% endblock %} - -{% block mariadb_header %}{% endblock %} - -{% import "macros.j2" as macros with context %} - -{{ macros.configure_user(name='mysql') }} - -{# NOTE(mnasiadka): Use AppStream version of MariaDB for now in CentOS #} - -{% if base_package_type == 'rpm' %} - -{# NOTE(mgoddard): EPEL required for pv package #} -{{ macros.enable_extra_repos(['epel']) }} - - {% set mariadb_packages = [ - 'expect', - 'mariadb-backup', - 'mariadb-server', - 'mariadb-server-galera', - 'pv', - 'rsync', - 'tar' - ] %} - -{# NOTE(yoctozepto): this is to ensure Kolla Ansible can configure MariaDB to find it #} -RUN ln -s /usr/lib64/galera-4 /usr/lib64/galera - -{% elif base_package_type == 'deb' %} -{{ macros.enable_extra_repos(['mariadb']) }} - {% set mariadb_packages = [ - 'expect', - 'mariadb-backup', - 'mariadb-server' - ] %} -{% endif %} - -{{ macros.install_packages(mariadb_packages | customizable("packages")) }} - -COPY mariadb_sudoers /etc/sudoers.d/kolla_mariadb_sudoers -COPY extend_start.sh /usr/local/bin/kolla_extend_start -COPY security_reset.expect /usr/local/bin/kolla_security_reset -COPY backup.sh /usr/local/bin/kolla_mariadb_backup.sh -COPY backup_replica.sh /usr/local/bin/kolla_mariadb_backup_replica.sh - -RUN chmod 644 /usr/local/bin/kolla_extend_start \ - && chmod 755 /usr/local/bin/kolla_security_reset \ - /usr/local/bin/kolla_mariadb_backup.sh \ - /usr/local/bin/kolla_mariadb_backup_replica.sh \ - && chmod 750 /etc/sudoers.d \ - && chmod 440 /etc/sudoers.d/kolla_mariadb_sudoers \ - && rm -rf /var/lib/mysql/* - -{% if base_package_type == 'deb' %} -RUN mkdir -p /var/run/mysqld && chown mysql /var/run/mysqld && chmod 755 /var/run/mysqld -{% endif %} - -{% if docker_healthchecks %} -{% block healthcheck_installation %} - -COPY healthcheck_mariadb /usr/local/bin/healthcheck_mariadb -RUN chmod 755 /usr/local/bin/healthcheck_mariadb - -{% endblock %} -{% endif %} - -{% if use_dumb_init %} -{% block mariadb_entrypoint %} -# NOTE(mgoddard): Override the dumb-init arguments to avoid passing -# --single-child. This does not play well with mysqld_safe, which ignores -# SIGTERM, meaning Docker needs to forcibly kill the container to stop it. -# Without --single-child, the TERM signal is sent to all subprocesses, -# including mysqld. - -ENTRYPOINT ["dumb-init", "--"] -CMD ["kolla_start"] -{% endblock %} -{% endif %} - -{{ macros.kolla_patch_sources() }} - -{% block mariadb_footer %}{% endblock %} -{% block footer %}{% endblock %} - -USER mysql diff --git a/docker/mariadb/mariadb-server/backup.sh b/docker/mariadb/mariadb-server/backup.sh deleted file mode 100644 index be63cd938..000000000 --- a/docker/mariadb/mariadb-server/backup.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bash - -set -eu -set -o pipefail - -# Execute a full backup -backup_full() { - echo "Taking a full backup" - LAST_FULL_DATE=$(date +%d-%m-%Y-%s) - BACKUP_FILE="mysqlbackup-${LAST_FULL_DATE}.qp.xbc.xbs.gz" - BACKUP_PATH="$BACKUP_DIR/full-${LAST_FULL_DATE}" - mkdir -p "$BACKUP_PATH" - - mariabackup \ - --defaults-file=/etc/mysql/my.cnf \ - --backup \ - --stream=xbstream \ - --history=$LAST_FULL_DATE | gzip > \ - "$BACKUP_PATH/$BACKUP_FILE" - - echo "$BACKUP_PATH/$BACKUP_FILE" > "$BACKUP_DIR/last_full_file" -} - -# Execute an incremental backup -backup_incremental() { - echo "Taking an incremental backup" - - if [ ! -r "$BACKUP_DIR/last_full_file" ]; then - echo "Error: No full backup file found." - exit 1 - fi - - FULL_BACKUP_FILE=$(cat "$BACKUP_DIR/last_full_file") - LAST_FULL_DATE=$(basename "$(dirname "$FULL_BACKUP_FILE")" | sed 's/^full-//') - NOW=$(date +%H-%M-%S-%d-%m-%Y) - INCR_DIR="$BACKUP_DIR/incr-${NOW}-since-${LAST_FULL_DATE}" - mkdir -p "$INCR_DIR" - - TMP_BASEDIR=$(mktemp -d) - echo "Decompressing full backup to temp dir: $TMP_BASEDIR" - gunzip -c "$FULL_BACKUP_FILE" | mbstream -x -C "$TMP_BASEDIR" - - mariabackup \ - --defaults-file=/etc/mysql/my.cnf \ - --backup \ - --stream=xbstream \ - --incremental-basedir="$TMP_BASEDIR" \ - --history="incr-${NOW}" | gzip > \ - "$INCR_DIR/incremental-${NOW}-mysqlbackup-${LAST_FULL_DATE}.qp.xbc.xbs.gz" - - rm -rf "$TMP_BASEDIR" -} - -BACKUP_DIR=/backup/ -cd "$BACKUP_DIR" - -if [ -n "${BACKUP_TYPE:-}" ]; then - case "$BACKUP_TYPE" in - "full") - backup_full - ;; - "incremental") - backup_incremental - ;; - *) - echo "Only full or incremental options are supported." - exit 1 - ;; - esac -else - echo "You need to specify either full or incremental backup options." - exit 1 -fi diff --git a/docker/mariadb/mariadb-server/backup_replica.sh b/docker/mariadb/mariadb-server/backup_replica.sh deleted file mode 100644 index ebdcbf6b0..000000000 --- a/docker/mariadb/mariadb-server/backup_replica.sh +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env bash - -set -eu -set -o pipefail - -BACKUP_DIR=/backup/ -DEFAULT_MY_CNF="/etc/mysql/my.cnf" -REPLICA_MY_CNF="$(mktemp)" -RETRY_INTERVAL=5 # Interval between retries (in seconds) -MAX_RETRIES=12 # Max retries (12 retries * 5 seconds = 60 seconds) - -# Cleanup function to remove the REPLICA_MY_CNF file -cleanup() { - rm -f "${REPLICA_MY_CNF}" -} - -# Set trap to ensure cleanup occurs on exit or error -trap cleanup EXIT - -cd "${BACKUP_DIR}" - -# Execute a full backup -backup_full() { - echo "Taking a full backup" - LAST_FULL_DATE=$(date +%d-%m-%Y-%s) - BACKUP_FILE="backup-full-${LAST_FULL_DATE}.mbs.gz" - BACKUP_PATH="${BACKUP_DIR}/full-${LAST_FULL_DATE}" - mkdir -p "${BACKUP_PATH}" - - mariabackup \ - --defaults-file="${REPLICA_MY_CNF}" \ - --backup \ - --stream=mbstream \ - --history="${LAST_FULL_DATE}" \ - | gzip > "${BACKUP_PATH}/${BACKUP_FILE}" - - echo "${BACKUP_PATH}/${BACKUP_FILE}" > "${BACKUP_DIR}/last_full_file" -} - -# Execute an incremental backup -backup_incremental() { - if [ ! -r "${BACKUP_DIR}/last_full_file" ]; then - echo "Error: No full backup file found." - exit 1 - fi - - FULL_BACKUP_FILE=$(cat "${BACKUP_DIR}/last_full_file") - LAST_FULL_DATE=$(basename "$(dirname "${FULL_BACKUP_FILE}")" | sed 's/^full-//') - NOW=$(date +%H-%M-%S-%d-%m-%Y) - INCR_DIR="${BACKUP_DIR}/incr-${NOW}-since-${LAST_FULL_DATE}" - mkdir -p "${INCR_DIR}" - - # Temp dir for full base restore - TMP_BASEDIR=$(mktemp -d) - - echo "Decompressing full backup to temp dir: ${TMP_BASEDIR}" - gunzip -c "${FULL_BACKUP_FILE}" | mbstream -x -C "${TMP_BASEDIR}" - - mariabackup \ - --defaults-file="${REPLICA_MY_CNF}" \ - --backup \ - --stream=mbstream \ - --incremental-basedir="${TMP_BASEDIR}" \ - --history="incr-${NOW}" \ - | gzip > "${INCR_DIR}/backup-incremental-${NOW}.mbs.gz" - - rm -rf "${TMP_BASEDIR}" -} - -# Retry logic for database queries -retry_mysql_query() { - local query="$1" - local result="" - local attempt=1 - - while [ ${attempt} -le ${MAX_RETRIES} ]; do - result=$(mysql -h "${HOST}" -u "${USER}" -p"${PASS}" -s -N -e "${query}" 2>/dev/null || true) - if [ -n "${result}" ]; then - echo "${result}" - return 0 - fi - echo "Attempt ${attempt}/${MAX_RETRIES} failed. Retrying in ${RETRY_INTERVAL} seconds..." - sleep "${RETRY_INTERVAL}" - attempt=$((attempt + 1)) - done - - echo "Error: Failed to execute the query after ${MAX_RETRIES} attempts." - return 1 -} - -get_and_set_replica_server() { - HOST="$(grep '^host' "${DEFAULT_MY_CNF}" | awk -F '=' '{print $2}' | xargs)" - USER="$(grep '^user' "${DEFAULT_MY_CNF}" | awk -F '=' '{print $2}' | xargs)" - PASS="$(grep '^password' "${DEFAULT_MY_CNF}" | awk -F '=' '{print $2}' | xargs)" - - ALL_HOSTS_SELECT="SELECT REGEXP_REPLACE(VARIABLE_VALUE, ':[0-9]*','') FROM information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_incoming_addresses';" - ALL_HOSTS=$(retry_mysql_query "${ALL_HOSTS_SELECT}") - if [ -z "${ALL_HOSTS}" ]; then - echo "Backup failed due to inability to fetch a list of servers." - exit 1 - fi - - ACTIVE_HOST_SELECT='SELECT @@hostname;' - ACTIVE_HOST=$(retry_mysql_query "${ACTIVE_HOST_SELECT}" | xargs getent hosts | awk '{print $1}') - if [ -z "${ACTIVE_HOST}" ]; then - echo "Backup failed due to inability to fetch active host." - exit 1 - fi - - # Multinode - if echo "${ALL_HOSTS}" | grep -q ','; then - for server in $(echo "${ALL_HOSTS}" | tr ',' '\n'); do - if [[ "${server}" != "${ACTIVE_HOST}" ]]; then - REPLICA_HOST="${server}" - break - fi - done - # Single node - else - REPLICA_HOST="${ALL_HOSTS}" - fi - if [ -z "${REPLICA_HOST}" ]; then - echo "Backup failed due to inability to determine replica host." - exit 1 - fi - - cp "${DEFAULT_MY_CNF}" "${REPLICA_MY_CNF}" - sed -i "s/${HOST}/${REPLICA_HOST}/g" "${REPLICA_MY_CNF}" -} - -if [ -n "${BACKUP_TYPE}" ]; then - get_and_set_replica_server - case "${BACKUP_TYPE}" in - "full") - backup_full - ;; - "incremental") - backup_incremental - ;; - *) - echo "Only full or incremental options are supported." - exit 1 - ;; - esac -else - echo "You need to specify either full or incremental backup options." - exit 1 -fi diff --git a/docker/mariadb/mariadb-server/extend_start.sh b/docker/mariadb/mariadb-server/extend_start.sh deleted file mode 100644 index 53a54557d..000000000 --- a/docker/mariadb/mariadb-server/extend_start.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -: ${MARIADB_LOG_DIR:=/var/log/kolla/mariadb} - -function bootstrap_db { - mysqld_safe --wsrep-new-cluster --skip-networking --wsrep-on=OFF --pid-file=/var/lib/mysql/mariadb.pid & - # Wait for the mariadb server to be "Ready" before starting the security reset with a max timeout - # NOTE(huikang): the location of mysql's socket file varies depending on the OS distributions. - # Querying the cluster status has to be executed after the existence of mysql.sock and mariadb.pid. - TIMEOUT=${DB_MAX_TIMEOUT:-60} - while [[ ! -S /var/lib/mysql/mysql.sock ]] && \ - [[ ! -S /var/run/mysqld/mysqld.sock ]] || \ - [[ ! -f /var/lib/mysql/mariadb.pid ]]; do - if [[ ${TIMEOUT} -gt 0 ]]; then - let TIMEOUT-=1 - sleep 1 - else - exit 1 - fi - done - - sudo -E kolla_security_reset - - set +o xtrace - mysql -u root --password="${DB_ROOT_PASSWORD}" -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY '${DB_ROOT_PASSWORD}' WITH GRANT OPTION;" - mysql -u root --password="${DB_ROOT_PASSWORD}" -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '${DB_ROOT_PASSWORD}' WITH GRANT OPTION;" - mysqladmin -uroot -p"${DB_ROOT_PASSWORD}" shutdown - set -o xtrace -} - -# Create log directory, with appropriate permissions -if [[ ! -d "${MARIADB_LOG_DIR}" ]]; then - mkdir -p ${MARIADB_LOG_DIR} -fi -if [[ $(stat -c %a ${MARIADB_LOG_DIR}) != "755" ]]; then - chmod 755 ${MARIADB_LOG_DIR} -fi - -# This catches all cases of the BOOTSTRAP variable being set, including empty -if [[ "${!KOLLA_BOOTSTRAP[@]}" ]]; then - mysql_install_db 2>&1 | tee -a ${MARIADB_LOG_DIR}/mariadb-bootstrap.log - bootstrap_db 2>&1 | tee -a ${MARIADB_LOG_DIR}/mariadb-bootstrap.log - exit 0 -fi - -# This catches all cases of the KOLLA_UPGRADE variable being set, including empty -if [[ "${!KOLLA_UPGRADE[@]}" ]]; then - # The mysql_upgrade command treats any directories under /var/lib/mysql as - # databases. Somehow we can end up with a .pki directory, which causes the - # command to fail with this error: - # Incorrect database name '#mysql50#.pki' when selecting the database - # There doesn't seem to be anything in the directory, so remove it. - rm -rf /var/lib/mysql/.pki - - mysql_upgrade --host=${DB_HOST} --port=${DB_PORT} --user=root --password="${DB_ROOT_PASSWORD}" 2>&1 | tee -a ${MARIADB_LOG_DIR}/mariadb-upgrade.log - exit 0 -fi - -if [[ "${!BOOTSTRAP_ARGS[@]}" ]]; then - ARGS="${BOOTSTRAP_ARGS}" -fi diff --git a/docker/mariadb/mariadb-server/healthcheck_mariadb b/docker/mariadb/mariadb-server/healthcheck_mariadb deleted file mode 100755 index 0d9f59a95..000000000 --- a/docker/mariadb/mariadb-server/healthcheck_mariadb +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -set -e - -MYSQL_USERNAME="${MYSQL_USERNAME:=-haproxy}" -MYSQL_TIMEOUT=10 - -MYSQL_CMDLINE="mysql -sN --connect-timeout=${MYSQL_TIMEOUT} -u ${MYSQL_USERNAME}" - -WSREP_STATUS=$($MYSQL_CMDLINE -e "SHOW STATUS LIKE 'wsrep_local_state_comment';" | awk '{print $2}') - -if [[ "${WSREP_STATUS}" == "Synced" ]] -then - echo "MariaDB Galera Cluster Node is synced." - exit 0 -else - echo "MariaDB Galera Cluster Node is NOT synced" - exit 1 -fi diff --git a/docker/mariadb/mariadb-server/mariadb_sudoers b/docker/mariadb/mariadb-server/mariadb_sudoers deleted file mode 100644 index 150534e16..000000000 --- a/docker/mariadb/mariadb-server/mariadb_sudoers +++ /dev/null @@ -1 +0,0 @@ -%kolla ALL=(root) NOPASSWD: /usr/local/bin/kolla_security_reset diff --git a/docker/mariadb/mariadb-server/security_reset.expect b/docker/mariadb/mariadb-server/security_reset.expect deleted file mode 100644 index 06f1d5c8e..000000000 --- a/docker/mariadb/mariadb-server/security_reset.expect +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/expect -f - -if [catch {set timeout $env(DB_MAX_TIMEOUT)}] {set timeout 10} -spawn mariadb-secure-installation -expect { - timeout { send_user "\nFailed to get 'Enter current password for root (enter for none):' prompt\n"; exit 1 } - eof { send_user "\nFailed to get 'Enter current password for root (enter for none):' prompt\n"; exit 1 } - "Enter current password for root (enter for none):" -} -send "\r" - -expect { - timeout { send_user "\nFailed to get 'Switch to unix_socket authentication [Y/n] ' prompt\n"; exit 1 } - eof { send_user "\nFailed to get 'Switch to unix_socket authentication [Y/n]' prompt\n"; exit 1 } - "Switch to unix_socket authentication \\\[Y/n\\\] " -} -send "n\r" - -expect { - timeout { send_user "\nFailed to get 'Change the root password? [Y/n]' prompt\n"; exit 1 } - eof { send_user "\nFailed to get 'Change the root password? [Y/n]' prompt\n"; exit 1 } - "Change the root password? \\\[Y/n\\\] " -} -send "y\r" - -expect { - timeout { send_user "\nFailed to get 'New password:' prompt\n"; exit 1 } - eof { send_user "\nFailed to get 'New password:' prompt\n"; exit 1 } - "New password:" -} -send "$env(DB_ROOT_PASSWORD)\r" - -expect { - timeout { send_user "\nFailed to get 'Re-enter new password:' prompt\n"; exit 1 } - eof { send_user "\nFailed to get 'Re-enter new password:' prompt\n"; exit 1 } - "Re-enter new password:" -} -send "$env(DB_ROOT_PASSWORD)\r" - -expect { - timeout { send_user "\nFailed to get 'Remove anonymous users?' prompt\n"; exit 1 } - eof { send_user "\nFailed to get 'Remove anonymous users?' prompt\n"; exit 1 } - "Remove anonymous users?" -} -send "y\r" - -expect { - timeout { send_user "\nFailed to get 'Disallow root login remotely?' prompt\n"; exit 1 } - eof { send_user "\nFailed to get 'Disallow root login remotely?' prompt\n"; exit 1 } - "Disallow root login remotely?" -} -send "n\r" - -expect { - timeout { send_user "\nFailed to get 'Remove test database and access to it?' prompt\n"; exit 1 } - eof { send_user "\nFailed to get 'Remove test database and access to it?' prompt\n"; exit 1 } - "Remove test database and access to it?" -} -send "y\r" - -expect { - timeout { send_user "\nFailed to get 'Reload privilege tables now?' prompt\n"; exit 1 } - eof { send_user "\nFailed to get 'Reload privilege tables now?' prompt\n"; exit 1 } - "Reload privilege tables now?" -} -send "y\r" -expect eof -- cgit v1.2.3