2000秒後の私へ

つまり、ただの覚書。

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

 

おわりに

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