2000秒後の私へ

つまり、ただの覚書。

ProxySQLでInnoDB Clusterもどきを作成する

これはなに?

InnoDB Clusterは、MySQL Router + MySQL Shell + GroupReplication の3つで構成される高可用性ソリューションだが、MySQL RouterとMySQL Shellを使わなくてもInnoDB Clusterと同様のこと (アプリケーション側とMySQL側のフェイルオーバー) を実現することは可能。

今回は、ProxySQL + GroupReplication を使用して、アプリケーション側のフェイルオーバーを行う方法について書こうと思う。

 

構成

Hyper-V上のUbuntu 18.04にLXCコンテナ (全てUbuntu 18.04) を立てて試してみた。

InnoDB Clusterの場合は、MySQL Routerが mysql_innodb_cluster_metadata スキーマを参照するためMySQL Shellが必須だが、今回の場合は別に必要はない。(ただ、MySQL Shell経由でGroupReplicationを組んだほうが簡単ではある。)

今回は一応、手動でGroupReplicationを組んでみることにする。

ノード IPアドレス ホスト名
GroupReplication Primary 10.0.3.11 gr_primary
GroupReplication Secondary1 10.0.3.12 gr_secondary1
GroupReplication Secondary2 10.0.3.13 gr_secondary2
ProxySQL 10.0.3.21 proxysql

 

準備

GroupReplicationの構築

Primaryノードの設定ファイルは以下のようにした。Secondaryノードに関しては、server_idreport_host , loose-group_replicaton_local_address 以外は同じため省略する。

[mysqld]
server_id=11   # 各ノードで一意な値を設定
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_checksum=NONE
report_host="10.0.3.11"  # 自身のIPアドレスを指定する
default_authentication_plugin=mysql_native_password

plugin_load=group_replication.so
loose-group_replication_group_name="b73bffe9-5eff-4e32-8bed-fddb45b1999b"
loose-group_replication_start_on_boot=off
loose-group_replication_local_address="10.0.3.11:33061"  # 自身のIPアドレス
loose-group_replication_group_seeds="10.0.3.11:33061,10.0.3.12:33061,10.0.3.13:33061"
loose-group_replication_bootstrap_group=off

また、ProxySQLは caching_sha2_password 認証に対応していない ため、default_authentication_plugin の値は mysql_native_password とする必要がある。

ProxySQL doesn't accept connections (frontend connections and connections to Admin) if the client tries to use caching_sha2_password authentication plugin. If clients are using libmysqlclient from MySQL 8.0 (that defaults to caching_sha2_password), they must explicitly use mysql_native_password.

GroupReplicationを構成する各ノードのmysqlに接続して、レプリケーション用のユーザを作成する。

mysql> create user repl@'%' identified by 'password';
mysql> grant replication slave on *.* to repl@'%';
mysql> change master to master_user='repl', master_password='password' for channel 'group_replication_recovery';
mysql> reset master;

次にPrimaryノードとなる最初の一台でのみ以下の作業を行う。Primaryノード以外でも group_replication_bootstrap_group をONにした場合、同じ名前のグループが複数起動されてしまうので注意。

Primary> set global group_replication_bootstrap_group=ON;
Primary> start group_replication;
Primary> set global group_replication_bootstrap_group=OFF;

続いて、SecondaryノードでもGroupReplicatonを開始する。

Secondary> start group_replication;

これで、GroupReplicationの構築が完成。

mysql> select member_host,member_state,member_role from performance_schema.replication_group_members;
+-------------+--------------+-------------+
| member_host | member_state | member_role |
+-------------+--------------+-------------+
| 10.0.3.11   | ONLINE       | PRIMARY     |
| 10.0.3.12   | ONLINE       | SECONDARY   |
| 10.0.3.13   | ONLINE       | SECONDARY   |
+-------------+--------------+-------------+

 

ProxySQLのインストール

次に、ProxySQLをインストールしていく。ProxySQLには1.4系と2.0系が存在するが、今回は1.4系を使用した。2.0系では色々な機能が追加されているとか (未確認)。

