【docker-composeでMysqlのMaster/Slave構成を作ってみた】

dockerでmysqlのマスター・スレーブ構成をつくるにはどうすればいいのか気になったので実際に作ってみることにした。

###そもそもどうやってレプリケーションは行われるのか？
まず、最低限必要な設定は以下の項目です。

masterと各slaveがそれぞれ異なるserver-idを持っていること
［master］バイナリログが有効となっていること
［master］スレーブのIOスレッドが接続するためのREPLICATIONSLAVE権限を持つユーザが存在すること
［slave］IOスレッドがCHANGE MASTER TOコマンドで指定するマスターに接続可能であること

###【図解】
![020192A5-4863-4964-AABA-C2165C857B52.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530710/540007bd-9128-0bfe-6c8c-6c13d85715f7.jpeg)

この図解では、GTIDに基づくレプリケーションの場合の流れを説明している。
GTIDとは、`server_id:taransaction_id`の組み合わせで、これをmasterのbinaryLogに書き込み、slaveのrelayLogに転送する。slave側ではこのGTIDに基づいて読み込み済みかそうでないかを判別する。そうでない場合にslaveのmysqlに書き込まれる。

GTIDベースのレプリケーションを行う場合は、`MASTER_AUTO_POSITION = 1` をCHANGE MASTER TOコマンドで指定する。

今後、説明する内容は、GTIDベースのレプリケーションを行う場合の設定である。

###１，実際のdocker-composeとDockerfileの設定

slave側で、masterと同じ名前でアカウントを作るように`environment`で指定したりすると、レプリケーション時にうまくいかなかったりしたので注意。

```yaml
version: '3'
services:
  master_mysql:
    build: ./mysql/master
    container_name: 'master_mysql'
    ports:
      - "3306:3306"
    environment:
        MYSQL_ROOT_PASSWORD: pass
        MYSQL_DATABASE: master_database
        MYSQL_USER: ken3pei
        MYSQL_PASSWORD: pass
    volumes:
      # 初期データを投入するSQLが格納されているdir
      - ./db/sql/master:/docker-entrypoint-initdb.d
      # 永続化するときにマウントするdir
      - ./mysql/master/volumes:/var/lib/mysql
  slave_mysql:
    build: ./mysql/slave
    container_name: 'slave_mysql'
    ports:
      - "6612:3306"
    environment:
        MYSQL_ROOT_PASSWORD: pass
    volumes:
      - ./db/sql/slave:/docker-entrypoint-initdb.d
      - ./mysql/slave/volumes:/var/lib/mysql
    depends_on:
      - master_mysql
  php:
    build: ./php
    ports:
      - '80:80'
    volumes:
      - ./php:/var/www/html
```

```docker
# master側
FROM mysql:5.7

COPY ./master.cnf /etc/mysql/conf.d/
```

```docker
# slave側
FROM mysql:5.7

COPY ./slave.cnf /etc/mysql/conf.d/
```

###２，build時に実行するSQLファイル

・replicaユーザーのホストを%にしているのは、IPアドレスか％でしかレプリケーションが成功しなかったからです。本当はコンテナ名で名前解決したかったのですができませんでした。（なぜ...）

・注：CHANGE MASTER TOコマンドは、`START SLAVE`状態では使えないので、間違えて打ち直す場合などは、`STOP SLAVE`コマンドでレプリケーションを停止してからにしましょう。

・IPアドレスを指定する場合は、`hostname -i`コマンドをマスター側コンテナで打ち、IPアドレスを確認して以下のsql文の%と置き換えればOKです。

・きちんとユーザーが創られているかは、`mysql> select user, host from mysql.user;` コマンドで確認できる。

```init_master.sql
CREATE USER 'replica'@'%' IDENTIFIED BY 'replica';
GRANT REPLICATION SLAVE ON *.* TO 'replica'@'%';
```

```init_slave.sql
CHANGE MASTER TO MASTER_HOST='master_mysql', 
    MASTER_PORT=3306, 
    MASTER_USER='replica', 
    MASTER_PASSWORD='replica', 
    MASTER_AUTO_POSITION = 1;

START SLAVE;
```

###３，master/slaveそれぞれの設定ファイル

・server-idでそれぞれ一意なidをもたせている。

・log-binによって、master側のbinaryLogを有効にしなければレプリケーションはできない。slave側で、log-binを指定しなくともレプリケーションはできるが、有効にすることでデータバックアップとクラッシュリカバリに利用できる。

・最初は`gtid-mode=ON`を指定するだけだと、「enforce-gtid-consistencyをONにしてから行いなさい」といった内容のエラーが出た。なのでenforce-gtid-consistencyを追加した。

・実は、`read_only`指定だとroot権限による書き込みは制御できない。そのためroot権限による書き込みも禁止したければ、Mysql5.7以降に追加された、`super_read_only=1` オプションを指定すればいい

・これら設定が反映されているかは、`mysql> SHOW VARIABLES;`コマンドを叩いて確認できる。

```master.cnf
[mysqld] 
server-id = 1
log-bin
enforce-gtid-consistency=ON
gtid-mode=ON
```

```slave.cnf
[mysqld] 
server-id = 2
log-bin
enforce-gtid-consistency=ON
gtid-mode=ON
read_only
```

###４，実際のディレクトリ構成

![60EEF18E-BC0F-4E72-8DDE-CBF941689EC1_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530710/fe222372-f534-759c-5721-3cd6fe566285.jpeg)


###５，レプリケーションができているか確認する。

・上記のように設定できたら`docker-compose up`などでビルドして確認する。

・slave側のコンテナに入り、さらにmysqlサーバーに入る。そこで以下のコマンドを入力。するとズラッと結果がでてくるので、そのなかのSlave_IO_RunningとSlave_SQL_RunningがYESになっていたらOK！

```sql
mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: master_mysql
                  Master_User: replica
                  Master_Port: 3306
				                ・・・・・
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
												・・・・・
												・・・・・
```

なにもしなくても、Master側で作るように指定したmaster_databaseというDBが反映されている。

```sql
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| master_database    |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
```

参考にしたサイト

[https://dev.mysql.com/doc/refman/5.6/ja/replication-howto-masterbaseconfig.html](https://dev.mysql.com/doc/refman/5.6/ja/replication-howto-masterbaseconfig.html)

[https://qiita.com/marienplatz/items/f5b544a29353bc0ad409](https://qiita.com/marienplatz/items/f5b544a29353bc0ad409)

[https://www.seeds-std.co.jp/blog/creators/2020-12-17-145757/](https://www.seeds-std.co.jp/blog/creators/2020-12-17-145757/)