CLOVER🍀

That was when it all began.

MySQL Routerを使用して、InnoDB Clusterを構成する(MySQL Routerの設定主体)

これは、なにをしたくて書いたもの?

以前、MySQL Shellを使用して、InnoDB Clusterのうち、Group Replicationの部分を構成してみました。

MySQL Shellで、InnoDB ClusterのGroup Replication部分を構成してみる - CLOVER🍀

今度は、この構成にMySQL Routerを加えたいと思います。

Group Replication構成に、MySQL Routerを足すことで、InnoDB Clusterの完成です。

MySQL :: MySQL 8.0 Reference Manual :: 21.1 Introducing InnoDB Cluster

f:id:Kazuhira:20190830234336p:plain

MySQL Router

MySQL Routerは、InnoDB Clusterの一部であり、MySQLを利用するアプリケーションから見た、MySQLサーバー群へのルーティングを
提供するミドルウェアです。

MySQL :: MySQL Router 8.0

MySQL Routerを使うことで、InnoDB Clusterにおける障害時のプライマリデータベースが切り替わった時にルーティングしてくれたり、
複数のリードレプリカへの振り分けなどを行ってくれます。

MySQL :: MySQL Router 8.0 :: 1.1 Routing for MySQL InnoDB cluster

要するに、MySQL Group Replicationに対するロードバランサー的な役割になるわけですね。

MySQL :: MySQL Router 8.0 :: 1.4 Application Considerations

ちなみに、ネットワーク遅延を減らすため、MySQL Routerはそれを利用するアプリケーションと同一のホストにインストールすることが
推奨されているようです。

MySQL :: MySQL Router 8.0 :: 3 Deploying MySQL Router

UNIXドメインソケットを使ってくれ、だそうです。

とまあ、前置きはこれくらいにして試してみましょう。

環境

MySQL Serverを3つ、MySQL Router、それからMySQL Shellを用意します。

以下のサーバーを使うことにします。

  • MySQL Server 1 … 172.17.0.2(mysql1)
  • MySQL Server 2 … 172.17.0.3(mysql2)
  • MySQL Server 3 … 172.17.0.4(mysql3)
  • MySQL Router … 172.17.0.5
  • MySQL Shell … 172.17.0.6

MySQLのバージョン。

# mysql --version
mysql  Ver 8.0.17 for Linux on x86_64 (MySQL Community Server - GPL)

MySQL Shellのバージョン。

$ mysqlsh --version
Logger: Tried to log to an uninitialized logger.
mysqlsh   Ver 8.0.17 for Linux on x86_64 - for MySQL 8.0.17 (MySQL Community Server (GPL))

MySQL Routerのインストール

こちらに記載のaptリポジトリをインストールした後、

MySQL :: Download MySQL APT Repository

aptでインストール。

$ sudo apt install -y mysql-router

Group Replication(Single Master)を構成する

では、MySQL ShellでGroup Replication(Single Master)を構成してみましょう。

Group Replicationを構成するためのMySQL Serverの事前の設定や、ユーザーについては特に書きません。以前のエントリを参照してください。

MySQL Shellで、InnoDB ClusterのGroup Replication部分を構成してみる - CLOVER🍀

MySQL Shellでログイン。

$ mysqlsh

3つのノードを使って、Group Replicationを構成します。

 MySQL  JS > \connect root@172.17.0.2:3306
 MySQL  172.17.0.2:3306 ssl  JS > var cluster = dba.createCluster('test_single_master_cluster')
 MySQL  172.17.0.2:3306 ssl  JS > cluster.addInstance('root@172.17.0.3:3306')
 MySQL  172.17.0.2:3306 ssl  JS > cluster.addInstance('root@172.17.0.4:3306')

完成。

 MySQL  172.17.0.2:3306 ssl  JS > cluster.status()
{
    "clusterName": "test_single_master_cluster", 
    "defaultReplicaSet": {
        "name": "default", 
        "primary": "172.17.0.2:3306", 
        "ssl": "REQUIRED", 
        "status": "OK", 
        "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", 
        "topology": {
            "172.17.0.2:3306": {
                "address": "172.17.0.2:3306", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.0.17"
            }, 
            "172.17.0.3:3306": {
                "address": "172.17.0.3:3306", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.0.17"
            }, 
            "172.17.0.4:3306": {
                "address": "172.17.0.4:3306", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.0.17"
            }
        }, 
        "topologyMode": "Single-Primary"
    }, 
    "groupInformationSourceMember": "172.17.0.2:3306"
}