$ wget https://repo.percona.com/apt/percona-release_0.1-10.bionic_all.deb
$ sudo dpkg -i percona-release_0.1-10.bionic_all.deb
$ sudo apt update
$ sudo apt install proxysql
$ proxysql --version
ProxySQL version 1.4.16-percona-1.1, codename Truls

ProxySQLには管理インターフェイスから設定を変更する方法と、設定ファイル (/etc/proxysql.cnf) から設定を変更する方法の2通りが可能だが、今回は管理インターフェイスで設定を行う。両者の違いは、パラメータを動的に変更可能かどうかで、管理インターフェイスから設定する場合は多くのパラメータを動的に変更できる。

 

sysスキーマに監視用ビューを作成する

lefredさんのgistに公開されてる additional_to_sys_GR.sql を使用してGroupReplicationの状態を監視するビューを作成する。 Primaryノードで以下のコードを実行する。

$ mysql -uroot -p < addition_to_sys_GR.sql

すると、以下のようなビューが作成される。 transactions_behindtransactions_to_cert の値はそれぞれ applier queue と certifier queue にキューイングされるトランザクション数に相当する (ただし、ProxySQLはtransactions_to_cert の値を多分参照してなさそう)。詳しくは公式の Flow Control を参照。

mysql> select * from sys.gr_member_routing_candidate_status;
+------------------+-----------+---------------------+----------------------+
| viable_candidate | read_only | transactions_behind | transactions_to_cert |
+------------------+-----------+---------------------+----------------------+
| YES              | NO        |                   0 |                    0 |
+------------------+-----------+---------------------+----------------------+

また、ProxySQLがこのビューを参照できるように、監視用ユーザーを作成しておく。

mysql> create user 'monitor'@'%' identified by 'monitor';
mysql> grant select on sys.* to 'monitor'@'%';

 

ProxySQLに接続先サーバー情報を追加する

mysqlクライアントを使ってProxySQLの管理インターフェイスに接続し、GroupReplicationに接続するために必要な設定を行う。

# 管理インターフェイスのポートは6032
$ mysql -uadmin -padmin -h127.0.0.1 -P6032 --prompt='Admin> '

# 書き込みグループを1, 読み込みグループは2とする
Admin> INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup) VALUES (1, 2);
Admin> INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES (1, '10.0.3.11',3306), (1, '10.0.3.12',3306), (1, '10.0.3.13',3306);
Admin> INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES (2, '10.0.3.11',3306), (2, '10.0.3.12',3306), (2, '10.0.3.13',3306);

Admin> select hostgroup_id, hostname, status from mysql_servers;
+--------------+-----------+--------+
| hostgroup_id | hostname  | status |
+--------------+-----------+--------+
| 1            | 10.0.3.11 | ONLINE |
| 1            | 10.0.3.12 | ONLINE |
| 1            | 10.0.3.13 | ONLINE |
| 2            | 10.0.3.11 | ONLINE |
| 2            | 10.0.3.12 | ONLINE |
| 2            | 10.0.3.13 | ONLINE |
+--------------+-----------+--------+

# 変更をRUNTIMEに反映させ、DISKに保存する
Admin> LOAD MYSQL SERVERS TO RUNTIME;
Admin> SAVE MYSQL SERVERS TO DISK;

 

GroupReplicatonの監視設定を行う

ProxySQL1.4から mysql_group_replication_hostgroups というテーブルが追加され、自作スクリプトを用意しなくてもGroupReplicationの状態を監視できるようになった。

Admin> INSERT INTO mysql_group_replication_hostgroups (writer_hostgroup,backup_writer_hostgroup,reader_hostgroup, offline_hostgroup,active,max_writers,writer_is_also_reader,max_transactions_behind) VALUES (1,3,2,4,1,1,0,100);

Admin> LOAD MYSQL SERVERS TO RUNTIME;
Admin> SAVE MYSQL SERVERS TO DISK;

 

R/W, R/O分割を行う

