InnoDB Cluster/ReplicasetのインスタンスのMySQL Router経由のアクセスを一時的に不可能にする
何気にブログを始めて半年ほど経っていました。
今のところ最低でも月に2本ほど書いているようですね。 ただ自分の中でノルマみたいなのは設定しないようにしています。(義務になっちゃうと一気にやる気を失せちゃうタイプなので)
たぶん無理に更新頻度を高く設定しないで書きたいときに書いているから続けられているだろうなあと思っています。
さて、本題へ…
今回はMySQL Router/Shellについてです。
MySQL Router/Shell 8.0.21から新たにInnoDB ClusterもしくはInnoDB Replicasetに含まれるインスタンスを一時的にアプリケーションからアクセス不可能にする機能が追加されました。
これにより例えばローリングアップグレードをする時や、バッグアップなど一時的にサーバー負荷が上がる操作をする時などに わざわざインスタンスを停止させたり、Cluster.removeInstance()をする必要がなくなりました。
というわけで早速触ってみます。
- 3行で
- MySQL Router概要
- InnoDB Group Replicationの準備
- MySQL Routerの準備
- インスタンスを一時的にアクセス不可能にする
- _hiddenの真偽を確認する
- アクセス不可にするインスタンスの既存コネクションを処理する
- まとめ
- 参考にさせていただいたサイト
3行で
8.0.21からMySQL Router経由のアクセスを制御できるようになった
これを使うことでインスタンスを停止することなくアプリケーションからの接続を不可能にすることができる
メンテナンスが組みやすくなる!
MySQL Router概要
その前にMySQL Routerについて簡単に整理しておきます。
MySQL RouterはMySQL Group Replicationとともに使うことでInnoDB Clusterを構成することが可能です。(正式には+MySQL Shell)
MySQL Routerは定期的にGroup Replicationからメタデータを取得しています。 このメタデータには例えばクラスターにはどのようなインスタンスが含まれているのか、インスタンスのステータス(書き込み可能か、インスタンスが停止していないか)などが含まれます。 そして、MySQL Routerはアプリケーションからクエリを受け取り、適切なインスタンスにそのリクエストを送ります。
MySQL Routerはアプリケーションに透過性を提供します。 すなわち、アプリケーションはクラスターに含まれるインスタンスが何台あるのか、故障しているインスタンスが無いのかといったことを考える必要がありません。
なお、今回はInnoDB Clusterに焦点を置いていますが、InnoDB ReplicaSetにも適用が可能です。
InnoDB ReplicaSetの場合はレプリケーション遅延が出ているインスタンスを一時的にアクセス不可にすることなども可能です。
バージョン
[root@centos8-mysql:~]# mysqld -V /usr/sbin/mysqld Ver 8.0.21 for Linux on x86_64 (MySQL Community Server - GPL) [root@centos8-mysql:~]# mysql -V mysql Ver 8.0.20 for Linux on x86_64 (MySQL Community Server - GPL) [root@centos8-mysql:~]# mysqlsh -V mysqlsh Ver 8.0.21 for Linux on x86_64 - for MySQL 8.0.21 (MySQL Community Server (GPL)) [root@centos8-mysql:~]# mysqlrouter -V MySQL Router Ver 8.0.21 for Linux on x86_64 (MySQL Community - GPL)
InnoDB Group Replicationの準備
今回はMySQL Shellのdba.deploySandboxInstance()を使って、ローカルのVMに3台のサンドボックス環境を立ち上げ、それらでGroup Replicationを構成します。 dba.deploySandboxInstance()を使ってGroup Replicationを構築する手順は過去に紹介していますので、よろしければそちらをご参考ください。
koreshiki-nanno.hatenablog.com
今回は4000、4001、4002をそれぞれポート番号に持つインスタンスを用意し、Single-PrimaryモードでGroup Replicationを構築しました。
TEST MySQL 8.0.21 root@localhost:4000 ssl JS 2020-07-17 22:44:16 > cluster.status() { "clusterName": "myCluster", "defaultReplicaSet": { "name": "default", "primary": "127.0.0.1:4000", "ssl": "REQUIRED", "status": "OK", "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", "topology": { "127.0.0.1:4000": { "address": "127.0.0.1:4000", "mode": "R/W", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.21" }, "127.0.0.1:4001": { "address": "127.0.0.1:4001", "mode": "R/O", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.21" }, "127.0.0.1:4002": { "address": "127.0.0.1:4002", "mode": "R/O", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.21" } }, "topologyMode": "Single-Primary" }, "groupInformationSourceMember": "127.0.0.1:4000" }
MySQL Routerの準備
--bootstrap
オプションを使うことでmysqlrouter設定ファイルを自動で生成することが可能です。
[root@centos8-mysql:~]# mysqlrouter --bootstrap=localhost:4000 --user=mysqlrouter [root@centos8-mysql:~]# systemctl start mysqlrouter [root@centos8-mysql:~]# systemctl status mysqlrouter ● mysqlrouter.service - MySQL Router Loaded: loaded (/usr/lib/systemd/system/mysqlrouter.service; disabled; vendor preset: disabled) Active: active (running) since Fri 2020-07-17 22:50:14 JST; 3s ago Main PID: 4514 (mysqlrouter) Tasks: 9 (limit: 11492) Memory: 10.9M CGroup: /system.slice/mysqlrouter.service └─4514 /usr/bin/mysqlrouter -c /etc/mysqlrouter/mysqlrouter.conf 7月 17 22:50:14 centos8-mysql systemd[1]: Started MySQL Router. 7月 17 22:50:14 centos8-mysql mysqlrouter[4514]: logging facility initialized, switching logging to loggers specified in configuration
まずはデフォルトの状態でRead-Only用のポートでアクセスすると2つのセカンダリインスタンスに接続できるか確認します。
[root@centos8-mysql:~]# mysql -h127.0.0.1 -P6447 -e "select @@port" +--------+ | @@port | +--------+ | 4001 | +--------+ [root@centos8-mysql:~]# mysql -h127.0.0.1 -P6447 -e "select @@port" +--------+ | @@port | +--------+ | 4002 | +--------+
デフォルトのルーティングストラテジーはラウンドロビンなので上記のようにそれぞれのセカンダリインスタンスにリクエストがいっていることがわかります。
インスタンスを一時的にアクセス不可能にする
それでは今回の主題であるインスタンスを一時的に不可能にする機能を使ってみます。 使い方はとてもシンプルです。
TEST MySQL 8.0.21 root@localhost:4000 ssl JS 2020-07-17 23:00:22 > cluster.setInstanceOption("localhost:4002", "tag:_hidden", true)
Cluster.setInstanceOptionの第1引数には対象となるインスタンスを、第2引数と第3引数にはタグ及び設定する値を指定します。
今回の場合4002ポートのインスタンスの_hidden
タグをtrue、つまり隠れている状態にします。
この状態で再びRead-Only用のポートにアクセスしてみます。
[root@centos8-mysql:~]# mysql -h127.0.0.1 -P6447 -e "select @@port" +--------+ | @@port | +--------+ | 4001 | +--------+ [root@centos8-mysql:~]# mysql -h127.0.0.1 -P6447 -e "select @@port" +--------+ | @@port | +--------+ | 4001 | +--------+ [root@centos8-mysql:~]# mysql -h127.0.0.1 -P6447 -e "select @@port" +--------+ | @@port | +--------+ | 4001 | +--------+
ポートが4002のインスタンスにはアクセスが飛ばないことが確認できました。
この時でもインスタンスの状態はONLINEになっています。
TEST MySQL 8.0.21 root@localhost:4000 ssl JS 2020-07-17 23:01:33 > cluster.status() { "clusterName": "myCluster", "defaultReplicaSet": { "name": "default", "primary": "127.0.0.1:4000", "ssl": "REQUIRED", "status": "OK", "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", "topology": { "127.0.0.1:4000": { "address": "127.0.0.1:4000", "mode": "R/W", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.21" }, "127.0.0.1:4001": { "address": "127.0.0.1:4001", "mode": "R/O", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.21" }, "127.0.0.1:4002": { "address": "127.0.0.1:4002", "mode": "R/O", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.21" } }, "topologyMode": "Single-Primary" }, "groupInformationSourceMember": "127.0.0.1:4000" }
この方法が従来のインスタンスを停止したり、removeInstance()する方法と比べて優れている点はGroup Replicationの構成自体には何の変更もしていないという点です。 レプリケーション自体は継続されてるのでデータはしっかりと同期されています。
-- まずは5件のデータのみ [root@centos8-mysql:~]# mysql -h127.0.0.1 -P4002 -e "select * from testdb.t1" +---+---------------------+ | a | b | +---+---------------------+ | 1 | 2020-07-17 23:15:08 | | 2 | 2020-07-17 23:15:08 | | 3 | 2020-07-17 23:15:09 | | 4 | 2020-07-17 23:15:15 | | 5 | 2020-07-17 23:15:16 | +---+---------------------+ -- 1件INSERT [root@centos8-mysql:~]# mysql -h127.0.0.1 -P6446 testdb -e "insert into testdb.t1 values (default, now())" -- データは同期されている [root@centos8-mysql:~]# mysql -h127.0.0.1 -P4002 -e "select * from testdb.t1" +---+---------------------+ | a | b | +---+---------------------+ | 1 | 2020-07-17 23:15:08 | | 2 | 2020-07-17 23:15:08 | | 3 | 2020-07-17 23:15:09 | | 4 | 2020-07-17 23:15:15 | | 5 | 2020-07-17 23:15:16 | | 6 | 2020-07-17 23:16:15 | +---+---------------------+
_hiddenの真偽を確認する
インスタンスがルーティングテーブルに含まれているかどうかはmysql_innodb_cluster_metadata.instances.attributes
などから確認が可能です。
mysql> SELECT instance_id, instance_name, address, attributes->>'$.tags' FROM mysql_innodb_cluster_metadata.instances; +-------------+----------------+----------------+-----------------------+ | instance_id | instance_name | address | attributes->>'$.tags' | +-------------+----------------+----------------+-----------------------+ | 1 | 127.0.0.1:4000 | 127.0.0.1:4000 | NULL | | 2 | 127.0.0.1:4001 | 127.0.0.1:4001 | NULL | | 3 | 127.0.0.1:4002 | 127.0.0.1:4002 | {"_hidden": true} | +-------------+----------------+----------------+-----------------------+
_hidden:true
であればMySQL Routerを通しての接続が不可能となります。
アクセス不可にするインスタンスの既存コネクションを処理する
tagには_hidden
以外にも_disconnect_existing_sessions_when_hidden
というものがあります。
これは何かというと_hidden
タグをtrueにした時にそのインスタンスの既存コネクションをどのように処理するかを制御するもので
trueならば既存コネクションを切断する(デフォルトの動作)
falseならば既存コネクションは切断しない
といった動作となります。
trueの場合
mysql> SELECT instance_id, instance_name, address, attributes->>'$.tags' FROM mysql_innodb_cluster_metadata.instances where instance_id = 3; +-------------+----------------+----------------+-----------------------------------------------------------------------+ | instance_id | instance_name | address | attributes->>'$.tags' | +-------------+----------------+----------------+-----------------------------------------------------------------------+ | 3 | 127.0.0.1:4002 | 127.0.0.1:4002 | {"_hidden": true, "_disconnect_existing_sessions_when_hidden": true} | +-------------+----------------+----------------+-----------------------------------------------------------------------+
mysql80-root@127.0.0.1:[(none)]>select @@port; +--------+ | @@port | +--------+ | 4002 | +--------+ 1 row in set (0.00 sec) mysql80-root@127.0.0.1:[(none)]>\s ERROR 2013 (HY000): Lost connection to MySQL server during query mysql80-root@127.0.0.1:[(none)]>\s ERROR 2006 (HY000): MySQL server has gone away No connection. Trying to reconnect... Connection id: 58 Current database: *** NONE *** -------------- mysql Ver 8.0.20 for Linux on x86_64 (MySQL Community Server - GPL) Connection id: 58 Current database: Current user: root@localhost SSL: Cipher in use is TLS_AES_256_GCM_SHA384 Current pager: less -i -X -R -E Using outfile: '' Using delimiter: ; Server version: 8.0.21 MySQL Community Server - GPL Protocol version: 10 Connection: 127.0.0.1 via TCP/IP Server characterset: utf8mb4 Db characterset: utf8mb4 Client characterset: utf8mb4 Conn. characterset: utf8mb4 TCP port: 6447 Binary data as: Hexadecimal Uptime: 1 hour 47 min 29 sec Threads: 6 Questions: 2557 Slow queries: 0 Opens: 333 Flush tables: 3 Open tables: 251 Queries per second avg: 0.396 -------------- mysql80-root@127.0.0.1:[(none)]>select @@port; +--------+ | @@port | +--------+ | 4001 | +--------+ 1 row in set (0.00 sec)
4002ポートに接続していたセッションはLost connectionエラーを受け取っています。
そして再度接続した際はまだ_hidden
タグがtrueとなっていない4001ポートに接続しに行きます。
これはデフォルトの動作です。
falseの場合
mysql> SELECT instance_id, instance_name, address, attributes->>'$.tags' FROM mysql_innodb_cluster_metadata.instances where instance_id = 3; +-------------+----------------+----------------+-----------------------------------------------------------------------+ | instance_id | instance_name | address | attributes->>'$.tags' | +-------------+----------------+----------------+-----------------------------------------------------------------------+ | 3 | 127.0.0.1:4002 | 127.0.0.1:4002 | {"_hidden": true, "_disconnect_existing_sessions_when_hidden": false} | +-------------+----------------+----------------+-----------------------------------------------------------------------+
mysql80-root@127.0.0.1:[(none)]>select @@port; +--------+ | @@port | +--------+ | 4002 | +--------+ 1 row in set (0.00 sec) mysql80-root@127.0.0.1:[(none)]>\s -------------- mysql Ver 8.0.20 for Linux on x86_64 (MySQL Community Server - GPL) Connection id: 69 Current database: Current user: root@localhost SSL: Cipher in use is TLS_AES_256_GCM_SHA384 Current pager: less -i -X -R -E Using outfile: '' Using delimiter: ; Server version: 8.0.21 MySQL Community Server - GPL Protocol version: 10 Connection: 127.0.0.1 via TCP/IP Server characterset: utf8mb4 Db characterset: utf8mb4 Client characterset: utf8mb4 Conn. characterset: utf8mb4 TCP port: 6447 Binary data as: Hexadecimal Uptime: 1 hour 49 min 40 sec Threads: 7 Questions: 2740 Slow queries: 0 Opens: 329 Flush tables: 3 Open tables: 247 Queries per second avg: 0.416 -------------- mysql80-root@127.0.0.1:[(none)]>select @@port; +--------+ | @@port | +--------+ | 4002 | +--------+ 1 row in set (0.00 sec)
ちょっとこのログだけではわかりにくいですが、_disconnect_existing_sessions_when_hidden
タグがfalseの場合は_hidden
タグをtrueにしても
既存セッションは切断されることなくそのインスタンスに接続したままとなります。
この辺りは何を行うために_hidden
タグをtrueにするかによって_disconnect_existing_sessions_when_hidden
タグをtrueにするかfalseにするか決めるべきでしょう。
といっても大抵の場合は_disconnect_existing_sessions_when_hidden
タグはデフォルトのtrueでいいような気もします。
まとめ
ということで、今回はMySQL Routerに新たに追加されたインスタンスへのアクセス制御の方法を見てきました。
これを使うことで運用がさらに簡単になるのではないでしょうか。
今回は、このあたりで。