はじめに #
第1回では、MySQL InnoDB Clusterのdeploy方法について紹介しましたが、今回はInnoDBのコンポーネントの1つであるMySQL-Routerについて紹介します。
1. MySQL-Routerとは #
k8sでpodにアクセスするには通常Serviceが利用されますが、ServiceによるpodへのルーティングはラウンドロビンのためPrimary/Secondaryを区別する必要があるmysqlには不適切です。
# SecondaryにルーティングされるとReadOnlyになるのでk8s任せはNG
mysql-1(Primary, RW)
Service -> mysql-2(Secondary, RO)
mysql-3(Secondary, RO)
そこでMySQL InnoDB ClusterではMySQL-Routerが自動でdeployされます。このMySQL-Routerは優れもので以下の機能を備えています。公式docs
- Primary/Secondaryを自動で判別して適切なルーティングの実施
- FailoverにてPrimaryが変更された場合でも自動で追従
- ReadはSecondary、WriteはPrimaryに分散させることでパフォーマンスの向上(R/W Split)
従ってMySQL-Routerを使った場合の通信は、k8sのServiceがMySQL-Routerのpodに通信を渡すことになります。
# Writeの場合(Primaryに必ずルーティング)
mysql-router
Service -> mysql-router -> mysql-1(primary, RW)
mysql-router
# Readの場合(Secondaryにルーティング)
mysql-router
Service -> mysql-router -> mysql-2(secondary, RO)
mysql-router mysql-3(secondary, RO)
本来MySQL-Routerのdeployには、mysqlrouter.cnf
を作成して、接続するDB等の情報を記載する必要や、MySQL-Router自体を複数用意して分散させる必要があります。参考(公式HP)
しかしmysql-operatorやk8sのReplica数の設定によってこれらの手順を自動で実施してくれます。
2. Portによる挙動の違い #
MySQL-Routerには大きく分けて以下の3つのモードがありアクセスするportによって動作を制御できます。
port | 役割 |
---|---|
6446 | Primaryへアクセス[RW] |
6447 | Secondaryのいずれかにアクセス[RO] |
6450 | WriteはPrimary、ReadはSecondaryに自動で判別[RW Split] |
RW SplitはMySQL8.2からの新機能です。参考(The Oracle MySQL Japan Blog)
また今回は紹介しませんがmysqlx用のportも存在します。公式HP
k8sで展開したMySQL RouterではServiceを見てみると、defaultの:3306
はTargetPort: 6446/TCP
にルーティングされているので、常にPrimaryにアクセスする形になります。適宜変更するか第3回で紹介するhelmのカスタマイズによってdefaultの挙動を変更しましょう。
$ kubectl describe service mysql-cluster
IP: 10.96.237.122
IPs: 10.96.237.122
Port: mysql 3306/TCP
TargetPort: 6446/TCP
NodePort: mysql 31117/TCP
Endpoints: 192.168.2.52:6446
Port: mysqlx 33060/TCP
TargetPort: 6448/TCP
NodePort: mysqlx 31811/TCP
Endpoints: 192.168.2.52:6448
Port: mysql-alternate 6446/TCP
TargetPort: 6446/TCP
NodePort: mysql-alternate 32475/TCP
Endpoints: 192.168.2.52:6446
Port: mysqlx-alternate 6448/TCP
TargetPort: 6448/TCP
NodePort: mysqlx-alternate 30889/TCP
Endpoints: 192.168.2.52:6448
Port: mysql-ro 6447/TCP
TargetPort: 6447/TCP
NodePort: mysql-ro 32048/TCP
Endpoints: 192.168.2.52:6447
Port: mysqlx-ro 6449/TCP
TargetPort: 6449/TCP
NodePort: mysqlx-ro 32510/TCP
Endpoints: 192.168.2.52:6449
Port: mysql-rw-split 6450/TCP
TargetPort: 6450/TCP
NodePort: mysql-rw-split 30946/TCP
Endpoints: 192.168.2.52:6450
Port: router-rest 8443/TCP
TargetPort: 8443/TCP
NodePort: router-rest 30997/TCP
Endpoints: 192.168.2.52:8443
3. RW Splitの挙動 #
MySQL-Routerの挙動がわかったので、実際にRW Splitが実現しているか確認します。
deployされているInnoDB Clusterは以下の通りで、mysql-cluster-0
がPrimaryです。
$ kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
mysql-cluster-0 2/2 Running 0 3h6m
mysql-cluster-1 2/2 Running 0 3h6m
mysql-cluster-2 2/2 Running 0 3h6m
mysql-cluster-router-57c657f6cf-xtwwm 1/1 Running 0 3h5m
$ kubectl logs mysql-cluster-router-57c657f6cf-xtwwm -n dev
Metadata for cluster 'mysql_cluster' has 3 member(s), single-primary:
mysql-cluster-0.mysql-cluster-instances.dev.svc.cluster.local:3306 / 33060 - mode=RW
mysql-cluster-2.mysql-cluster-instances.dev.svc.cluster.local:3306 / 33060 - mode=RO
mysql-cluster-1.mysql-cluster-instances.dev.svc.cluster.local:3306 / 33060 - mode=RO
この状態でMySQLの:6450
(RW Split)にアクセスします。
$ mysql -h 192.168.1.220 -P 6450 -p
Enter password:
mysql>
書き込みモードをcheckすると、mysql-cluster-0
とPrimaryにアクセスし、read_onlyもfalseであることがわかります。
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@hostname,@@port,@@read_only;
+-----------------+--------+-------------+
| @@hostname | @@port | @@read_only |
+-----------------+--------+-------------+
| mysql-cluster-0 | 3306 | 0 |
+-----------------+--------+-------------+
1 row in set (0.01 sec)
mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)
次に読み込みモードをcheckすると、mysql-cluster-1
とSecondaryにアクセスし、read_onlyもtrueになっています。
mysql> START TRANSACTION READ ONLY;
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT @@hostname,@@port,@@read_only;
+-----------------+--------+-------------+
| @@hostname | @@port | @@read_only |
+-----------------+--------+-------------+
| mysql-cluster-2 | 3306 | 1 |
+-----------------+--------+-------------+
1 row in set (0.00 sec)
mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)
これでRW Splitによって同一SessionであってもWriteはPrimaryにReadはSecondaryに自動で振り分けてくれることが確認できました。
session毎に1つのPrimaryと1つのSecondaryが選ばれるので、同一Sessionでは常に同じSecondarymysql-cluster-2
が選ばれます。参考(公式doc)
違うSecondaryも使用したい場合はsessionを変える必要があるので注意しましょう。
$ mysql -h 192.168.1.220 -P 6450 -p
Enter password:
mysql> START TRANSACTION READ ONLY;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@hostname,@@port,@@read_only;
+-----------------+--------+-------------+
| @@hostname | @@port | @@read_only |
+-----------------+--------+-------------+
| mysql-cluster-1 | 3306 | 1 |
+-----------------+--------+-------------+
1 row in set (0.00 sec)
4. DB障害の挙動 #
PrimaryのNodeが故障した場合にRW Splitの挙動はどうなるでしょうか? 故障を模擬するためPrimaryのpodを削除してみます
$ kubectl delete pod mysql-cluster-0 -n dev
pod "mysql-cluster-0" deleted
すると、同一session中であればReconnectが発生し、Failover後のmysql-cluster-2
繋がるようになりました。
mysql> START TRANSACTION;
ERROR 2013 (HY000): Lost connection to MySQL server during query
No connection. Trying to reconnect...
Connection id: 0
Current database: *** NONE ***
Query OK, 0 rows affected (0.03 sec)
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@hostname,@@port,@@read_only;
+-----------------+--------+-------------+
| @@hostname | @@port | @@read_only |
+-----------------+--------+-------------+
| mysql-cluster-2 | 3306 | 0 |
+-----------------+--------+-------------+
1 row in set (0.00 sec)
mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)
Routerのlogにはmysql-cluster-0
が止まってから、mysql-cluster-2
が新たなPrimaryとして選出される様子を見ることができます。(今回はnode故障ではなくpod単体削除のため、mysql-cluster-0
はk8sによって自動で復旧しSecondaryとして再登録されています。)
$ kubectl logs mysql-cluster-router-57c657f6cf-xtwwm -n dev
2024-06-09 11:15:34 metadata_cache WARNING [7f8508763700] Failed fetching metadata from metadata server on mysql-cluster-0.mysql-cluster-instances.dev.svc.cluster.local:3306 - No result returned for v2_this_instance metadata query
2024-06-09 11:15:34 metadata_cache INFO [7f8508763700] Connected with metadata server running on mysql-cluster-2.mysql-cluster-instances.dev.svc.cluster.local:3306
2024-06-09 11:15:34 metadata_cache WARNING [7f8508763700] GR member mysql-cluster-0.mysql-cluster-instances.dev.svc.cluster.local:3306 (51aeada7-2630-11ef-8617-56a8e7260f34), state: 'Online' missing in the metadata, ignoring
2024-06-09 11:15:34 metadata_cache INFO [7f8508763700] Potential changes detected in cluster after metadata refresh (view_id=0)
2024-06-09 11:15:34 metadata_cache INFO [7f8508763700] Metadata for cluster 'mysql_cluster' has 2 member(s), single-primary:
2024-06-09 11:15:34 metadata_cache INFO [7f8508763700] mysql-cluster-2.mysql-cluster-instances.dev.svc.cluster.local:3306 / 33060 - mode=RO
2024-06-09 11:15:34 metadata_cache INFO [7f8508763700] mysql-cluster-1.mysql-cluster-instances.dev.svc.cluster.local:3306 / 33060 - mode=RO
2024-06-09 11:15:34 routing INFO [7f8508763700] Stop accepting connections for routing routing:bootstrap_x_rw listening on 0.0.0.0:6448
2024-06-09 11:15:34 routing INFO [7f8508763700] Disconnecting client 192.168.1.217:16997 from server
2024-06-09 11:15:34 routing INFO [7f8508763700] Routing routing:bootstrap_rw_split listening on '0.0.0.0:6450' got request to disconnect 1 invalid connections: metadata change
2024-06-09 11:15:34 routing INFO [7f8508763700] Stop accepting connections for routing routing:bootstrap_rw listening on 0.0.0.0:6446
2024-06-09 11:15:35 metadata_cache INFO [7f8508763700] Potential changes detected in cluster after metadata refresh (view_id=0)
2024-06-09 11:15:35 metadata_cache INFO [7f8508763700] Metadata for cluster 'mysql_cluster' has 2 member(s), single-primary:
2024-06-09 11:15:35 metadata_cache INFO [7f8508763700] mysql-cluster-2.mysql-cluster-instances.dev.svc.cluster.local:3306 / 33060 - mode=RW
2024-06-09 11:15:35 metadata_cache INFO [7f8508763700] mysql-cluster-1.mysql-cluster-instances.dev.svc.cluster.local:3306 / 33060 - mode=RO
2024-06-09 11:15:35 routing INFO [7f8508763700] Start accepting connections for routing routing:bootstrap_x_rw listening on '0.0.0.0:6448'
2024-06-09 11:15:35 routing INFO [7f8508763700] Start accepting connections for routing routing:bootstrap_rw listening on '0.0.0.0:6446'
2024-06-09 11:17:13 metadata_cache INFO [7f8508763700] Potential changes detected in cluster after metadata refresh (view_id=0)
2024-06-09 11:17:13 metadata_cache INFO [7f8508763700] Metadata for cluster 'mysql_cluster' has 2 member(s), single-primary:
2024-06-09 11:17:13 metadata_cache INFO [7f8508763700] mysql-cluster-2.mysql-cluster-instances.dev.svc.cluster.local:3306 / 33060 - mode=RW
2024-06-09 11:17:13 metadata_cache INFO [7f8508763700] mysql-cluster-1.mysql-cluster-instances.dev.svc.cluster.local:3306 / 33060 - mode=RO
2024-06-09 11:17:14 metadata_cache INFO [7f8508763700] Potential changes detected in cluster after metadata refresh (view_id=0)
2024-06-09 11:17:14 metadata_cache INFO [7f8508763700] Metadata for cluster 'mysql_cluster' has 3 member(s), single-primary:
2024-06-09 11:17:14 metadata_cache INFO [7f8508763700] mysql-cluster-2.mysql-cluster-instances.dev.svc.cluster.local:3306 / 33060 - mode=RW
2024-06-09 11:17:14 metadata_cache INFO [7f8508763700] mysql-cluster-1.mysql-cluster-instances.dev.svc.cluster.local:3306 / 33060 - mode=RO
2024-06-09 11:17:14 metadata_cache INFO [7f8508763700] mysql-cluster-0.mysql-cluster-instances.dev.svc.cluster.local:3306 / 33060 - mode=RO
5. Router障害時の挙動 #
今度はRouterを2台deployし、片方のRouterをdelete + 直後にmysqladmin ping
を実施します。
削除した直後のmysqld is alive
となっているので生きている方のRouterを使って継続してDBにアクセスできていることがわかります。
またdeleteした直後に新しいRouterがContainerCreating
で作成されるので、直ぐに冗長構成へ復帰することもできています。
$ date &&\
kubectl delete pod mysql-cluster-router-6cfb6c567b-n4jkt -n prd &&\
kubectl get pods -n prd | grep router &&\
mysqladmin -h 192.168.1.220 -p${MYSQL_PASSWORD} ping &&\
sleep 5 &&\
mysqladmin -h 192.168.1.220 -p${MYSQL_PASSWORD} ping &&\
date
Sun Jun 9 11:54:20 AM UTC 2024
pod "mysql-cluster-router-6cfb6c567b-n4jkt" deleted
mysql-cluster-router-6cfb6c567b-gqgds 1/1 Running 0 98s
mysql-cluster-router-6cfb6c567b-pvp69 0/1 ContainerCreating 0 1s
mysqld is alive
mysqld is alive
Sun Jun 9 11:54:26 AM UTC 2024
おわりに #
これでMySQL InnoDB Clusterのメインの機能については紹介できました。k8sとHelmさえ使えれば、非常に手軽に耐障害性 + RW Splitによるパフォーマンス向上のDBを作成することができます。(構成としては複雑なので運用時のトラブルが怖いですが)
次回はHelm chartを変更してカスタマイズする方法について紹介します。