docker compose で構築済みの開発環境に MySQL スレーブを追加したい
やりたいこと
docker compose で構築された mysql 環境にスレーブを追加したい。 (レプリケーションの遅延に起因するバグが報告されたので、開発環境で再現したい。)
チームに展開するので、手順は可能な限り簡単にしたい。
前提
以下のような環境が存在し、それなりにデータが入っている状態とします。
docker-compose.yml
services: master-db: image: mysql:5.6 volumes: - ./master/data:/var/lib/mysql - ./master/conf.d:/etc/mysql/conf.d - ./master/initdb.d:/docker-entrypoint-initdb.d env_file: - ./master/master.env
master/conf.d/my.cnf
[mysqld] log-bin = mysql-bin server-id = 1
マスタとするため、 log-bin と server-id の設定が必要です。 (スレーブ追加時に設定しても問題ないはず。)
master/master.env
MYSQL_ROOT_PASSWORD=password MYSQL_DATABASE=hoge MYSQL_USER=master MYSQL_PASSWORD=password
mysql スレーブを追加する
なにはともあれ、 docker-compose.yml にスレーブのサービスを追加します。
docker compose up
すれば普通にサービス自体は追加されますよね。
docker-compose.yml
# ... slave-db: image: mysql:5.6 volumes: - ./slave/data:/var/lib/mysql - ./slave/conf.d:/etc/mysql/conf.d - ./slave/init.sh:/usr/local/bin/init.sh env_file: - ./slave/slave.env
slave/conf.d/my.cnf
[mysqld] server-id = 11
スレーブとするため、 server-id の設定が必要です。
slave/slave.env
MASTER_HOST=master-db MASTER_USER=root MASTER_PASSWORD=password MYSQL_ROOT_PASSWORD=password MYSQL_DATABASE=hoge MYSQL_USER=slave MYSQL_PASSWORD=password
通常の環境変数に加えて、マスタの情報である MASTER_* を記述します。
slave/init.sh
#!/bin/sh mysqldump -h $MASTER_HOST -u $MASTER_USER -p$MASTER_PASSWORD --master-data --all-databases > /tmp/master.dump until mysql -u root -p$MYSQL_ROOT_PASSWORD -e ''; do echo -n . sleep 1 done mysql -u root -p$MYSQL_ROOT_PASSWORD -e 'STOP SLAVE' mysql -u root -p$MYSQL_ROOT_PASSWORD -e "CHANGE MASTER TO MASTER_HOST = '$MASTER_HOST', MASTER_USER = '$MASTER_USER', MASTER_PASSWORD = '$MASTER_PASSWORD'" mysql -u root -p$MYSQL_ROOT_PASSWORD < /tmp/master.dump mysql -u root -p$MYSQL_ROOT_PASSWORD -e 'START SLAVE' rm /tmp/master.dump
docker compose up
で環境を立ち上げたあと、
docker compose exec slave-db sh /usr/local/bin/init.sh
で叩きます。
ここでマスタからスレーブへデータをコピーし、レプリケーションの設定を行っています。
このスクリプトがキモになりますので、内容を順を追って説明します。
マスタからデータをダンプする
mysqldump -h $MASTER_HOST -u $MASTER_USER -p$MASTER_PASSWORD --master-data --all-databases > /tmp/master.dump
--master-data
オプションでダンプする内容にバイナリログの座標を含めています。
(具体的には CHANGE MASTER TO MASTER_LOG_FILE = '...', MASTER_LOG_POS = ###
が出力されます。)
MySQL :: MySQL 5.6 リファレンスマニュアル :: 17.1.1.5 mysqldump を使用したデータスナップショットの作成
スレーブにマスタ情報をセットする
mysql -u root -p$MYSQL_ROOT_PASSWORD -e 'STOP SLAVE' mysql -u root -p$MYSQL_ROOT_PASSWORD -e "CHANGE MASTER TO MASTER_HOST = '$MASTER_HOST', MASTER_USER = '$MASTER_USER', MASTER_PASSWORD = '$MASTER_PASSWORD'"
マスタ情報を変更するため、 STOP SLAVE
でスレーブを止めます。
その後、 CHANGE MASTER TO ...
でマスタ情報(ホスト、ユーザ、パスワード)をセットします。
MASTER_HOST
(及び MASTER_PORT
)を変更するとバイナリログの座標がリセットされてしまうため、データをインポートする前に行う必要があります。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.4.2.1 CHANGE MASTER TO 構文
MASTER_HOST または MASTER_PORT オプションを指定すると、スレーブは (そのオプション値が現在の値と同じ場合でも) マスターサーバーが以前とは異なっていると見なします。この場合、マスターバイナリログファイルの名前と位置の古い値は適用されなくなったと見なされるため、このステートメントで MASTER_LOG_FILE と MASTER_LOG_POS を指定しないと、そのあとに MASTER_LOG_FILE='' と MASTER_LOG_POS=4 が暗黙のうちに付加されます。
スレーブにデータをインポートする
mysql -u root -p$MYSQL_ROOT_PASSWORD < /tmp/master.dump mysql -u root -p$MYSQL_ROOT_PASSWORD -e 'START SLAVE'
データに CHANGE MASTER TO MASTER_LOG_FILE = '...', MASTER_LOG_POS = '...'
が含まれていますので、マスターのデータがインポートされると同時にバイナリログの座標が正しくセットされます。
これでスレーブ側のセットアップができたので、 START SLAVE
でスレーブを再開させます。
おわりに
これらの変更によってファイル群を git pull
で更新したあと、以下の手順でスレーブを追加できるようになりました。
スレーブ・サービスで1コマンド叩くだけでセットアップできるので、それなりに上手くできたかなと思います。
docker compose up -d docker compose exec slave-db sh /usr/local/bin/init.sh
検証に使ったコードが↓にあります。
間違いや問題がありましたらご指摘いただけると幸いです。
※ 説明を簡単にするためにサービス同士の依存関係、認証、権限周りなどは簡略化してあります。実用する際はご注意ください。