ProxySQLでは、Read-Write (RW) と Read-Only (RO) の振り分け方として、主に以下の3つの方法が可能。それぞれの方法について説明を行う。

  1. 接続ポートを分離する
  2. 接続ユーザーを分離する
  3. クエリの種類によって分離する

 

1. 接続ポートを分離する場合

接続ポートを分離することによってR/W, R/O分割を行う。R/W用ポートは6401, R/O用ポートは6402を使用した。以下の内容はProxySQL Wiki) を参考にした。

接続用ユーザを作成する

ProxySQLからMySQLに接続するためのユーザを作成する。MySQLとProxySQLの両方でユーザ作成を行う必要がある。

# GroupReplicationのPrimaryノードで実行
mysql> CREATE USER 'proxysql'@'%' identified by 'password';
mysql> GRANT ALL ON *.* TO 'proxysql'@'%';

# ProxySQLの管理インターフェイスで実行
Admin> INSERT INTO mysql_users (username, password) VALUES ('proxysql', 'password');
Admin> LOAD MYSQL USERS TO RUNTIME;
Admin> SAVE MYSQL USERS TO DISK;
接続ポートの設定を行う

ProxySQLの mysql-interfaces の値を変更する。ProxySQLは大抵のパラメータを動的に変更可能だが、このパラメータに関しては再起動する必要がある。

Admin> SET mysql-interfaces='0.0.0.0:6401;0.0.0.0:6402';
Admin> SAVE MYSQL VARIABLES TO DISK;
$ sudo service proxysql restart
ルーティングルールを設定する

6401ポートから接続された場合は書き込みグループへ、6402ポートから接続された場合は読み込みグループにルーティングするためのルールを設定する。

Admin> INSERT INTO mysql_query_rules (rule_id,active,proxy_port,destination_hostgroup,apply) VALUES (1,1,6401,1,1), (2,1,6402,2,1);
Admin> LOAD MYSQL QUERY RULES TO RUNTIME;
Admin> SAVE MYSQL QUERY RULES TO DISK;

これで、接続ポートによるR/W, R/O分割が可能。

試してみる

ProxySQLノード上で以下のコマンドを実行する。ちゃんと6401ポート→Primaryノード、6402ポート→Secondaryノードに接続できることが確認できる。

$ mysql -u proxysql -h 127.0.0.1 -P 6401 -p -BNe 'select @@hostname;'
gr_primary

$ mysql -u proxysql -h 127.0.0.1 -P 6402 -p -BNe 'select @@hostname;'
gr_secondary2

 

2. 接続ユーザを分離する場合

接続ユーザを分離することによってR/W, R/O分割を行う。 R/W用ユーザはwriter、R/O用ユーザはreaderとした。

接続ユーザを作成する

ProxySQLからMySQLに接続するためのユーザを作成する。今回はwriterとreaderユーザを作成する。

# GroupReplicationのPrimaryノードで実行
mysql> CREATE USER 'writer'@'%' IDENTIFIED BY 'password';
mysql> GRANT ALL ON *.* TO 'writer'@'%';
mysql> CREATE USER 'reader'@'%' IDENTIFIED BY 'password';
mysql> GRANT ALL ON *.* TO 'reader'@'%';

# ProxySQLの管理インターフェイスで実行
Admin> INSERT INTO mysql_users (username, password, default_hostgroup) VALUES ('writer', 'password', 1), ('reader', 'password', 2);
Admin> LOAD MYSQL USERS TO RUNTIME;
Admin> SAVE MYSQL USERS TO DISK;
試してみる

ProxySQLノード上で以下のコマンドを実行する。ちゃんとwriterユーザ→Primaryノード、readerユーザ→Secondaryノードに接続できることが確認できる。(ProxySQLのデフォルトの接続ポートは6033)

$ mysql -u writer -h 127.0.0.1 -P 6033 -p -BNe 'select @@hostname;'
gr_primary

$ mysql -u reader -h 127.0.0.1 -P 6033 -p -BNe 'select @@hostname;'
gr_secondary2

 

