MySQLのグループレプリケーションについて学習してみた

# はじめに
クラウド事業部に所属しているnanashi_yd(H.A)と申します。
今年から自社に転職し、現在は常駐で運用保守業務に携わらせていただいています。
(こちらが人生初のアウトプット記事投稿です。)
また、本記事は エーピーコミュニケーションズ Advent Calendar 2023 23日目の記事となります。

https://qiita.com/advent-calendar/2023/ap-com

# 執筆動機
業務でMySQLを触る機会があり、グループレプリケーション構成のことを初めて知ったため手元の環境を使って体系的に学習していきたいというのが執筆動機です。

# 取り扱わない内容
<ul>
    <li>ハイパーバイザー、OSやDockerのインストール方法
    <li>マルチプライマリモードの実装方法及びシングルプライマリモードとの比較
    <li>ProxySQLをかまして実際の運用に近い形での実装(これもいずれやってみたい)
</ul>

# 環境構成
<ul>
    <li>Virtualbox 6.1
    <li>AlmaLinux 8
    <li>MySQL 5.7
    <li>Docker version 24.0.7
    <li>Docker Compose version v2.23.3
</ul>

# グループレプリケーションとは
複数サーバでシステムの状態やデータの変更などを共有し、システムの冗長化を実装するものです。
例えば、A～Eのサーバでグループレプリケーションを構成しているシステムがあったとして仮にAサーバがダウンした場合でもほかのB～Eサーバでシステムを継続して提供する仕組みを導入する上で必要になるのがグループレプリケーションになります。

# 1.構成
本記事ではテスト環境として1台のVM上に3つのコンテナを起動します。
1つをプライマリ、残り2つをセカンダリにするシングルプライマリ構成を採用します。
ディレクトリ構成は以下の通りです。
```bash
[root@mysql_alma mysql_env]# tree .
.
├  db
│?? └ conf.d
│??     ├  sec1
│       │　　　└my.cnf
│??     ├  sec2
│       │　　　└my.cnf
│??     └ my.cnf
├ db_primary_data
├ db_sec1_data
├ db_sec2_data
└ docker-compose.yml

```
今回複数のmysqlコンテナを作成するため、docker-compose.ymlを作成しています。docker-compose.ymlの設定は以下の通り。

```yaml
version: '3'
services:
  primary_env:
    image: mysql:5.7
    volumes:
      - ./db_primary_data:/var/lib/mysql
      - ./db/conf.d/my.cnf:/etc/my.cnf
    restart: always
    networks:
      test_networks:
        ipv4_address: 172.16.238.2
    expose:
      - "33061"
    extra_hosts:
      - "primary_env:172.16.238.2"
      - "secondary_env_1:172.16.238.3"
      - "secondary_env_2:172.16.238.4"
    container_name: primary_env
    privileged: true
    environment:
      - MYSQL_ROOT_PASSWORD=(見せられないよ!)
      - MYSQL_DATABSE=nanashi_test
      - MYSQL_USER=nanashi_yd
      - MYSQL_PASSWORD=(見せられないよ!)
    command:
      - --server-id=1

  secondary_env_1:
    image: mysql:5.7
    volumes:
      - ./db_sec1_data:/var/lib/mysql
      - ./db/conf.d/sec1/my.cnf:/etc/my.cnf
    restart: always
    networks:
      test_networks:
        ipv4_address: 172.16.238.3
    expose:
      - "33061"
    extra_hosts:
      - "primary_env:172.16.238.2"
      - "secondary_env_1:172.16.238.3"
      - "secondary_env_2:172.16.238.4"
    container_name: secondary_env_1
    privileged: true
    environment:
      - MYSQL_ROOT_PASSWORD=(見せられないよ!)
      - MYSQL_DATABSE=nanashi_test1
      - MYSQL_USER=nanashi_yd
      - MYSQL_PASSWORD=(見せられないよ!)
    command:
      - --server-id=2

  secondary_env_2:
    image: mysql:5.7
    volumes:
      - ./db_sec2_data:/var/lib/mysql
      - ./db/conf.d/sec2/my.cnf:/etc/my_env2.cnf
    restart: always
    networks:
      test_networks:
        ipv4_address: 172.16.238.4
    expose:
      - "33061"
    extra_hosts:
      - "primary_env:172.16.238.2"
      - "secondary_env_1:172.16.238.3"
      - "secondary_env_2:172.16.238.4"
    container_name: secondary_env_2
    privileged: true
    environment:
      - MYSQL_ROOT_PASSWORD=(見せられないよ!)
      - MYSQL_DATABSE=nanashi_test2
      - MYSQL_USER=nanashi_yd
      - MYSQL_PASSWORD=(見せられないよ!)
    command:
      - --server-id=3

networks:
  test_networks:
    ipam:
     driver: default
     config:
       - subnet: 172.16.238.0/24

```
volumesでmysqlのデータの格納先及び設定ファイルの格納先を指定しています。
つづいて、各コンテナのIPアドレスは、各コンテナ同士で疎通できるようにdocker networkでセグメントを指定して作成し、ipv4アドレスを割り振っています。
ポートについては公式サイトで推奨されている33061ポートを使用しています。
また、グループレプリケーションではserver_idという、システム変数で指定された一意のサーバー IDが必要になります。
そのため、そちらもdocker-compose.ymlで指定しています。

