MySQL: データベースを mysqldump からのリストアでコピーする
プライベートサブネットやフィルタリングにより直接アクセスできない MySQL サーバー間で、踏み台への SSH トンネルを経由してデータベースをコピーする手順の例です。(直接アクセスできる場合も、トンネル作成手順を除けば同じように使えます。)

条件
- MySQL へアクセスできる踏み台 (Bastion) ホストへ SSH アクセスできる
- Bastion ホストには MySQL クライアントや Docker がない
- ローカル PC では Docker が動作 (MySQL クライアントは不要)
- MySQL のバージョンは同一またはダンプに互換性があること
注意事項
- コピー開始後に発生した変更は反映されません。
- データは一旦手元まで受信して折り返しています。そのぶん通信が発生するので注意。
手順
各手順は、それぞれ別のターミナルで実行します。
1. コピー元 DB クラスターへの SSH トンネルを作成
コピー元 DB クラスターへの SSH トンネルをローカル PC の 10001 番ポートに割り当てます。Docker コンテナから通信するために、ループバックでなくローカル PC の IP アドレスも指定します。
#!/bin/bash
MY_HOST=(ローカル PC の IP アドレス)
BASTION_HOST=(Bastion の IP アドレス)
BASTION_SSH_PORT=(Bastion の SSH ポート番号)
BASTION_USERNAME=(Bastion の SSH ユーザー名)
SOURCE_DB_HOST=(コピー元 DB クラスターのエンドポイント)
SOURCE_DB_PORT=(コピー元 DB クラスターのポート番号)
ssh -v -N -L "${MY_HOST}:10001:${SOURCE_DB_HOST}:${SOURCE_DB_PORT}" \
    -p "${BASTION_SSH_PORT}" "${BASTION_USERNAME}@${BASTION_HOST}"
2. コピー先 DB クラスターへの SSH トンネルを作成
コピー先 DB クラスターへの SSH トンネルをローカル PC の 10002 番ポートに割り当てます。Docker コンテナから通信するために、ループバックでなくローカル PC の IP アドレスも指定します。
#!/bin/bash
MY_HOST=(ローカル PC の IP アドレス)
BASTION_HOST=(Bastion の IP アドレス)
BASTION_SSH_PORT=(Bastion の SSH ポート番号)
BASTION_USERNAME=(Bastion の SSH ユーザー名)
TARGET_DB_HOST=(コピー先 DB クラスターのエンドポイント)
TARGET_DB_PORT=(コピー先 DB クラスターのポート番号)
ssh -v -N -L "${MY_HOST}:10002:${TARGET_DB_HOST}:${TARGET_DB_PORT}" \
    -p "${BASTION_SSH_PORT}" "${BASTION_USERNAME}@${BASTION_HOST}"
3. データベースのコピーを実行
それぞれトンネルを経由して通信しつつデータベースをコピーします。
#!/bin/bash
MY_HOST=(ローカル PC の IP アドレス)
SOURCE_DB_HOST=${MY_HOST}
SOURCE_DB_PORT=10001
SOURCE_DB_USERNAME=(コピー元 DB のユーザー名)
SOURCE_DB_PASSWORD=(コピー元 DB のパスワード)
TARGET_DB_HOST=${MY_HOST}
TARGET_DB_PORT=10002
TARGET_DB_USERNAME=(コピー先 DB のユーザー名)
TARGET_DB_PASSWORD=(コピー先 DB のパスワード)
DATABASES=(コピーするデータベース名(複数指定はスペースで区切る))
MYSQL_IMAGE=mysql:8
#MYSQL_IMAGE=amd64/mysql:5.7.40  # for MySQL 5.7
#MYSQL_IMAGE=mariadb:10.1  # MySQL 5.6 compatible
#MYSQL_IMAGE=mariadb:10.3  # MySQL 5.7 compatible
for dbname in ${DATABASES}
do
    docker run -it --rm -p "${SOURCE_DB_PORT}" -p "${TARGET_DB_PORT}" "${MYSQL_IMAGE}" bash -c "
        # Create database
        echo \"--> Create database: ${dbname}\"
        #mysql -h \"${TARGET_DB_HOST}\" -P \"${TARGET_DB_PORT}\" -u \"${TARGET_DB_USERNAME}\" -p\"${TARGET_DB_PASSWORD}\" \
        #    -e \"DROP DATABASE IF EXISTS \\\`${dbname}\\\`;\"
        mysql -h \"${TARGET_DB_HOST}\" -P \"${TARGET_DB_PORT}\" -u \"${TARGET_DB_USERNAME}\" -p\"${TARGET_DB_PASSWORD}\" \
            -e \"CREATE DATABASE \\\`${dbname}\\\`;\"
        # Copy database
        echo \"--> Copy database: ${dbname}\"
        mysqldump -h \"${SOURCE_DB_HOST}\" -P \"${SOURCE_DB_PORT}\" -u \"${SOURCE_DB_USERNAME}\" -p\"${SOURCE_DB_PASSWORD}\" \
            --single-transaction --compress --order-by-primary --set-gtid-purged=OFF \"${dbname}\" \
        | mysql -h \"${TARGET_DB_HOST}\" -P \"${TARGET_DB_PORT}\" -u \"${TARGET_DB_USERNAME}\" -p\"${TARGET_DB_PASSWORD}\" \
            --compress --database \"${dbname}\"
    "
