docker-composeで手軽にgroup replicationの試験環境構築をしてみる

この記事はMySQL Casual Advent Calendar 2017の22日目です。


# はじめに
huatoです。
MySQ 5.6のサポート期限が見えてきたので最近はMySQL5.7の動作試験を進めてます。
Group　Replicationの検証目的で複数インスタンス立ち上げるのが面倒だったのでdocker-composeで手軽に
試験環境を構築してみました。
なおGroup Replication自体の説明はいろいろな方が記事を書かれているので除外してます。


# docker周りの設定

https://docs.docker.com/engine/installation/linux/docker-ce/centos/#install-docker-ce-1

CentOS7.3をインストールしてdocker動作に必要な設定をぽちぽちとすすめます。
ubuntuの環境インストール方法などもマニュアルが用意されてます。

```
yum install -y yum-utils device-mapper-persistent-data lvm2

yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

yum-config-manager --disable docker-ce-edge
yum makecache fast

yum install docker-ce
```

```Bash
cd /etc/
mkdir -p docker
vi daemon.json
```


```Bash

{
  "storage-driver": "devicemapper"
}

{
        "dns": ["8.8.8.8", "8.8.4.4"]
}

```
dockerサービス起動
service docker restart

# docker-compose

dockerコマンドでインスタンスごとに立ち上げていくこともできますが
複数環境を構築・管理するのであればdocker-composeが便利です。

https://docs.docker.com/compose/install/#install-compose

1ライナーでインストール自体は終わります。

```Bash
sudo curl -L https://github.com/docker/compose/releases/download/1.17.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod 755 /usr/local/bin/docker-compose
```
各設定ファイルを配置する場所を作成します。
cd /root/
mkdir compose


構成管理に必要となるdocker-compose.ymlを用意します。
公式イメージを使うとパスワード設定方法などの考慮をしなくてよいので楽です。

設定はreference manualを元にしています。
https://dev.mysql.com/doc/refman/5.7/en/group-replication-configuring-instances.html
性能試験に関しては考慮していないためGroup Replication動作に必要となる最低限の設定のみです。

docker-compose.yml

```Bash
version: '2'
services:
  node1:
     restart: always
     image: mysql:5.7
     networks:
          db:
           ipv4_address: 192.168.2.1
     expose:
      - "6606"
     volumes:
       - /data/mysql1:/var/lib/mysql
       - $PWD/node1.cnf:/etc/my.cnf
     privileged: true
     extra_hosts:
       - "node1:192.168.2.1"
       - "node2:192.168.2.2"
       - "node3:192.168.2.3"
     env_file: pass.env

  node2:
     restart: always
     image: mysql:5.7
     networks:
          db:
           ipv4_address: 192.168.2.2
     expose:
      - "6606"
     volumes:
       - /data/mysql2:/var/lib/mysql
       - $PWD/node2.cnf:/etc/my.cnf
     privileged: true
     extra_hosts:
       - "node1:192.168.2.1"
       - "node2:192.168.2.2"
       - "node3:192.168.2.3"
     env_file: pass.env

  node3:
     restart: always
     image: mysql:5.7
     networks:
          db:
           ipv4_address: 192.168.2.3
     expose:
      - "6606"
     volumes:
       - /data/mysql3:/var/lib/mysql
       - $PWD/node3.cnf:/etc/my.cnf
     privileged: true
     extra_hosts:
       - "node1:192.168.2.1"
       - "node2:192.168.2.2"
       - "node3:192.168.2.3"
     env_file: pass.env

  node5:
     restart: always
     image: mysql:5.7
     networks:
          db:
           ipv4_address: 192.168.2.5
     volumes:
       - /data/mysql5:/var/lib/mysql
       - $PWD/node5.cnf:/etc/my.cnf
     privileged: true
     extra_hosts:
       - "node1:192.168.2.1"
       - "node2:192.168.2.2"
       - "node3:192.168.2.3"
     env_file: pass.env

networks:
  db:
   ipam:
    config:
      - subnet: 192.168.0.0/22
```
pass.env

```Bash
MYSQL_RANDOM_ROOT_PASSWORD=yes
```

各ノードごとにmy.cnfを用意します。

node1.cnf

