【MySQL 8.0.22】 レプリケーション on Docker


Dockerを使ってMySQLのレプリケーション環境を構築していきたいと思います。

Dockerfileやdocker-composeを使った構築方法に関しては、結構たくさん記事があるので、
趣向を変えてコマンドだけで構築していきたいなと思います。

  • 環境
    • macOS Catalina ver: 10.15.17
    • Docker Engine ver: 19.03.13

完成図

ネットワークの作成

「192.168.220.0/24」の範囲で「mysql_net」という名前のネットワークを構築します。
コマンドは以下です。

docker network create -d bridge --subnet=192.168.220.0/24 mysql_net

作成されたことを確認したい場合は以下のコマンドを使ってみてください。

docker network ls -f "NAME=mysql_net"

bridgeモードについては以下の方の記事がわかりやすいです。

Docker network 概論 - Qiita

ボリュームの作成

replicationを行うのに必要なダンプを効率よく共有できるように共有するvolumeを準備します。
コマンドは以下です。

docker volume create --name shared-dump

作成されたことを確認したい場合は以下のコマンドを使ってみてください。

docker volume ls -f "NAME=shared-dump"

コンテナの作成

今回は、現在最新バージョンである8.0.22を利用します。
以下のコマンドでイメージをpullしてください。

docker pull mysql:8.0.22

以下のコマンドでイメージがpullできていることを確認して下さい。

docker images mysql:8.0.22

MasterとSlaveのコンテナを一つずつ作成します。

# Master
docker run -d \
           -v shared-dump:/dump \
           -e MYSQL_ROOT_PASSWORD=root \
           --network mysql_net --ip 192.168.220.50 \
           --name master \
           mysql:8.0.22

# Slave
docker run -d \
           -v shared-dump:/dump \
           -e MYSQL_ROOT_PASSWORD=root \
           --network mysql_net --ip 192.168.220.100 \
           --name slave \
           mysql:8.0.22

MasterとSlaveによるコマンドの違いはほとんどありません。
一応、どちらがMasterでどちらがSlaveであるかをわかりやすくするためにコンテナの名前を変えています。異なる点としてはこのぐらいです。

ちょっとした検証でコンテナを立ち上げる際は、--rmオプションをつけて立ち上げるのが個人的に好きです(諸事情があって今回は付与できませんでしたが)。--rmオプションは停止と同時にコンテナを削除してくれるのでゴミが残らないところがポイントです。

設定ファイルを置き換える

設定ファイルの置き換えがrunサブコマンドでできればいいんですけどね。

Dockerfileやdocker-composeだとこの辺りの処理がとても楽ですよね。
この辺りがコマンドだと辛いですねー。

というわけで、レプリケーションに必要なserver-idの設定値をそれぞれの環境に入れていきたいと思います。

# master
docker exec master bash -c "echo 'server-id = 101' >> /etc/mysql/my.cnf"

# slave
docker exec slave bash -c "echo 'server-id = 102' >> /etc/mysql/my.cnf"

設定を更新するためにmysqlを再起動する必要があります。
ただ、dockerを再起動するとコンテナは停止しちゃうのでコンテナの再起動が必要です。
そのため今回は--rmオプションが使えませんでした。

以下のコマンドを実行して再起動します。

docker restart $(docker ps -aq -f "NAME=master" -f "NAME=slave")

確認には以下を実行してみてください。

for role in master slave; do
  docker exec ${role} bash -c "mysql -uroot -proot -e 'select @@server_id;'"
done

レプリケーション

レプリケーション以外の環境は概ねできました。

ここからはあまりdocker関係ないですが、レプリケーションの検証まで行ってみます。

検証用にデータベースを作成する

for role in master slave; do
  docker exec ${role} bash -c "mysql -uroot -proot -e 'create database repl;'"
done

レプリケーションを開始するための準備

ここから先の作業はcommandを渡して作業すると面倒なのでコンテナに入ることにします。

# 以下のコマンドでmasterコンテナに入ります。
docker exec -it master bash

# rootユーザでmysqlに入ります。
mysql -uroot -proot

適当なテーブルを作成し、データを投入します。

-- 適当なデータを投入します。
create table repl.users (id int unsigned not null auto_increment, name varchar(255) not null, primary key(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
insert into repl.users (name) values ("sample taro"), ("sample hanako");

-- slaveが接続するために必要なレプリケーションユーザを作成します
create user 'repl'@'192.168.220.100' identified with mysql_native_password by 'pQ8gDdzQHEtX@b8';
grant replication slave, replication client on *.* to 'repl'@'192.168.220.100';

-- 現在のマスターの状態を確認します。FileとPosition列の値をメモしてください
show master status;

quit

ダンプを取得します。

mysqldump -uroot -proot repl> /dump/repl_dump.sql

レプリケーションを開始する

次はslaveコンテナでの操作となりますので、まずはコンテナに入りましょう。

docker exec -it slave bash

レプリケーションを開始するまでの流れとしては以下です。

  1. dumpを取り込む
  2. レプリケーションの設定を行う
  3. レプリケーションを開始する

まずは、ダンプを取り込みます。

mysql -uroot -proot repl < /dump/repl_dump.sql

レプリケーションの設定を行う前にmasterと差分があることを確認しましょう(mysqlへログインしている前提です)。
masterでの作業でメモした「File」と「Position」の値を利用します。

-- replicationの設定を行います。
change master to
master_host='192.168.220.50',
master_user='repl',
master_password='pQ8gDdzQHEtX@b8',
master_log_file='binlog.000003',
master_log_pos=1518;

-- レプリケーションを開始します。
start replica;

start slaveコマンドは消されるようで今後はstart replicaを使うようです。

同期を確認する

現在のrepl.usersテーブルの状態は以下です。

mysql> select * from repl.users;
+----+---------------+
| id | name          |
+----+---------------+
|  1 | sample taro   |
|  2 | sample hanako |
+----+---------------+
2 rows in set (0.00 sec)

適当なデータを投入し、master,slave両方でテーブルの状態を確認してみます。

mysql> insert into repl.users (name) values ("takeshi"), ("tanaka"), ("sasaki");
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

-- master
mysql> select @@server_id; select * from repl.users;
+-------------+
| @@server_id |
+-------------+
|         101 |
+-------------+
1 row in set (0.00 sec)

+----+---------------+
| id | name          |
+----+---------------+
|  1 | sample taro   |
|  2 | sample hanako |
|  3 | takeshi       |
|  4 | tanaka        |
|  5 | sasaki        |
+----+---------------+
5 rows in set (0.00 sec)

-- slave
mysql> select @@server_id; select * from repl.users;
+-------------+
| @@server_id |
+-------------+
|         102 |
+-------------+
1 row in set (0.00 sec)

+----+---------------+
| id | name          |
+----+---------------+
|  1 | sample taro   |
|  2 | sample hanako |
|  3 | takeshi       |
|  4 | tanaka        |
|  5 | sasaki        |
+----+---------------+
5 rows in set (0.00 sec)

大丈夫そうですね。

最後に

本来はもう少しひねりたかったのですが、普通の検証になってしまいました(すみません)。

インフラ業務では、本番により近い環境で作業を行うかどうかで品質が大きく変わってしまいます。
なので、dockerをもっとうまく使いこなせればなーと日々感じますねー。