マスターは、最初のノードですね。

            "172.17.0.2:3306": {
                "address": "172.17.0.2:3306", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.0.17"
            }, 

では、MySQL RouterでMySQL Serverに接続して、MySQL Routerの設定を行います。「--bootstrap」オプションを指定して、
Group Replicationを構成しているMySQL Serverのうちのひとつに接続します。

$ sudo mysqlrouter --bootstrap root@172.17.0.2:3306 --user=mysqlrouter
Please enter MySQL password for root:

MySQL :: MySQL 8.0 Reference Manual :: 21.3 Using MySQL Router with InnoDB Cluster

MySQL :: MySQL Router 8.0 :: 3.1 Bootstrapping MySQL Router

このコマンドを実行すると、MySQL Routerの設定ファイルなどが作成されるのですが、この時のユーザーを「--user」オプションで
指定します。

MySQL Routerはaptでインストールしているので、その時に作成される「mysqlrouter」ユーザーに合わせました。

こんな感じのログが出力されます。

# Bootstrapping system MySQL Router instance...

- Checking for old Router accounts
  - No prior Router accounts found
- Creating mysql account mysql_router1_rh4tbrc7rbpy@'%' for cluster management
- Storing account in keyring
- Adjusting permissions of generated files
- Creating configuration /etc/mysqlrouter/mysqlrouter.conf

# MySQL Router configured for the InnoDB cluster 'test_single_master_cluster'

After this MySQL Router has been started with the generated configuration

    $ /etc/init.d/mysqlrouter restart
or
    $ mysqlrouter -c /etc/mysqlrouter/mysqlrouter.conf

the cluster 'test_single_master_cluster' can be reached by connecting to:

## MySQL Classic protocol

- Read/Write Connections: localhost:6446
- Read/Only Connections:  localhost:6447

## MySQL X protocol

- Read/Write Connections: localhost:64460
- Read/Only Connections:  localhost:64470


Existing configuration backed up to '/etc/mysqlrouter/mysqlrouter.conf.bak'

生成された設定ファイルを見てみましょう。
/etc/mysqlrouter/mysqlrouter.conf

# File automatically generated during MySQL Router bootstrap
[DEFAULT]
name=system
user=mysqlrouter
keyring_path=/run/mysqlrouter/keyring
master_key_path=/etc/mysqlrouter/mysqlrouter.key
connect_timeout=15
read_timeout=30
dynamic_state=/var/run/mysqlrouter/state.json

[logger]
level = INFO

[metadata_cache:test_single_master_cluster]
router_id=1
user=mysql_router1_rh4tbrc7rbpy
metadata_cluster=test_single_master_cluster
ttl=0.5
use_gr_notifications=0

[routing:test_single_master_cluster_default_rw]
bind_address=0.0.0.0
bind_port=6446
destinations=metadata-cache://test_single_master_cluster/default?role=PRIMARY
routing_strategy=first-available
protocol=classic

[routing:test_single_master_cluster_default_ro]
bind_address=0.0.0.0
bind_port=6447
destinations=metadata-cache://test_single_master_cluster/default?role=SECONDARY
routing_strategy=round-robin-with-fallback
protocol=classic

[routing:test_single_master_cluster_default_x_rw]
bind_address=0.0.0.0
bind_port=64460
destinations=metadata-cache://test_single_master_cluster/default?role=PRIMARY
routing_strategy=first-available
protocol=x

[routing:test_single_master_cluster_default_x_ro]
bind_address=0.0.0.0
bind_port=64470
destinations=metadata-cache://test_single_master_cluster/default?role=SECONDARY
routing_strategy=round-robin-with-fallback
protocol=x

接続先の定義がいろいろあります。

また、設定ファイルの中身を見てみると、Group Replicationを構成するMySQL Serverの情報は、以下のファイルに書かれているようです。
/var/run/mysqlrouter/state.json

{
    "metadata-cache": {
        "group-replication-id": "88fcae5a-cb37-11e9-a5aa-0242ac110002",
        "cluster-metadata-servers": [
            "mysql://172.17.0.2:3306",
            "mysql://172.17.0.3:3306",
            "mysql://172.17.0.4:3306"
        ]
    },
    "version": "1.0.0"
}