# 2.my.cnfの設定
docker-compose.ymlで各コンテナの設定は記載できたため、実際にmysql自体の設定を行っていきます。
今回は以下のような設定で組んでいます。
構築段階のため、いくつか無効にしている設定がありますが、実際にグループレプリケーションを動かす際には有効化します。対象は以下の通り
<ul>
      <li>read_only
      <li>group_replication_start_on_boot
      <li>group_replication_bootstrap_group
</ul>

```sql
[mysqld]
#
# Disable other storage engines
#
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"

#
# Replication configuration parameters
#
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW
log-error=/var/log/mysqld.log

#read_only=on

#
# Group Replication configuration
#
transaction_write_set_extraction=XXHASH64
plugin_load_add='group_replication.so'
group_replication_group_name="(各コンテナのuuidを記載します)"
group_replication_group_seeds="172.16.238.2:33061,172.16.238.3:33061,172.16.238.4:33061,127.0.0.1"
group_replication_start_on_boot=OFF
group_replication_local_address= "172.16.238.2:33061"
group_replication_single_primary_mode=ON
group_replication_bootstrap_group= OFF
```
上記はプライマリ用コンテナの設定ファイルのため、セカンダリ用コンテナの設定ファイルを用意する必要があります。(そちらは割愛)

:::note alert
上記に関係にする設定として、group_replication_single_primary_modeというものがあります。
これはシングルプライマリモードを設定する際にONにする必要がある設定値になります。
:::

# 3.実際に確認してみる
では、グループレプリケーションを実際に動かします。
プライマリ用コンテナのmysqlにログインし、以下を実行します。

```sql
mysql> SET SQL_LOG_BIN=0;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE USER rpl_user@'%' IDENTIFIED BY 'password';
Query OK, 0 rows affected (0.00 sec)

mysql> GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%';
Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

mysql> CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='password' FOR CHANNEL 'group_replication_recovery';
Query OK, 0 rows affected, 2 warnings (0.29 sec)

mysql>
mysql> SET GLOBAL group_replication_bootstrap_group=ON;
[root@mysql_alma mysql_env]# docker-compose exec -it primary_env mysql -u root -p(見せられないよ!)
mysql> START GROUP_REPLICATION;
mysql> SET GLOBAL group_replication_bootstrap_group=OFF;
mysql> select @@global.read_only read_only;
+-----------+
| read_only |
+-----------+
|         0 |
+-----------+
1 row in set (0.00 sec)

mysql>
```
最初にレプリケーションユーザーの作成を行って、グループレプリケーションを開始しました。
無事に起動できた様子のため、プライマリ用コンテナのread_onlyが0になっていることを確認しました。
read_onlyが0であれば更新系、1であれば参照系のみと別れているため、確認が必要になります。
次に、構築したプライマリ用コンテナがレプリケーショングループに参加していることを確認します。
```sql
mysql> SELECT * FROM performance_schema.replication_group_members;
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST  | MEMBER_PORT | MEMBER_STATE |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
| group_replication_applier | 2b7cd864-9d4f-11ee-a6dc-0242ac10ee02 | 4b1424000e52 |        3306 | ONLINE       |
+---------------------------+--------------------------------------+--------------+-------------+--------------+
1 row in set (0.00 sec)

mysql>
```
上記クエリを実施し、ステータスがONLINEになっていることを確認します。
ONLINEになっていない場合、グループレプリケーションにうまく参加できていないことになるため、見直しが必要になります。