3. クエリの種類によって分離する場合

ProxySQLには正規表現によって一致したクエリを特定のグループへルーティングする機能がある。ProxySQL Wikiではこの方法が推奨されていそう。

接続ユーザを作成する

MySQLとProxySQLに接続ユーザを作成する。proxysql ユーザは default_hostgroup を1に設定し、全てのクエリをR/Wノード (Primaryノード) に向けておく。

mysql> CREATE USER 'proxysql'@'%' IDENTIFIED BY 'password';
mysql> GRANT ALL ON *.* TO 'proxysql'@'%';

Admin> INSERT INTO mysql_users (username, password, default_hostgroup) VALUES ('proxysql', 'password', 1);
Admin> LOAD MYSQL USERS TO RUNTIME;
Admin> SAVE MYSQL USERS TO DISK;
ルーティングルールを設定する

ProxySQL Wiki を参考にして、簡単なルーティングルールを設定してみる。

Admin> INSERT INTO mysql_query_rules (rule_id,active,match_digest,destination_hostgroup,apply) VALUES (1,1,'^SELECT.*FOR UPDATE$',1,1), (2,1,'^SELECT',2,1);
Admin> LOAD MYSQL QUERY RULES TO RUNTIME;
Admin> SAVE MYSQL QUERY RULES TO DISK;

この場合、SELECT ... FOR UPDATE がR/Wノードに、それ以外のSELECTがR/Oノードにルーティングされる。また、SELECT 以外で始まるすべてのクエリはデフォルトグループ (R/Wノード) にルーティングされる。

試してみる

ProxySQLノード上で以下のコマンドを実行する。SELECTから始まる場合は以下のようにSecondaryノードにルーティングされることが分かる。

$ mysql -u proxysql -h 127.0.0.1 -P 6033 -p -BNe 'select @@hostname;'
gr_secondary1

ためしに、以下のようにtestデータベースを作成してみたところ、ちゃんとPrimaryノードにルーティングされたことが分かる。

$ mysql -u proxysql -h 127.0.0.1 -P 6033 -e 'create database test;' -p

# Primaryノードで確認する
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test               |
+--------------------+

 

まとめ

ProxySQLを使ってInnoDB Clusterと同じように、アプリケーション側のフェイルオーバーができることが確認できた。ProxySQLはL7プロキシのため、かなり柔軟なルーティングが可能だったりする。ただし、その反面、管理しなければいけないものが増えるため、構成管理ツールとか導入しないと厳しいだろうなぁ、とか感じた。 もし、接続ポートをR/WとR/Oで分離したくないならば、MySQL Routerの代わりにProxySQLを使用するのも良いのかもしれない。

続・MySQL Routerの冗長化について

これはなに?

前の記事Keepalived を用いたMySQL Routerの冗長化について書いたが、今回は Pacemaker + Corosync による冗長化について書こうと思う。
Keeplialivedの代わりに Pacemaker + Corosyncを使う以外は特に違いがないため InnoDB Clusterの構築までについては省略する。

 

構成

前回の記事と同じ構成。 Hyper-V上のUbuntu 18.04にLXCコンテナ (全てUbuntu 18.04) を立てて試してみた。

 

ノード IPアドレス ホスト名
GroupReplication Primary 10.0.3.11 gr_primary
GroupReplication Secondary1 10.0.3.12 gr_secondary1
GroupReplication Secondary2 10.0.3.13 gr_secondary2
MySQL Router Active 10.0.3.21 router_active
MySQL Router Standby 10.0.3.22 router_standby
MySQL Shell 10.0.3.31 mysql_shell

 

Pacemaker + Corosync のインストール

MySQL Routerノード (10.0.3.21, 10.0.3.22) に必要なパッケージをインストールする。

$ sudo apt install pacemaker pcs resource-agents fence-agents

pcsはPacemakerを管理するためのCLIで、従来のcrmshの代わりに使用することが推奨されている。
上記のパッケージをインストールすると、haclusterというクラスタ用ユーザーが作成されるため、両方のノードで同じパスワードを設定しておく。

