□MySQL8でInnoDBクラスターを試す
・まずは環境設定
CentOS8のインストールが終わった直後くらいの状態からの構築メモ。
dnf -y update
dnf -y install emacs-nox
dnf -y install epel-release
dnf -y install certbot # pythonも一緒に入ってくれるから手抜き
alternatives --config python
で、python3を指定する。
/usr/bin/env python
で動けばOK。
クラスタを構成するインスタンス(サーバー)のホスト名をhostsファイルに記述。
emacs /etc/hosts
192.168.79.210 cl000
192.168.79.211 cl001
192.168.79.212 cl002
さらに、コマンドプロンプトにもインスタンス名が出るようにする。
emacs /etc/hostname
cl000
さらに、SELinuxを無効にしておく。
emacs /etc/sysconfig/selinux
####SELINUX=enforcing
SELINUX=permissive
さらに、MySQLに対して外部からアクセスを許可させる。(3306番ポートの開放)
firewall-cmd --add-service=mysql --zone=public --permanent
firewall-cmd --reload
※本来であれば、これとは別にiptablesでクラスタを構成するインスタンスのみが3306番ポートにアクセスできるように制限すべき。
そしたらいったん再起動。
reboot
・MySQL8のインストール
CentOS8が配っているディストリビューション版ではツールが足りないのと、そもそもセキュリティレベルが低すぎるので、きちんと(?)公式からコミュニティ版をインストールする。
dnf -y install https://dev.mysql.com/get/mysql80-community-release-el8-1.noarch.rpm
dnf --repo=mysql80-community search mysql-community
dnf --repo=mysql80-community info mysql-community-server
dnf -y install --repo=mysql80-community \
mysql-community-server mysql-community-client mysql-community-common
dnf -y install mysql-shell mysql-router-community
※mysql-community-devel mysql-community-libsを入れようとすると、コンフリクトが発生するので注意。
でもって、MySQLサーバーの基本設定をする。
emacs /etc/my.cnf
:
#default_authentication_plugin = mysql_native_password
default_authentication_plugin = mysql_native_password
slow_query_log = 1
slow_query_log_file = /var/lib/mysql/slow_query.log
long_query_time = 3
max_connections=200
innodb_file_per_table
loose-group_replication_ip_whitelist="192.168.79.0/24"
:
※まだこの段階ではgroup_replication_ip_whitelistはエラーになるので接頭語「loose-」をつけてスルーさせる
https://dev.mysql.com/doc/refman/8.0/en/group-replication-ip-address-whitelisting.html
そしたらMySQLを起動して、root用の一時パスワードを確認する。
systemctl enable mysqld
systemctl restart mysqld
systemctl status mysqld
grep --color=auto 'temporary password' /var/log/mysqld.log
rootの一時パスワードでいったんMySQLにログインして、パスワードを変更する。
mysql -u root -p
SET GLOBAL validate_password.length=0;
SET GLOBAL validate_password.mixed_case_count=0;
SET GLOBAL validate_password.number_count=0;
SET GLOBAL validate_password.special_char_count=0;
SET GLOBAL validate_password.policy=LOW;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'kantan7pwd';
SHOW VARIABLES LIKE '%validate_password%';
EXIT;
※簡単なパスワードを設定できるようにvalidate_password.~を変更している
・続いてクラスタの設定
MySQL ShellでMySQLにログインしてクラスタ用のアカウントを設定する。
mysqlsh
\c root@localhost
dba.configureLocalInstance()
とすると
Configuring local MySQL instance listening at port 3306 for use in an InnoDB cluster… This instance reports its own address as localhost.localdomain:3306 Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed. ERROR: User 'root' can only connect from 'localhost'. New account(s) with proper source address specification to allow remote connection from all instances must be created to manage the cluster. 1) Create remotely usable account for 'root' with same grants and password 2) Create a new admin account for InnoDB cluster with minimal required grants 3) Ignore and continue 4) Cancel Please select an option [1]:
と聞かれる。
rootをそのままネットワーク接続OKなアカウントとして設定するのはちょっとアレなので、クラスタ専用のアカウント(clroot)にするため、2を選択する。
そしてアカウントとそのパスワードの設定をする。
Please provide an account name (e.g: icroot@%) to have it created with the necessary privileges or leave empty and press Enter to cancel. Account Name: clroot Password for new account: * Confirm password: *
パスワードの確認ができると、設定を変更するか、MySQLを再起動するかと聞かれるので、それぞれyとする。
NOTE: Some configuration options need to be fixed: +--------------------------+---------------+----------------+--------------------------------------------------+ | Variable | Current Value | Required Value | Note | +--------------------------+---------------+----------------+--------------------------------------------------+ | binlog_checksum | CRC32 | NONE | Update the server variable | | enforce_gtid_consistency | OFF | ON | Update read-only variable and restart the server | | gtid_mode | OFF | ON | Update read-only variable and restart the server | | server_id | 1 | | Update read-only variable and restart the server | +--------------------------+---------------+----------------+--------------------------------------------------+ Some variables need to be changed, but cannot be done dynamically on the server. Do you want to perform the required configuration changes? [y/n]: y Do you want to restart the instance after configuring it? [y/n]: y
とすると、
Cluster admin user 'clroot'@'%' created. Configuring instance… The instance 'cl000:3306' was configured to be used in an InnoDB cluster. Restarting MySQL… NOTE: MySQL server at cl000:3306 was restarted.
となるので、設定状況を確認してみる。
dba.checkInstanceConfiguration('clroot@cl000')
Please provide the password for 'clroot@cl000': * Save password for 'clroot@cl000'? [Y]es/[N]o/Ne[v]er (default No): Validating local MySQL instance listening at port 3306 for use in an InnoDB cluster… This instance reports its own address as cl000:3306 Clients and other cluster members will communicate with it through this address by default. If this is not correct, the report_host MySQL system variable should be changed. Checking whether existing tables comply with Group Replication requirements… No incompatible tables detected Checking instance configuration… Instance configuration is compatible with InnoDB cluster The instance 'cl000:3306' is valid to be used in an InnoDB cluster. { "status": "ok" }
となればOK。
グループレプリケーション用の33061番ポートを「それぞれのインスタンス」で開ける。
emacs /etc/firewalld/services/mysql-cluster.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
<short>MySQL</short>
<description>MySQL Database Server Group Replication</description>
<port protocol="tcp" port="33061"/>
</service>
少し待ってfirewalldがこのxmlファイルを認識したら、
firewall-cmd --add-service=mysql-cluster --zone=public --permanent
firewall-cmd --list-services --zone=public --permanent
firewall-cmd --reload
とすれば、インスタンス間でクラスタ用の通信が行えるようになる。
これを他のインスタンス(cl001, cl002)でも同様に行う。
・実際のクラスタの作成
ここまでの準備ができたら、あとは実際にクラスタを作成する。
まずはプライマリインスタンス(cl000)にて、
mysqlsh
\c clroot@cl000
dba.createCluster('Cluster000')
とすると、
A new InnoDB cluster will be created on instance 'cl000:3306'. Disabling super_read_only mode on instance 'cl000:3306'. Validating instance configuration at cl000:3306… This instance reports its own address as cl000:3306 Instance configuration is suitable. NOTE: Group Replication will communicate with other members using 'cl000:33061'. Use the localAddress option to override. Creating InnoDB cluster 'Cluster000' on 'cl000:3306'… Adding Seed Instance… Cluster successfully created. Use Cluster.addInstance() to add MySQL instances. At least 3 instances are needed for the cluster to be able to withstand up to one server failure. <Cluster:Cluster000>
としてプライマリインスタンスができる。
そしたら、インスタンス(cl001とcl002)を追加する。
cluster = dba.getCluster()
とすることで、clusterに「Cluster000」が関連付けされるので
cluster.status()
とすると、
{ "clusterName": "Cluster000", "defaultReplicaSet": { "name": "default", "primary": "cl000:3306", "ssl": "REQUIRED", "status": "OK_NO_TOLERANCE", "statusText": "Cluster is NOT tolerant to any failures.", "topology": { "cl000:3306": { "address": "cl000:3306", "mode": "R/W", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.19" } }, "topologyMode": "Single-Primary" }, "groupInformationSourceMember": "cl000:3306" }
のようにして、クラスタに対しての操作ができるようになるので、さっそくインスタンスを追加してみる。
cluster.addInstance('clroot@cl001')
とすると、どのようなリカバリ(複製)モードにするか聞かれる。
NOTE: The target instance 'cl001:3306' has not been pre-provisioned (GTID set is empty). The Shell is unable to decide whether incremental state recovery can correctly provision it. The safest and most convenient way to provision a new instance is through automatic clone provisioning, which will completely overwrite the state of 'cl001:3306' with a physical snapshot from an existing cluster member. To use this method by default, set the 'recoveryMethod' option to 'clone'. The incremental state recovery may be safely used if you are sure all updates ever executed in the cluster were done with GTIDs enabled, there are no purged transactions and the new instance contains the same GTID set as the cluster or a subset of it. To use this method by default, set the 'recoveryMethod' option to 'incremental'. Please select a recovery method [C]lone/[I]ncremental recovery/[A]bort (default Clone):
今回は初めてのクラスタ構築なので、C(複製)でもI(増分リカバリ)でもどちらでもOK。
※https://dev.mysql.com/doc/refman/8.0/en/mysql-innodb-cluster-working-with-clone.html
とすると、
NOTE: Group Replication will communicate with other members using 'cl001:33061'. Use the localAddress option to override. Validating instance configuration at cl001:3306… This instance reports its own address as cl001:3306 Instance configuration is suitable. A new instance will be added to the InnoDB cluster. Depending on the amount of data on the cluster this might take from a few seconds to several hours. Adding instance to the cluster… Monitoring recovery process of the new cluster member. Press ^C to stop monitoring and let it continue in background. Clone based state recovery is now in progress. NOTE: A server restart is expected to happen as part of the clone process. If the server does not support the RESTART command or does not come back after a while, you may need to manually start it back. * Waiting for clone to finish… NOTE: cl001:3306 is being cloned from cl000:3306 ** Stage DROP DATA: Completed ** Clone Transfer FILE COPY ############################################################ 100% Completed PAGE COPY ############################################################ 100% Completed REDO COPY ############################################################ 100% Completed ** Stage RECOVERY: | NOTE: cl001:3306 is shutting down… * Waiting for server restart… ready * cl001:3306 has restarted, waiting for clone to finish… * Clone process has finished: 61.72 MB transferred in about 1 second (~1.00 B/s) State recovery already finished for 'cl001:3306' The instance 'cl001' was successfully added to the cluster.
として、インスタンスの追加ができたら、もう一つ(cl002)も追加。
cluster.addInstance('clroot@cl002')
こちらも
: The instance 'cl002' was successfully added to the cluster.
となれば、追加完了。
cluster.status()
{ "clusterName": "Cluster000", "defaultReplicaSet": { "name": "default", "primary": "cl000:3306", "ssl": "REQUIRED", "status": "OK", "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", "topology": { "cl000:3306": { "address": "cl000:3306", "mode": "R/W", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.19" }, "cl001:3306": { "address": "cl001:3306", "mode": "R/O", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.19" }, "cl002:3306": { "address": "cl002:3306", "mode": "R/O", "readReplicas": {}, "replicationLag": null, "role": "HA", "status": "ONLINE", "version": "8.0.19" } }, "topologyMode": "Single-Primary" }, "groupInformationSourceMember": "cl000:3306" }
これでcl000(プライマリ)、cl001(リードオンリー)、cl002(リードオンリー)のクラスタ構築ができた。
※/var/lib/mysql/mysqld-auto.cnfというファイルができる。
・MySQL Routerの設定
クラスタを構成したら、プライマリインスタンスに対して直接接続してあれこれしてもいいが、MySQL Routerというバランサーツールがあるので、これを素直に使うほうがよい。
※https://dev.mysql.com/doc/refman/8.0/en/mysql-innodb-cluster-using-router.html
公式ドキュメントには、MySQL Routerはアプリケーションと同じホストでの動作を推奨しているが、とりあえず今回はプライマリインスタンス上でMySQL Routerも動かしてしまう。
まずはcl000(プライマリ)を指定して、初期設定をする。
mysqlrouter --bootstrap clroot@cl000 --user=mysqlrouter
としてパスワードを入力(mysqlshellのような*のエコーバックは無い)すると、
Please enter MySQL password for clroot: # Bootstrapping system MySQL Router instance… - Creating account(s) (only those that are needed, if any) - Verifying account (using it to run SQL queries that would be run by Router) - Storing account in keyring - Adjusting permissions of generated files - Creating configuration /etc/mysqlrouter/mysqlrouter.conf Existing configuration backed up to '/etc/mysqlrouter/mysqlrouter.conf.bak' # MySQL Router configured for the InnoDB Cluster 'Cluster000' After this MySQL Router has been started with the generated configuration $ /etc/init.d/mysqlrouter restart or $ systemctl start mysqlrouter or $ mysqlrouter -c /etc/mysqlrouter/mysqlrouter.conf the cluster 'Cluster000' can be reached by connecting to: ## MySQL Classic protocol - Read/Write Connections: localhost:6446 - Read/Only Connections: localhost:6447 ## MySQL X protocol - Read/Write Connections: localhost:64460 - Read/Only Connections: localhost:64470
というメッセージが出て、設定が完了する。
※/etc/mysqlrouter/mysqlrouter.confに設定が出力される。
MySQL Routerをサービスとして常に動かす場合には、メッセージにあるように
systemctl ebanle mysqlrouter
systemctl start mysqlrouter
systemctl status mysqlrouter
とすればよい。
・シングルプライマリモードのテスト
プライマリインスタンス(cl000)で
mysql -u root -p
としてMySQLにログインをしてテスト用のデータベースとユーザーを作る。
use mysql;
DROP DATABASE IF EXISTS `test001_db`;
DROP USER IF EXISTS 'test001'@'localhost';
DROP USER IF EXISTS 'test001'@'%';
FLUSH PRIVILEGES;
CREATE DATABASE `test001_db`;
CREATE USER 'test001'@'localhost' IDENTIFIED BY 'test001pwd';
CREATE USER 'test001'@'%' IDENTIFIED BY 'test001pwd';
SELECT user, host, plugin, authentication_string FROM user;
GRANT ALL ON `test001_db`.* TO 'test001'@'localhost';
GRANT ALL ON `test001_db`.* TO 'test001'@'%';
FLUSH PRIVILEGES;
EXIT;
すると、自動的にリードオンリーインスタンス(cl001, cl002)にもデータベースができるので、プライマリインスタンス(cl000)で、テスト用のデータベースを操作してみる。
普通にmysql(クライアント)コマンドでプライマリインスタンスに接続してもいいが、せっかくMySQL Routerを動かしたので、こちらに接続してみる。
mysql --host=cl000 --port=6446 --user=test001 -p
use test001_db;
DROP TABLE IF EXISTS `test001`;
CREATE TABLE test001
(
column01 int,
column02 text,
column03 text,
PRIMARY KEY(column01)
);
INSERT INTO test001 VALUES (1, 'bbb', 'ccc');
※テーブルにプライマリキーがないとダメ
※リードオンリーポートで上記SQLを試してみると、ちゃんとエラーになる。
mysql –host=cl000 –port=6447 –user=test001 -p
・やっとMySQL8のクラスタ構築ができたので…
あとは公式ドキュメントを読みながら、インスタンスを止めたりして高可用性を試したり、マルチプライマリモードも試してみたい。