なお、「--bootstrap」オプションを指定して実行する度に、設定ファイルは再作成されます。

MySQL :: MySQL Router 8.0 :: 4.3.1.1 mysqlrouter — Command Line Options

--bootstrap URI, -B URI

ここで、MySQL Routerを再起動します。

$ sudo systemctl restart mysqlrouter.service

ところで、クラスタにサーバーが増減したりして構成に変更があった場合、こうやって「--bootstrap」オプションで設定ファイルを
再作成したり、設定ファイルを修正したりしてMySQL Routerを再起動する必要があるみたいです。

MySQL :: MySQL 8.0 Reference Manual :: 21.3 Using MySQL Router with InnoDB Cluster

Tip
When you change the topology of a cluster by adding another server instance after you have bootstrapped MySQL Router, you need to update bootstrap_server_addresses based on the updated metadata. Either restart MySQL Router using the --bootstrap option, or manually edit the bootstrap_server_addresses section of the mysqlrouter.conf file and restart MySQL Router.

「--bootstrap」を使って設定ファイルを作成した時のログに戻ると、プロトコルとアクセスする用途に応じて、
4種類のポートに分かれていることが表示されていました。

## MySQL Classic protocol

- Read/Write Connections: localhost:6446
- Read/Only Connections:  localhost:6447

## MySQL X protocol

- Read/Write Connections: localhost:64460
- Read/Only Connections:  localhost:64470

今回は、MySQL Classic protocolと書かれている、今まで通りのプロトコルでアクセスしてみましょう。

Read/Write用は6446、Read Onlyは6447のようです。

接続して「@@hostname」を見てみます。

mysqlコマンドからアクセスしてみましょう。MySQL Routerの、Read/Write用の6446ポートに接続。

$ mysql -h 172.17.0.5 -P 6446 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql1     |
+------------+

mysql1に接続されました。

何度接続しても、mysql1に接続が割り振られます。

$ mysql -h 172.17.0.5 -P 6446 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql1     |
+------------+


$ mysql -h 172.17.0.5 -P 6446 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql1     |
+------------+


$ mysql -h 172.17.0.5 -P 6446 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql1     |
+------------+

では、Read Only用の6447ポートに接続してみましょう。

$ mysql -h 172.17.0.5 -P 6447 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql2     |
+------------+

1 row in set (0.0005 sec)

mysql2になりました。

Read Only用のポートの場合は、Read Only用のインスタンスに割り振られるようですね。

$ mysql -h 172.17.0.5 -P 6447 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql3     |
+------------+


$ mysql -h 172.17.0.5 -P 6447 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql2     |
+------------+


$ mysql -h 172.17.0.5 -P 6447 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql3     |
+------------+

テーブル作成の確認。Read/Write側で作成。

$ mysql -h 172.17.0.5 -P 6446 -uroot -p -e 'create database test; use test; create table t1(id int);'

Read Only側で確認。

$ mysql -h 172.17.0.5 -P 6447 -uroot -p -e 'use test; select count(1) from t1;'
Enter password: 
+----------+
| count(1) |
+----------+
|        0 |
+----------+

Single Master時の挙動は確認できた感じですね。

Group Replication(Multi Master)を構成する

最後に、Multi Master ModeのGroup Replicationに対して、MySQL Routerからアクセスしてみましょう。

1度環境を破棄して、Group Replicationから再構成しなおします。

マルチマスターなGroup Replicationの作成。

 MySQL  172.17.0.2:3306 ssl  JS > var cluster = dba.createCluster('test_multi_master_cluster', { multiPrimary: true })
 MySQL  172.17.0.2:3306 ssl  JS > cluster.addInstance('root@172.17.0.3:3306')
 MySQL  172.17.0.2:3306 ssl  JS > cluster.addInstance('root@172.17.0.4:3306')

確認。

 MySQL  172.17.0.2:3306 ssl  JS > cluster.status()
{
    "clusterName": "test_multi_master_cluster", 
    "defaultReplicaSet": {
        "name": "default", 
        "ssl": "REQUIRED", 
        "status": "OK", 
        "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", 
        "topology": {
            "172.17.0.2:3306": {
                "address": "172.17.0.2:3306", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.0.17"
            }, 
            "172.17.0.3:3306": {
                "address": "172.17.0.3:3306", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.0.17"
            }, 
            "172.17.0.4:3306": {
                "address": "172.17.0.4:3306", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.0.17"
            }
        }, 
        "topologyMode": "Multi-Primary"
    }, 
    "groupInformationSourceMember": "172.17.0.2:3306"
}