$ sudo passwd hacluster

そのあと、pcsdサービスを起動する。

$ sudo systemctl start pcsd
$ sudo systemctl enable pcsd

 

クラスタの作成

クラスタ間通信を行うために名前解決ができる必要があるが、今回は内部DNSなどを設定していないため、MySQL Routerノードの /etc/hosts ファイルに設定しておく。

$ cat /etc/hosts
10.0.3.21  router_active
10.0.3.22  router_standby

また、corosyncをインストールした段階で /etc/corosync/corosync.conf が存在しているが、このファイルを削除しないと以下の手順が失敗するため事前に削除しておく。

$ sudo pcs cluster destroy    # クラスタ構成ファイルが削除される
$ sudo systemctl enable pacemaker    # 上のコマンドによりpacemakerサービスが無効化されるため

 
pcsコマンドを用いてクラスタを設定していく。以下はどちらか一方のノードでのみ実行する。しかし、pcs cluster startを実行しようとすると以下のようなエラーメッセージを出して失敗する。

$ sudo pcs cluster auth router_active router_standby -u hacluster -p password
$ sudo pcs cluster setup --name router_cluster router_active router_standby

$ sudo pcs cluster start --all   # ここで失敗する
router_standby: Error connecting to router_standby - (HTTP error: 400)
router_active: Error connecting to router_active - (HTTP error: 400)
Error: unable to start all nodes
router_standby: Error connecting to router_standby - (HTTP error: 400)
router_active: Error connecting to router_active - (HTTP error: 400)

 
おそらく、以下のissueと同じ現象だと思う。 github.com

ローカルノードが異なるIPアドレスに解決してしまうのが原因らしい。

It turns out the whole issue was the local node resolving to a different IP internally. When that happens, commands return http code 400, but there are no logs that record what went wrong. Also, setting debug: on in the configuration doesn't make it output debug messages to the logs.

 
上記のissueと同じように、corosync.serviceファイルのType=notifyType=simpleに変更して試してみても起動しなかったため、仕方がないので以下のようにホスト名の代わりにIPアドレスを使うことにした。

$ sudo pcs cluster auth 10.0.3.21 10.0.3.22 -u hacluster -p password
$ sudo pcs cluster setup --name router_cluster 10.0.3.21 10.0.3.22 --force
$ sudo pcs cluster start --all
$ sudo pcs cluster enable --all

 
ちゃんと起動しているのが確認できる。

root@router_active:~# sudo pcs status

2 nodes configured
0 resources configured

Online: [ router_active router_standby ]

No resources

 
今回は検証目的なのでSTONITHは無効化しておく。また、2台構成のクラスタを組むのでquorumはignoreに設定しておく。

$ sudo pcs property set stonith-enabled=false
$ sudo pcs property set no-quorum-policy=ignore

また、migration-thresholdも1に設定する。

$ sudo resource defaults migration-threshold=1

 

仮想IPリソースの作成

続いて、仮想IPリソースを作成する。今回は仮想IPとして 10.0.3.20 を割り当てることとする。また、モニタリング間隔は10秒ごととした。

$ sudo pcs resource create virtualip ocf:heartbeat:IPaddr2 ip=10.0.3.20 cidr_netmask=24 op monitor interval=10s

以下のようにvirtualipリソースが作成されているのを確認できる。

$ sudo pcs resource show virtualip
 Resource: virtualip (class=ocf provider=heartbeat type=IPaddr2)
  Attributes: cidr_netmask=24 ip=10.0.3.20
  Operations: monitor interval=10s (virtualip-monitor-interval-10s)
              start interval=0s timeout=20s (virtualip-start-interval-0s)
              stop interval=0s timeout=20s (virtualip-stop-interval-0s)

 

MySQL Routerリソースの作成

次に、MySQL Routerをリソースとして追加する。

$ sudo pcs resource create mysqlrouter systemd:mysqlrouter clone

以下のようにmysqlrouterリソースが作成されているのが確認できる。