# 4.セカンダリ用コンテナの構築及び、グループレプリケーション参加設定
プライマリ用コンテナは起動及びグループレプリケーションに参加できたため、同様にセカンダリ用コンテナも構築及びグループレプリケーション参加設定をしていきます。
docker-compose.ymlやmy.cnfは前述している内容で記載しているため、こちらでは割愛します。
では、セカンダリ用コンテナのmysqlに接続して設定していきます。
```sql
[root@mysql_alma mysql_env]# docker-compose exec -it secondary_env_1 mysql -u root -p(見せられないよ!)

## プライマリでも行ったグループレプリケーションユーザーの作成を行います。
mysql> CREATE USER rpl_user@'%' IDENTIFIED BY 'password';
Query OK, 0 rows affected (0.00 sec)

mysql> GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%';
Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

mysql> CHANGE MASTER TO MASTER_USER='rpl_user', MASTER_PASSWORD='password' FOR CHANNEL 'group_replication_recovery';
Query OK, 0 rows affected, 2 warnings (0.29 sec)

mysql> START GROUP_REPLICATION;
ERROR 3092 (HY000): The server is not configured properly to be an active member of the group. Please see more details on error log.
mysql>

```
エラーが表示されてグループレプリケーションに参加できなかった模様...
もう一個のコンテナでも同様に実施すると...
```sql
mysql> START GROUP_REPLICATION;
ERROR 3092 (HY000): The server is not configured properly to be an active member of the group. Please see more details on error log.
mysql>
```
同様のエラーが表示されている...mysqld.logを見てもそれらしきエラーログは出てない...
おそらくポートの設定方法や各コンテナのネットワーク疎通の設定に誤りがあると思われるが、今回はここまで(沼ってるが、記事投稿の時間もあるため...)

# 終わりに
結果としては、プライマリに関しては起動できたのですが、セカンダリに関してはグループレプリケーションが開始できなかったです...悔しいですが、今回はここまでとして今後も少しずつ調べていくことにします。
とはいえ、私自身、業務でDBサーバ自体触るのが現職になってから初であり不明点が多かったのですが、MySQL一つ取り上げてもこういった仕組みがあってシステムが動いているのかと大変勉強になりました。
ただ、学習していけばしていくほどまだまだ知らないことも多かったので身が引き締まる思いです...

:::note info
今回、こういう形で終わってしまったため問題解消したものを、また別の記事にしていきます。
また、このグループレプリケーション機能が8系になった場合どうなるのか、proxySQLなどを使用した実際の運用に近い形の実装についても今後アウトプットとして記事にしていきたいです。
※それ以外でもAWSやプログラミング(Java, Rails)あたりもこの年末年始で勉強していきたい
:::
では皆様、良いクリスマスを

# 参考資料

https://dev.mysql.com/doc/refman/5.7/en/group-replication.html

https://qiita.com/huato/items/29714e2ddf72df7009ec

https://www.tohoho-web.com/ex/mysql-replication.html

https://knowledge.sakura.ad.jp/4786/

https://docs.docker.com/compose/compose-file/compose-file-v3/#ipv4_address-ipv6_address