From 59616d4423712a9b4b55d7025c95c8bddd504850 Mon Sep 17 00:00:00 2001 From: Michal Nasiadka Date: Thu, 27 Feb 2020 11:36:56 +0100 Subject: [PATCH] Add mariadb_clustercheck image Add mariadb_clustercheck image with xinetd and clustercheck binary for haproxy checking of galera status. Reorganise existing mariadb images by creating mariadb-base and mariadb-server. Existing mariadb image will be deprecated (in a separate change). Change-Id: Ib601f64e1514521154beeaac40f0c058a7119408 --- docker/mariadb/mariadb-base/Dockerfile.j2 | 28 +++++++ .../mariadb-clustercheck/Dockerfile.j2 | 28 +++++++ docker/mariadb/mariadb-server/Dockerfile.j2 | 79 +++++++++++++++++++ docker/mariadb/{ => mariadb-server}/backup.sh | 0 .../{ => mariadb-server}/extend_start.sh | 0 .../{ => mariadb-server}/healthcheck_mariadb | 0 .../{ => mariadb-server}/mariadb_sudoers | 0 .../security_reset.expect | 0 docker/mariadb/{ => mariadb}/Dockerfile.j2 | 0 docker/mariadb/mariadb/backup.sh | 48 +++++++++++ docker/mariadb/mariadb/extend_start.sh | 58 ++++++++++++++ docker/mariadb/mariadb/healthcheck_mariadb | 17 ++++ docker/mariadb/mariadb/mariadb_sudoers | 1 + docker/mariadb/mariadb/security_reset.expect | 58 ++++++++++++++ 14 files changed, 317 insertions(+) create mode 100644 docker/mariadb/mariadb-base/Dockerfile.j2 create mode 100644 docker/mariadb/mariadb-clustercheck/Dockerfile.j2 create mode 100644 docker/mariadb/mariadb-server/Dockerfile.j2 rename docker/mariadb/{ => mariadb-server}/backup.sh (100%) rename docker/mariadb/{ => mariadb-server}/extend_start.sh (100%) rename docker/mariadb/{ => mariadb-server}/healthcheck_mariadb (100%) rename docker/mariadb/{ => mariadb-server}/mariadb_sudoers (100%) rename docker/mariadb/{ => mariadb-server}/security_reset.expect (100%) rename docker/mariadb/{ => mariadb}/Dockerfile.j2 (100%) create mode 100644 docker/mariadb/mariadb/backup.sh create mode 100644 docker/mariadb/mariadb/extend_start.sh create mode 100755 docker/mariadb/mariadb/healthcheck_mariadb create mode 100644 docker/mariadb/mariadb/mariadb_sudoers create mode 100644 docker/mariadb/mariadb/security_reset.expect diff --git a/docker/mariadb/mariadb-base/Dockerfile.j2 b/docker/mariadb/mariadb-base/Dockerfile.j2 new file mode 100644 index 0000000000..c9e5669491 --- /dev/null +++ b/docker/mariadb/mariadb-base/Dockerfile.j2 @@ -0,0 +1,28 @@ +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') }} + +{{ macros.enable_extra_repos(['mariadb']) }} + +{% if base_package_type == 'rpm' %} + {% set mariadb_base_packages = [ + 'mariadb', + ] %} + +{% elif base_package_type == 'deb' %} + {% set mariadb_base_packages = [ + 'mariadb-client', + ] %} +{% endif %} + +{{ macros.install_packages(mariadb_base_packages | customizable("packages")) }} + +{% block mariadb_base_footer %}{% endblock %} +{% block footer %}{% endblock %} diff --git a/docker/mariadb/mariadb-clustercheck/Dockerfile.j2 b/docker/mariadb/mariadb-clustercheck/Dockerfile.j2 new file mode 100644 index 0000000000..ea3114b6e6 --- /dev/null +++ b/docker/mariadb/mariadb-clustercheck/Dockerfile.j2 @@ -0,0 +1,28 @@ +FROM {{ namespace }}/{{ image_prefix }}mariadb-base:{{ tag }} +LABEL maintainer="{{ maintainer }}" name="{{ image_name }}" build-date="{{ build_date }}" + +{% block mariadb_clustercheck_header %}{% endblock %} + +{% import "macros.j2" as macros with context %} + +{% if base_package_type == 'rpm' %} + {% set mariadb_clustercheck_packages = [ + 'mariadb-server-galera', + 'xinetd' + ] %} + +{% elif base_package_type == 'deb' %} + {% set mariadb_clustercheck_packages = [ + 'xinetd' + ] %} +{% endif %} + +{{ macros.install_packages(mariadb_clustercheck_packages | customizable("packages")) }} + +{% if base_package_type == 'deb' %} +RUN curl -sSL -o /usr/bin/clustercheck https://src.fedoraproject.org/rpms/mariadb/raw/10.3/f/clustercheck.sh \ + && chmod 755 /usr/bin/clustercheck +{% endif %} + +{% block mariadb_clustercheck_footer %}{% endblock %} +{% block footer %}{% endblock %} diff --git a/docker/mariadb/mariadb-server/Dockerfile.j2 b/docker/mariadb/mariadb-server/Dockerfile.j2 new file mode 100644 index 0000000000..16cc8ae05d --- /dev/null +++ b/docker/mariadb/mariadb-server/Dockerfile.j2 @@ -0,0 +1,79 @@ +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(mgoddard): EPEL required for pv package #} +{{ macros.enable_extra_repos(['epel', 'mariadb']) }} + +{% if base_package_type == 'rpm' %} + {% set mariadb_packages = [ + 'expect', + 'galera', + 'hostname', + 'mariadb-backup', + 'mariadb-server-galera', + 'mariadb-server-utils', + 'pv', + 'rsync', + 'tar' + ] %} + +{% elif base_package_type == 'deb' %} + {% 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 +RUN chmod 755 /usr/local/bin/kolla_extend_start \ + && chmod 755 /usr/local/bin/kolla_security_reset \ + && 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 %} + +COPY backup.sh /usr/local/bin/kolla_mariadb_backup.sh +RUN chmod 755 /usr/local/bin/kolla_mariadb_backup.sh + +{% 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 %} + +{% block mariadb_footer %}{% endblock %} +{% block footer %}{% endblock %} + +USER mysql diff --git a/docker/mariadb/backup.sh b/docker/mariadb/mariadb-server/backup.sh similarity index 100% rename from docker/mariadb/backup.sh rename to docker/mariadb/mariadb-server/backup.sh diff --git a/docker/mariadb/extend_start.sh b/docker/mariadb/mariadb-server/extend_start.sh similarity index 100% rename from docker/mariadb/extend_start.sh rename to docker/mariadb/mariadb-server/extend_start.sh diff --git a/docker/mariadb/healthcheck_mariadb b/docker/mariadb/mariadb-server/healthcheck_mariadb similarity index 100% rename from docker/mariadb/healthcheck_mariadb rename to docker/mariadb/mariadb-server/healthcheck_mariadb diff --git a/docker/mariadb/mariadb_sudoers b/docker/mariadb/mariadb-server/mariadb_sudoers similarity index 100% rename from docker/mariadb/mariadb_sudoers rename to docker/mariadb/mariadb-server/mariadb_sudoers diff --git a/docker/mariadb/security_reset.expect b/docker/mariadb/mariadb-server/security_reset.expect similarity index 100% rename from docker/mariadb/security_reset.expect rename to docker/mariadb/mariadb-server/security_reset.expect diff --git a/docker/mariadb/Dockerfile.j2 b/docker/mariadb/mariadb/Dockerfile.j2 similarity index 100% rename from docker/mariadb/Dockerfile.j2 rename to docker/mariadb/mariadb/Dockerfile.j2 diff --git a/docker/mariadb/mariadb/backup.sh b/docker/mariadb/mariadb/backup.sh new file mode 100644 index 0000000000..e56450171f --- /dev/null +++ b/docker/mariadb/mariadb/backup.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +set -eu +set -o pipefail + +# Execute a full backup +backup_full() { + echo "Taking a full backup" + mariabackup \ + --defaults-file=/etc/mysql/my.cnf \ + --backup \ + --stream=xbstream \ + --history=$(date +%d-%m-%Y) | gzip > \ + $BACKUP_DIR/mysqlbackup-$(date +%d-%m-%Y-%s).qp.xbc.xbs.gz +} + +# Execute an incremental backup +backup_incremental() { + echo "Taking an incremental backup" + mariabackup \ + --defaults-file=/etc/mysql/my.cnf \ + --backup \ + --stream=xbstream \ + --incremental-history-name=$(date +%d-%m-%Y) \ + --history=$(date +%d-%m-%Y) | gzip > \ + $BACKUP_DIR/incremental-$(date +%H)-mysqlbackup-$(date +%d-%m-%Y-%s).qp.xbc.xbs.gz +} + +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/extend_start.sh b/docker/mariadb/mariadb/extend_start.sh new file mode 100644 index 0000000000..72f60c857a --- /dev/null +++ b/docker/mariadb/mariadb/extend_start.sh @@ -0,0 +1,58 @@ +#!/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 + 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 +} + +# 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 + bootstrap_db + 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}" + exit 0 +fi + +if [[ "${!BOOTSTRAP_ARGS[@]}" ]]; then + ARGS="${BOOTSTRAP_ARGS}" +fi diff --git a/docker/mariadb/mariadb/healthcheck_mariadb b/docker/mariadb/mariadb/healthcheck_mariadb new file mode 100755 index 0000000000..b79b19bb47 --- /dev/null +++ b/docker/mariadb/mariadb/healthcheck_mariadb @@ -0,0 +1,17 @@ +#!/bin/bash + +MYSQL_USERNAME="${MYSQL_USERNAME:=-haproxy}" +MYSQL_TIMEOUT=10 + +MYSQL_CMDLINE="mysql -nNE --connect-timeout=${MYSQL_TIMEOUT} -u ${MYSQL_USERNAME}" + +WSREP_STATUS=$($MYSQL_CMDLINE -e "SHOW STATUS LIKE 'wsrep_local_state_comment';") + +if [[ "${WSREP_STATUS}" == "Synced" ]] +then + echo "MariaDB Galera Cluster Node is synced." + exit 0 +else + echo "MariaDB Galera Cluster Node is NOT synced" + exit 0 +fi diff --git a/docker/mariadb/mariadb/mariadb_sudoers b/docker/mariadb/mariadb/mariadb_sudoers new file mode 100644 index 0000000000..150534e165 --- /dev/null +++ b/docker/mariadb/mariadb/mariadb_sudoers @@ -0,0 +1 @@ +%kolla ALL=(root) NOPASSWD: /usr/local/bin/kolla_security_reset diff --git a/docker/mariadb/mariadb/security_reset.expect b/docker/mariadb/mariadb/security_reset.expect new file mode 100644 index 0000000000..6d2755e4d4 --- /dev/null +++ b/docker/mariadb/mariadb/security_reset.expect @@ -0,0 +1,58 @@ +#!/usr/bin/expect -f + +if [catch {set timeout $env(DB_MAX_TIMEOUT)}] {set timeout 10} +spawn mysql_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 'Set root password?' prompt\n"; exit 1 } + eof { send_user "\nFailed to get 'Set root password?' prompt\n"; exit 1 } + "Set root password?" +} +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