$ sudo pcs resource show mysqlrouter
 Resource: mysqlrouter (class=systemd type=mysqlrouter)
  Operations: monitor interval=60 timeout=100 (mysqlrouter-monitor-interval-60)
              start interval=0s timeout=100 (mysqlrouter-start-interval-0s)
              stop interval=0s timeout=100 (mysqlrouter-stop-interval-0s)

 

コロケーション制約を加える

作成した仮想IPリソースをMySQL Routerインスタンスに配置するために、INFINITYスコアを持つコロケーション制約を加える。

$ sudo pcs constraint colocation add virtualip with mysqlrouter-clone score=INFINITY

ちゃんと仮想IPが割り当てられていることが確認できる。

root@router_active:~# ip addr show
40: eth0@if41: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:5c:7a:63 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.3.21/24 brd 10.0.3.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet 10.0.3.20/24 brd 10.0.3.255 scope global secondary eth0
       valid_lft forever preferred_lft forever

 

障害テスト

あとは、MySQL Routerを止めたり、インスタンスを止めたりして仮想IPが入れ替わることを確認できれば終わり。

root@router_standby:~# ip addr show
46: eth0@if47: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:ca:16:95 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.3.22/24 brd 10.0.3.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet 10.0.3.20/24 brd 10.0.3.255 scope global secondary eth0
       valid_lft forever preferred_lft forever

 

まとめ

KeepalivedPacemaker + Corosyncで冗長構成を組んでみたが、個人的にはKeepalivedの方が分かりやすい気がした。昔はKeepalivedがMulticastにしか対応していなかったため、本番環境とかだと使いづらかったのかもしれないが、今は unicast_peerとかでUnicastも対応できるっぽい。なので、Keepalivedを使うかPcacemaker + Corosyncを使うかは個人の好み次第で良いのかも?

MySQL Routerの冗長化について

これはなに?

本当はInnoDB Cluster構築の記事から始めようと思っていたけれど、もう素晴らしい記事が山ほどあって心を折られたので、差別化を図るためにMySQL Routerの冗長化手法について考えてみる。

具体的には Keepalived による冗長化手法を試してみた。

最終的な構成図は以下のような感じになる予定。 GroupReplicationはシングルプライマリモードで構築することにした。 f:id:Puan:20200403235915p:plain

2つのMySQL Routerで構成されたHAクラスタに1つの仮想IPを割り当てる構成。つまり、単純なActive-Stanby方式。もちろん2つ以上の仮想IPを割り当ててもよい (障害時に負荷に耐えられるなら) 。

ちなみに 公式ドキュメント には、アプリケーションサーバ上にMySQL Routerをインストールすることが推奨されているので、本記事は非推奨の方法であるということに注意。

For best performance, MySQL Router is typically installed on the same host as the application that uses it.

 

構成

Hyper-V上のUbuntu 18.04にLXCコンテナ (全てUbuntu 18.04) を立てて試してみた。

構成は以下のような感じ。バージョンはMySQL Server 8.0.19, MySQL Rotuer 8.0.19, MySQL Shell 8.0.19を使用した。

 

ノード IPアドレス ホスト名
GroupReplication Primary 10.0.3.11 gr_primary
GroupReplication Secondary1 10.0.3.12 gr_secondary1
GroupReplication Secondary2 10.0.3.13 gr_secondary2
MySQL Router Active 10.0.3.21 router_active
MySQL Router Standby 10.0.3.22 router_standby
MySQL Shell 10.0.3.31 mysql_shell

 

my.cnfの設定

以下のような項目を /etc/mysql/my.cnf に追記する。以下はPrimaryノードの設定。Secondaryノードは server_idreport_host の値をそれぞれ変更しておく。

[mysqld]
server_id = 11    # 一意な値を設定
gtid_mode = on
enforce_gtid_consistency = on
binlog_checksum = none
report_host = "10.0.3.11"   # ホスト名解決ができない場合は必要
default_authentication_plugin = mysql_native_password

 

GroupReplicationの構築