```Bash
[mysqld]
server_id=1
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_checksum=NONE
log_slave_updates=ON
log-bin
binlog_format=ROW
binlog_error_action=IGNORE_ERROR
binlog_rows_query_log_events
binlog_row_image=minimal
character-set-server=utf8mb4
master_info_repository=TABLE
relay_log_info_repository=TABLE
innodb_buffer_pool_dump_at_shutdown=ON
innodb_buffer_pool_load_at_startup=OFF
innodb_strict_mode=0
innodb_checksum_algorithm=innodb
innodb_flush_method=O_DIRECT
log_timestamps=SYSTEM
log_error=/var/lib/mysql/mysql.err
transaction_write_set_extraction=XXHASH64
loose-group_replication_group_name="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
loose-group_replication_start_on_boot=off
loose-group_replication_auto_increment_increment=1
loose-group_replication_local_address="192.168.2.1:6606"
loose-group_replication_group_seeds= "192.168.2.1:6606,192.168.2.2:6606,192.168.2.3:6606"
loose-group_replication_ip_whitelist="192.168.2.1,192.168.2.2,192.168.2.3,127.0.0.1"
loose-group_replication_bootstrap_group=off
```

node2.cnf,node3.cnfはnode1.cnfをコピー後
server_idの値の変更とloose-group_replication_local_addressを自身のIPに書き換えます。
my.cnfに明示的に記載していませんがauto_increment_offsetの値はgroup replication開始時にserver-idで上書きされていました。
回避策は現時点で無さそうですのでアプリケーションでauto_incrementを使っている場合は気にしたほうが良いかと。

参考:https://mysqlhighavailability.com/mysql-group-replication-auto-increment-configuration-handling/


起動
/usr/local/bin/docker-compose up -d
-dはバックグラウンド起動に必要となるオプションです。


各環境にログインします。
docker ps -a　コマンドでインスタンスのNAMESを確認しましょう

```Bash
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
ba96f05116fa        mysql:5.7           "docker-entrypoint..."   8 hours ago         Up 8 hours          3306/tcp             compose_node5_1
4eeef5032e12        mysql:5.7           "docker-entrypoint..."   8 hours ago         Up 8 hours          3306/tcp, 6606/tcp   compose_node3_1
c97ea26cc3a2        mysql:5.7           "docker-entrypoint..."   8 hours ago         Up 8 hours          3306/tcp, 6606/tcp   compose_node2_1
dd8c636252cb        mysql:5.7           "docker-entrypoint..."   8 hours ago         Up 8 hours          3306/tcp, 6606/tcp   compose_node1_1
```
docker exec -it NAMES /bin/bash　で各環境にログインできます。
デタッチはCtrl+p+qです。
Ctrl+Cすると環境が停止されますので気をつけてください。

各環境ごとに実行するクエリが異なります。
mysqlのログイン時のrootパスワードはランダム生成のためログから確認します。

docker-compose logs | grep PASS

```Bash
node2_1  | GENERATED ROOT PASSWORD: caid0aochifaeshoo5zaizooYai0kaet
node3_1  | GENERATED ROOT PASSWORD: Phahbahzoz6gee2ied0Gii9eihei9xif
node1_1  | GENERATED ROOT PASSWORD: shoa2ohChoh6eicheiBohyooxe2ooJei
node5_1  | GENERATED ROOT PASSWORD: zi9neeng1mue8ni3Oongiu2Pee4yaech
```
ログイン
mysql -uroot -p

【node1】

```Bash
reset master;
SET SQL_LOG_BIN=0;
CREATE USER 'rpl_user'@'%';
GRANT REPLICATION SLAVE ON *.* TO 'rpl_user'@'%' IDENTIFIED BY 'dummy2';
FLUSH PRIVILEGES;
CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='dummy2' FOR CHANNEL 'group_replication_recovery';
INSTALL PLUGIN group_replication SONAME 'group_replication.so';
SET SQL_LOG_BIN=1;
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;
```

【node2,node3】

```Bash
reset master;
SET SQL_LOG_BIN=0;
CREATE USER 'rpl_user'@'%';
GRANT REPLICATION SLAVE ON *.* TO 'rpl_user'@'%' IDENTIFIED BY 'dummy2';
FLUSH PRIVILEGES;
CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='dummy2' FOR CHANNEL 'group_replication_recovery';
INSTALL PLUGIN group_replication SONAME 'group_replication.so';
SET SQL_LOG_BIN=1;
START GROUP_REPLICATION;
```

