MySQL Routerの冗長化について
これはなに?
本当はInnoDB Cluster構築の記事から始めようと思っていたけれど、もう素晴らしい記事が山ほどあって心を折られたので、差別化を図るためにMySQL Routerの冗長化手法について考えてみる。
具体的には Keepalived による冗長化手法を試してみた。
最終的な構成図は以下のような感じになる予定。 GroupReplicationはシングルプライマリモードで構築することにした。
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_id
と report_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プロトコルを用いて接続される)
destinations
が metadata-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が停止した場合でもちゃんと問題なく動作するかを検証する。シナリオは以下のような感じ。
まず、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
おわりに
学んだことのアウトプットとしてブログをはじめてみたけど、記事を書くのってめちゃくちゃ大変。。。
はたしてこれからも続けられるのか。。。