MySQL Shellを使用してGroupReplicationを組んでいく。
まず、GroupReplicationを構築予定の各ノードでMySQL Shellからアクセス可能なユーザを作成しておく。ここでは簡単のために ALL 権限を付与した root ユーザを作成する。

mysql> CREATE USER 'root'@'%' IDENTIFIED BY 'password';
mysql> GRANT ALL ON *.* TO 'root'@'%' WITH GRANT OPTION;
mysql> reset master;

 
次に、MySQL Shellを使用してPrimaryノードに接続し、GroupReplicationを組む。
途中でCLONEプラグインを使用したりしたが、本題ではないので省略する。

$ mysqlsh
MySQL JS> shell.connect('root@10.0.3.11')
MySQL 10.0.3.11:33060+ ssl JS> cluster = dba.createCluster('test')
MySQL 10.0.3.11:33060+ ssl JS> cluster.addInstance('root@10.0.3.12')
MySQL 10.0.3.11:33060+ ssl JS> cluster.addInstance('root@10.0.3.13')

 
以下のようにGroupReplicationが構築できていることが確認できる。(cluster.status()だと出力が長いので...)

MySQL 10.0.3.11:33060+ ssl JS> \sql
MySQL 10.0.3.11:33060+ ssl SQL> select member_host, member_port, member_state, member_role, member_version from performance_schema.replication_group_members;
+-------------+-------------+--------------+-------------+----------------+
| member_host | member_port | member_state | member_role | member_version |
+-------------+-------------+--------------+-------------+----------------+
| 10.0.3.12   |        3306 | ONLINE       | SECONDARY   | 8.0.19         |
| 10.0.3.13   |        3306 | ONLINE       | SECONDARY   | 8.0.19         |
| 10.0.3.11   |        3306 | ONLINE       | PRIMARY     | 8.0.19         |
+-------------+-------------+--------------+-------------+----------------+
3 rows in set (0.0007 sec)

 
各ノードに mysql_innodb_cluster_metadata スキーマが作成されているのを確認する。このスキーマがないと、MySQL RouterがGroupReplicationの状態を監視できないので重要。
ちなみに、MySQL Shellを使わずに手動で構築した場合、このスキーマが作成されないため、自分で作成するなどの対応が必要となる。(InnoDB Clusterの構成要素にMySQL RouterとGroupReplicationだけではなく、MySQL Shellも含まれている理由がこのスキーマの存在だと思う)。

mysql> show databases;
+-------------------------------+
| Database                      |
+-------------------------------+
| information_schema            |
| mysql                         |
| mysql_innodb_cluster_metadata |
| performance_schema            |
| sys                           |
+-------------------------------+
5 rows in set (0.01 sec)

 

MySQL Routerの設定

MySQL Routerをインストールした各ノードで以下のようにGroupReplicationに接続する。

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

 
すると、以下のようなコンフィグファイルが作成される。
bind_port で指定されたポートにMySQL Clientで接続すると、destinations で指定された対象ノードにルーティングを行ってくれる。(例えば、6446ポートに接続した場合は、Pirmaryノードにclassicプロトコルを用いて接続される)
destinationsmetadata-cache://test/?role=PRIMARY のようになっていることから分かるように、MySQL RotuerはGroupReplicationのメタ情報を取得して動的にルーティングを行っている。(いままで, MySQL Router + MySQL Fabricで行っていたことが、MySQL Router + MySQL Shellに置き換えられたという感じだろうか)

$ cat /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=/run/mysqlrouter/state.json

[logger]
level = INFO

[metadata_cache:test]
cluster_type=gr
router_id=1
user=mysql_router1_hq01yshcvj7e
metadata_cluster=test
ttl=0.5
use_gr_notifications=0

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

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

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

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

 
MySQL ServerにもMySQL Router用のユーザが作成されているのが確認できる。