「Multi-Primary」です。

では、MySQL Routerの設定。

$ sudo mysqlrouter --bootstrap root@172.17.0.2:3306 --user=mysqlrouter

MySQL Routerを再起動。

$ sudo systemctl restart mysqlrouter.service

MySQL Routerに対して、接続確認をしてみます。

まずは、Read/Writeなポートの方から。

$ mysql -h 172.17.0.5 -P 6446 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql1     |
+------------+

mysql1です。

で、何度か試してみても…。

$ mysql -h 172.17.0.5 -P 6446 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql1     |
+------------+


$ mysql -h 172.17.0.5 -P 6446 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql1     |
+------------+

mysql1です。あれ、他のサーバーに行きませんね?

Read Onlyの方は、別のサーバーに割り振ってくれます。

$ mysql -h 172.17.0.5 -P 6447 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql1     |
+------------+


$ mysql -h 172.17.0.5 -P 6447 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql2     |
+------------+


$ mysql -h 172.17.0.5 -P 6447 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql3     |
+------------+

ここで、以下のドキュメントを読みつつ

MySQL :: MySQL 8.0 Reference Manual :: 21.3 Using MySQL Router with InnoDB Cluster

The way incoming connections are redirected depends on the type of cluster being used. When using a single-primary cluster, by default MySQL Router publishes a X Protocol and a classic protocol port, which clients connect to for read-write sessions and which are redirected to the cluster's single primary. With a multi-primary cluster read-write sessions are redirected to one of the primary instances in a round-robin fashion. For example, this means that the first connection to port 6446 would be redirected to the ic-1 instance, the second connection to port 6446 would be redirected to the ic-2 instance, and so on. For incoming read-only connections MySQL Router redirects connections to one of the secondary instances, also in a round-robin fashion. To modify this behavior see the routing_strategy option.

MySQL Routerの設定ファイルを確認してみます。

[routing:test_multi_master_cluster_default_rw]
bind_address=0.0.0.0
bind_port=6446
destinations=metadata-cache://test_multi_master_cluster/default?role=PRIMARY
routing_strategy=first-available
protocol=classic

[routing:test_multi_master_cluster_default_ro]
bind_address=0.0.0.0
bind_port=6447
destinations=metadata-cache://test_multi_master_cluster/default?role=SECONDARY
routing_strategy=round-robin-with-fallback
protocol=classic

Read/Writeの方の「routing_strategy」が、「first-available」になっています。

routing_strategy

試しに、Read/Writeの方の「routing_strategy」を「round-robin」にしてみましょう。

[routing:test_multi_master_cluster_default_rw]
bind_address=0.0.0.0
bind_port=6446
destinations=metadata-cache://test_multi_master_cluster/default?role=PRIMARY
routing_strategy=round-robin
protocol=classic

MySQL Routerを再起動。

$ sudo systemctl restart mysqlrouter.service

これで、Read/Writeの方でもラウンドロビンの接続になりました。

$ mysql -h 172.17.0.5 -P 6446 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql1     |
+------------+


$ mysql -h 172.17.0.5 -P 6446 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql2     |
+------------+


$ mysql -h 172.17.0.5 -P 6446 -uroot -p -e 'select @@hostname;' 
Enter password: 
+------------+
| @@hostname |
+------------+
| mysql3     |
+------------+

なお、シングルマスターの時に「routing_strategy」を「round-robin」にしても、プライマリであるmysql1にしか振り分けられません。
そりゃあそうですね。

レプリケーションの確認は、割愛(Read/Writeのどのサーバーで更新しても、別のサーバーに反映されます)。
ちなみに、マルチマスターの場合はRead Only接続でも更新操作ができてしまうので、注意を…。

とりあえず、マルチマスターの場合でも設定できましたが。が、ちゃんと、設定内容を理解した方がよさそうですね…。

まとめ

MySQL Routerを使って、シングルマスター、マルチマスターの場合なGroup Replication構成のMySQLに接続してみました。

Group Replicationまで組めていれば、MySQL Routerを利用すること自体はそんなに苦労はしませんでした。

今度は、ノードを増減させた場合にMySQL Routerのメタデータの再設定が必要なようなので、そちらも確認してみるとしましょう。