done
- MySQL クライアントを Docker コンテナ上で動作
- コンテナからホストの SSH トンネルポートへ通信をおこなう
 
- mysqldump に --single-transactionオプションを指定することで、テーブルロックはおこなわない- mysql:5.7.41 では Couldn't execute 'FLUSH TABLES WITH READ LOCK'というエラーになってしまうため、5.7.40 を使用
 
- mysql:5.7.41 では 
- MySQL サーバーに直接アクセスできる場合は、トンネルを作成せず TARGET_DB_HOST/TARGET_DB_PORT に直接データベースのエンドポイントを指定すれば良い
ダンプを一旦ファイル保存してからコピーする場合は、以下のようにします。この場合、pv (Pipe Viewer) で進捗を表示することもできます。
# Dump from source
for dbname in ${DATABASES}
do
    dumpfile="/work/${dbname}.db"
    docker run -it --rm -v "$(pwd):/work" -p "${SOURCE_DB_PORT}" "${MYSQL_IMAGE}" bash -eu -c "
        # Dump database
        echo \"--> Dump database: ${dbname}\"
        mysqldump -h \"${SOURCE_DB_HOST}\" -P \"${SOURCE_DB_PORT}\" -u \"${SOURCE_DB_USERNAME}\" -p\"${SOURCE_DB_PASSWORD}\" \
            --single-transaction --order-by-primary --set-gtid-purged=OFF \
            --compression-algorithms=zlib \"${dbname}\" > \"${dumpfile}\"
    "
done
# Copy to target
for dbname in ${DATABASES}
do
    dumpfile="/work/${dbname}.db"
    docker run -it --rm -v "$(pwd):/work" -p "${TARGET_DB_PORT}" "${MYSQL_IMAGE}" bash -eu -c "
        # install pv(Pipe Viewer)
        #yum install -y pv  # for amd64/mysql:5.7.40
        #apt-get update && apt-get install -y pv  # for mariadb
        microdnf install -y epel-release && microdnf install -y pv  # for mysql:8 (oraclelinux)
        # Create database
        echo \"--> Create database: ${dbname}\"
        #mysql -h \"${TARGET_DB_HOST}\" -P \"${TARGET_DB_PORT}\" -u \"${TARGET_DB_USERNAME}\" -p\"${TARGET_DB_PASSWORD}\" \
        #    -e \"DROP DATABASE IF EXISTS \\\`${dbname}\\\`;\"
        mysql -h \"${TARGET_DB_HOST}\" -P \"${TARGET_DB_PORT}\" -u \"${TARGET_DB_USERNAME}\" -p\"${TARGET_DB_PASSWORD}\" \
            -e \"CREATE DATABASE \\\`${dbname}\\\`;\"
        # Restore database
        echo \"--> Restore database: ${dbname}\"
        pv \"${dumpfile}\" \
        | mysql -h \"${TARGET_DB_HOST}\" -P \"${TARGET_DB_PORT}\" -u \"${TARGET_DB_USERNAME}\" -p\"${TARGET_DB_PASSWORD}\" \
            --compression-algorithms=zlib --database \"${dbname}\"
    "
done