mysql> select user,host from mysql.user;
+----------------------------+-----------+
| user                       | host      |
+----------------------------+-----------+
| mysql_innodb_cluster_11    | %         |
| mysql_innodb_cluster_12    | %         |
| mysql_innodb_cluster_13    | %         |
| mysql_router1_hq01yshcvj7e | %         |
| mysql_router2_xswvvfihkzr4 | %         |
| root                       | %         |
| mysql.infoschema           | localhost |
| mysql.session              | localhost |
| mysql.sys                  | localhost |
| root                       | localhost |
+----------------------------+-----------+

 
MySQL Shellノード (10.0.3.31) から接続して、ちゃんとルーティングされていることを確認しておく。

root@mysql_shell:~# mysqlsh --sql root@10.0.3.21:6446 -p -e 'select @@hostname;'
@@hostname
gr_primary
root@mysql_shell:~# mysqlsh --sql root@10.0.3.22:6446 -p -e 'select @@hostname;'
@@hostname
gr_primary

 

Keepalivedの設定

MySQL Routerを冗長化するためにKeepalivedの設定をおこなっていく。
MySQL Routerノード (10.0.3.21, 10.0.3.22) にKeepalivedをインストールし、設定ファイル (/etc/keepalived/keepalived.conf) に以下のような設定を行う。各パラメータの詳細は他の記事や 公式ドキュメント を参考に。

$ cat /etc/keepalived/keepalived.conf
vrrp_script chk_mysqlrouter {
  script "/usr/bin/killall -0 /usr/bin/mysqlrouter"   # mysqlrouterのプロセスを監視する
  interval 2
  fall 2
}

vrrp_instance VI_1 {
  interface eth0
  state BACKUP   # Active, StandbyともにBACKUPとした
  virtual_router_id 51
  priority 101   # Activeノードは101, Standbyノードは100に設定した
  advert_int 1
  nopreempt
  virtual_ipaddress {
    10.0.3.20/24    # 仮想IPとして10.0.3.20を使用する
  }
  track_script {
    chk_mysqlrouter
  }
}

 

Activeノード (10.0.3.21) に、仮想IP (10.0.3.20) が割り当てられていることが確認できる。

root@router_active:~# ip addr show eth0
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:a6:b9:dd brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.3.21/24 brd 10.0.3.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet 10.0.3.20/24 scope global secondary eth0
       valid_lft forever preferred_lft forever

 
MySQL Shellノードから仮想IPに対して接続して、ちゃんとルーティングが行われていることを確認する。

root@mysql_shell:~# mysqlsh --sql root@10.0.3.20:6446 -p -e 'select @@hostname;'
@@hostname
gr_primary
root@mysql_shell:~# mysqlsh --sql root@10.0.3.20:6447 -p -e 'select @@hostname;'
@@hostname
gr_secondary2

これでKeepalivedを用いたMySQL Routerの冗長化が完成。

 

障害テスト

最後に、Activeノードに障害が発生し、MySQL Routerが停止した場合でもちゃんと問題なく動作するかを検証する。シナリオは以下のような感じ。 f:id:Puan:20200405221818p:plain

 
まず、ActiveノードのMySQL Routerを停止する。

root@router_active:~# sudo service mysqlrouter stop

 
仮想IP (10.0.3.20) が Standbyノード (10.0.3.22) に切り替わっているのが確認できる。

root@router_standby:~# ip addr show eth0
20: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:12:55:6f brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.3.22/24 brd 10.0.3.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet 10.0.3.20/24 scope global secondary eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::216:3eff:fe12:556f/64 scope link 
       valid_lft forever preferred_lft forever

 
障害後も、仮想IP経由で障害前と同じようにGroupReplicationに接続できることが確認できる。

root@mysql_shell:~# mysqlsh --sql root@10.0.3.20:6446 -p -e 'select @@hostname;'
@@hostname
gr_primary
root@mysql_shell:~# mysqlsh --sql root@10.0.3.20:6447 -p -e 'select @@hostname;'
@@hostname
gr_secondary2

 

おわりに

学んだことのアウトプットとしてブログをはじめてみたけど、記事を書くのってめちゃくちゃ大変。。。  
はたしてこれからも続けられるのか。。。