From 64dfc608ab4997f905fc3d46662fed6455d039d0 Mon Sep 17 00:00:00 2001 From: tsiji Date: Thu, 23 Jul 2026 14:55:54 +0800 Subject: [PATCH] deploy: add native mysql clone helper for 110 --- ops/110-native-mysql/README.md | 11 ++++++ ops/110-native-mysql/clone-replica.sh | 55 +++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100755 ops/110-native-mysql/clone-replica.sh diff --git a/ops/110-native-mysql/README.md b/ops/110-native-mysql/README.md index 9f28fdf..fb12374 100644 --- a/ops/110-native-mysql/README.md +++ b/ops/110-native-mysql/README.md @@ -56,3 +56,14 @@ FROM performance_schema.replication_applier_status; Both replication service states must be `ON`, both read-only values must be `1`, and the connection error number must be `0` before ProxySQL can use the host. + +Clone the second and backup replicas from the first MySQL 8.0.46 replica with +a temporary, root-readable secret file that is never committed: + +```bash +sudo ./clone-replica.sh 192.168.110.73 /path/to/clone-secret +``` + +The donor account must be limited to `BACKUP_ADMIN`. Remove that account and +all temporary secret files after both clones have completed and replication +has been verified. diff --git a/ops/110-native-mysql/clone-replica.sh b/ops/110-native-mysql/clone-replica.sh new file mode 100755 index 0000000..c71dd69 --- /dev/null +++ b/ops/110-native-mysql/clone-replica.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $EUID -ne 0 || $# -ne 2 ]]; then + echo "usage: sudo $0 " >&2 + exit 2 +fi + +donor_host=$1 +secret_file=$(realpath "$2") + +if [[ ! $donor_host =~ ^192\.168\.110\.[0-9]{1,3}$ ]]; then + echo "invalid donor IP: $donor_host" >&2 + exit 2 +fi + +if [[ ! -f $secret_file || ! -s $secret_file ]]; then + echo "clone secret file is missing or empty: $secret_file" >&2 + exit 2 +fi + +clone_secret=$(tr -d '\r\n' < "$secret_file") +if [[ ! $clone_secret =~ ^[0-9a-fA-F]{32}$ ]]; then + echo "clone secret must contain exactly 32 hexadecimal characters" >&2 + unset clone_secret + exit 2 +fi + +restore_read_only() { + mysql --protocol=socket -uroot --force \ + -e "SET GLOBAL read_only=ON; SET GLOBAL super_read_only=ON" \ + >/dev/null 2>&1 || true +} + +if ! mysql --protocol=socket -uroot -Nse \ + "SELECT PLUGIN_STATUS FROM information_schema.PLUGINS WHERE PLUGIN_NAME='clone'" \ + | grep -qx ACTIVE; then + trap restore_read_only EXIT + mysql --protocol=socket -uroot <<'SQL' +SET SESSION sql_log_bin=0; +SET GLOBAL super_read_only=OFF; +SET GLOBAL read_only=OFF; +INSTALL PLUGIN clone SONAME 'mysql_clone.so'; +SET GLOBAL read_only=ON; +SET GLOBAL super_read_only=ON; +SQL + trap - EXIT +fi + +mysql --protocol=socket -uroot -e \ + "SET GLOBAL clone_valid_donor_list='${donor_host}:3306'; CLONE INSTANCE FROM 'clone_110'@'${donor_host}':3306 IDENTIFIED BY '${clone_secret}'" +unset clone_secret + +mysql --protocol=socket -uroot -e \ + "SELECT STATE,SOURCE,ERROR_NO,ERROR_MESSAGE FROM performance_schema.clone_status; SELECT @@server_id,@@read_only,@@super_read_only,@@gtid_mode"