5.7.19以前のMySQLバージョンから起動した場合、Group Replication起動時にエラーで失敗することがあります。
起動に失敗した場合はmysql_upgradeを実施することで解消されました。

mysql_upgrade -uroot -p


3インスタンスでの作業が終わったらマスターがどの環境になっているか確認してみます。
SHOW SLAVE STATUSでは確認できないためperformance_schemaの値を参照します。

select * from performance_schema.replication_group_members;

```Bash
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST  | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | d33b4161-d016-11e7-a59f-0242c0a80203 | 4eeef5032e12 |        3306 | ONLINE       |
| group_replication_applier | d3dabd9c-d016-11e7-a6db-0242c0a80201 | dd8c636252cb |        3306 | ONLINE       |
| group_replication_applier | d4c00f25-d016-11e7-a50d-0242c0a80202 | c97ea26cc3a2 |        3306 | ONLINE       |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
3 rows in set (0.00 sec)
```

select member_host from performance_schema.global_status inner join performance_schema.replication_group_members where variable_name='group_replication_primary_member' AND member_id=variable_value;

```Bash
+--------------+
| member_host  |
+--------------+
| dd8c636252cb |
+--------------+
1 row in set (0.00 sec)
```

最後にgroup replicationの構成をくみ上げたら各環境のmy.cnfを書き換えてください。
この設定を修正しないとサーバ再起動時にgroup replicationの設定が反映されずレプリケーショングループに自動的に戻りません。

```Bash
loose-group_replication_start_on_boot=off
=>
group_replication_start_on_boot=on
```

Group Replicationが最大9インスタンスまでという制限があるため
Group Replicationの管理対象外のスレーブをGroup Replicationのマスターにぶら下げる場合についても書いておきます。

node5.cnf

```Bash
[mysqld]
server_id=1
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_checksum=NONE
log_slave_updates=ON
log-bin
binlog_format=ROW
binlog_error_action=IGNORE_ERROR
log_error=/var/lib/mysql/mysql.err
binlog_rows_query_log_events
binlog_row_image=minimal
character-set-server=utf8mb4
master_info_repository=TABLE
relay_log_info_repository=TABLE
innodb_buffer_pool_dump_at_shutdown=ON
innodb_buffer_pool_load_at_startup=OFF
innodb_strict_mode=0
innodb_checksum_algorithm=innodb
innodb_flush_method=O_DIRECT
log_timestamps=SYSTEM
```

【node5】

```Bash
reset master;
CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='dummy2',MASTER_AUTO_POSITION = 1,MASTER_HOST='192.168.2.1';
START SLAVE;
```

通常のGTIDレプリケーションとして動作します。

```Bash
mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.2.1
                  Master_User: rpl_user
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: dd8c636252cb-bin.000001
          Read_Master_Log_Pos: 1082
               Relay_Log_File: ba96f05116fa-relay-bin.000002
                Relay_Log_Pos: 1301
        Relay_Master_Log_File: dd8c636252cb-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 1082
              Relay_Log_Space: 1507
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File:
           Master_SSL_CA_Path:
              Master_SSL_Cert:
            Master_SSL_Cipher:
               Master_SSL_Key:
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 1
                  Master_UUID: d3dabd9c-d016-11e7-a6db-0242c0a80201
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind:
      Last_IO_Error_Timestamp:
     Last_SQL_Error_Timestamp:
               Master_SSL_Crl:
           Master_SSL_Crlpath:
           Retrieved_Gtid_Set: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-3
            Executed_Gtid_Set: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:1-3
                Auto_Position: 1
         Replicate_Rewrite_DB:
                 Channel_Name:
           Master_TLS_Version:
1 row in set (0.00 sec)

ERROR:
No query specified
```

docker-composeでのGroup Replication試験環境についての説明はこれで以上ですが
Group ReplicationだけではDB運用には不十分でアプリケーションからの接続をmysql routerやProxySQL等で管理しないといけません。
機会があればそちらも記事にしたいと思います。

# 参考にさせていただいた記事

http://datacharmer.blogspot.jp/2017/01/mysql-group-replication-installation.html
https://dev.mysql.com/doc/refman/5.7/en/group-replication-launching.html