41から始めました

文字通り41歳から始めたブログです。DB(MySQL)を使ってお仕事してるので、DB周りの話を中心に最近始めたこととかをTwitterのノリで書いています。なお、本サイトにおいて示されている見解は私個人の見解であり、所属団体や組織を代表するものではありません。

AWS(EC2)上にAZまたぎでGroup Replication(MySQL5.7)作った話

今回、AZをまたいだEC2(AWS Amazon Linux2)上にMySQL5.7(.25)をインストールし、Group Replicationを構成する手順を記載します。

MySQL Shellを使って設定するので、MySQL Shell用のインスタンスを一台用意します(※)。

※本来は3台のDB上のどれかに入れてもいいんですが、別途1台用意して外から設定したほうがインスタンス的にスマートなので1台追加します

※8.0.4(RC)では全ノードにMySQL Shellのインストールが必要だったはずなので、もしかするとMySQL5.7のこれよりも前のバージョンだとそういうこともあることだけは認識しといたほうが良いです。

前提

Group Replicationの要件

  • ノード数は最低3台以上
    • ノード数については奇数であることが推奨される
  • ストレージエンジンはInnoDBのみ
  • すべてのテーブルにPK必要
  • GTIDが有効であること

今回の構成

構成は次の通りとする。

  • AZは異なるところにDBインスタンスを配置
  • MySQL Shell用にインスタンスを構築。AZはPrimaryとそろえた(違ってもいい)
  • IP Addressについては仮のものなので各自の環境で変更してください。
    • server_idやAZも同様
    • server_idやPortはmy.cnfに書くのでお好きなように。
対象 server_id IPaddress(Private) 設定するPort AZ
Primary 1 10.0.0.1 33061 ap-northeast-1a
Secondary1 2 10.0.1.1 33062 ap-northeast-1c
Secondary2 3 10.0.2.1 33063 ap-northeast-1d
MySQL Shell - 10.0.0.2 - ap-northeast-1a

ハマりそうなポイント

2つあります。

  • AWSのセキュリティグループ
  • クラスタ構築時のwhitelist指定

特に前者は気づきやすいですが、後者は意外と知られていない気がするので注意してください。

EC2インスタンスの構築

やっていない人はここから。 1. VPC作成 2. サブネット追加

この辺についてはわからない人は別途記事を読んでゼロから作ってください。

EC2インスタンス作成

MySQL Shell用、3×DB用EC2インスタンス分作成

  • Linux 2 AMIで作った。
  • Amazon Linux 2 AMI (HVM), SSD Volume Type
  • t2.micro
  • セキュリティグループは注意

セキュリティグループ

グループレプリケーションのメンバーとなるインスタンスはお互いがTCP接続できないといけない。 3306と設定するポート番号は最低開けなきゃいけないんだけど、22とかもmy.cnfのコピーとかで使いたい人いるだろうし、 その辺の設定についてはお任せ。 (面倒くさい人はすべてのTCPをメンバーのセキュリティグループIDで設定しとけばいいんじゃないかと?) この疎通ができていないとAWS上でのグループレプリケーションは設定で当然ハマるので注意。

MySQL Group Replication構築

mariaDBアンインストール

デフォだとMariaDB入ってるんで、それ使うならそのままでいいけど、今回はMySQL5.7を入れるんで削除。 いちいち入れるかどうか聞かれるの面倒くさいので-yを付けてますが、確認したい人は-yを外してください。

sudo yum list installed | grep mariadb
sudo yum remove mariadb-libs -f

MySQL インストール

MySQL5.7を入れる (8.0を入れたい気持ちは グッ と抑える)

sudo yum install http://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm -y
sudo yum install mysql-community-server -y

キャッシュのクリア(念のため)

sudo yum clean all
sudo rm -rf /var/cache/yum

インストール結果の確認

yum repolist enabled | grep "mysql.\*-community.\*"

問題なければこんな表示が出るはず

mysql-connectors-community/x86_64     MySQL Connectors Community           74+21
mysql-tools-community/x86_64          MySQL Tools Community                   84
mysql57-community/x86_64              MySQL 5.7 Community Server             327

MySQLバージョン確認

確認コマンド

mysql --version

結果例

mysql  Ver 14.14 Distrib 5.7.25, for Linux (x86_64) using  EditLine wrapper

確認コマンド

mysqld -V

結果例

mysqld  Ver 5.7.25 for Linux on x86_64 (MySQL Community Server (GPL))

MySQL起動

sudo /bin/systemctl start mysqld.service
systemctl list-units | grep mysqld.service

rootパスワードの変更

パスワードの確認

cat /var/log/mysqld.log | grep password

確認したパスワードでログイン

mysql -u root -p

rootパスワードの変更

SET GLOBAL validate_password_length=8;
SET GLOBAL validate_password_policy=LOW;
ALTER USER root@localhost IDENTIFIED BY '<任意のパスワード>';

変更前パラメータ確認

自分が変更する予定のパラメータを確認しておくといいかと思います。 次の例では文字コード、コラーションオプティマイザルール、バッファプールを変更するので確認しています。

show variables like "chara%";
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8mb4                    |
| character_set_connection | utf8mb4                    |
| character_set_database   | latin1                     |
| character_set_filesystem | binary                     |
| character_set_results    | utf8mb4                    |
| character_set_server     | latin1                     |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.00 sec)

show variables like "optimizer_switch";
+------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Variable_name    | Value                                                                                                                                                                                                                                                                                                                                                                                                            |
+------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| optimizer_switch | index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on |
+------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

show variables like "innodb_buffer_pool_size";
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| innodb_buffer_pool_size | 134217728 |
+-------------------------+-----------+
1 row in set (0.00 sec)

パラメータ変更

変更パラメータ内容

以下はあくまで参考値なので作ったEC2の環境によって変えること。

今回の設定は

query_cache_size                 = 0
query_cache_type                    = 0
default_storage_engine              = InnoDB
transaction_isolation               = REPEATABLE-READ
lower_case_table_names              = 1
character_set_server                = utf8mb4
lc_messages                         = ja_JP

log_timestamps                      = system
explicit_defaults_for_timestamp     = 1
innodb_flush_neighbors              = 0
innodb_ft_min_token_size            = 1
internal_tmp_disk_storage_engine    = MyISAM
optimizer_switch                    = mrr_cost_based=off,batched_key_access=on
range_optimizer_max_mem_size        = 0
sql_mode                            = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

read_buffer_size                    = 2M
read_rnd_buffer_size                = 16M
sort_buffer_size                    = 2M

skip_external_locking
bulk_insert_buffer_size             = 8M
myisam_sort_buffer_size             = 8M
myisam_repair_threads               = 1
myisam_recover_options              = BACKUP,FORCE

innodb_temp_data_file_path          = ibtmp1:128M
innodb_flush_log_at_trx_commit      = 1
innodb_max_dirty_pages_pct          = 90
innodb_flush_method                 = O_DIRECT
performance_schema                  = 1
innodb_checksum_algorithm           = crc32
innodb_large_prefix                 = 1
innodb_print_all_deadlocks          = 1
innodb_buffer_pool_load_at_startup  = 0
innodb_buffer_pool_dump_at_shutdown = 1
innodb_buffer_pool_dump_pct         = 100
innodb_open_files                   = 2048
innodb_strict_mode                  = 1

# Memory等
innodb_buffer_pool_size             = 820M
innodb_buffer_pool_instances        = 8
innodb_write_io_threads             = 8
innodb_read_io_threads              = 8
innodb_thread_concurrency           = 0
innodb_log_buffer_size              = 16M
innodb_log_file_size                = 2048M
innodb_log_files_in_group           = 2
log_queries_not_using_indexes       = 0

[mysqldump]
quick
max_allowed_packet                  = 16M
default_character_set               = utf8mb4

[mysql]
no_auto_rehash
default_character_set               = utf8mb4

[myisamchk]

[mysqlhotcopy]
interactive_timeout

[client]
default-character-set=utf8mb4

コンフィグファイル編集

sudo vim /etc/my.cnf

MySQL 再起動

sudo /bin/systemctl restart mysqld.service
systemctl list-units | grep mysqld.service

変更パラメータ確認

show variables like "chara%";
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8mb4                    |
| character_set_connection | utf8mb4                    |
| character_set_database   | utf8mb4                    |
| character_set_filesystem | binary                     |
| character_set_results    | utf8mb4                    |
| character_set_server     | utf8mb4                    |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.01 sec)

show variables like "optimizer_switch";
+------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Variable_name    | Value                                                                                                                                                                                                                                                                                                                                                                                                            |
+------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| optimizer_switch | index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=off,block_nested_loop=on,batched_key_access=on,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on |
+------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

show variables like "innodb_buffer_pool_size";
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| innodb_buffer_pool_size | 805306368|
+-------------------------+-----------+
1 row in set (0.00 sec)

運用ユーザー作成(opeとmanager)

今後、色々データ入れたり、誰かに見せるための運用向けユーザーとして参照用(ope)とリリース用(manager)の二つのユーザーを作成しました。

(この辺は特にグループレプリケーションに関係ないので飛ばしてもらっても構わないし、権限やユーザー名は変えてもらってもいいです)

-- 作業前確認
SELECT host, user FROM mysql.user ;
SET GLOBAL validate_password_length=8;
SET GLOBAL validate_password_policy=LOW;

-- managerユーザー作成
CREATE USER 'manager'@'%' IDENTIFIED BY "xxxxxxx" ;
GRANT ALL ON *.* TO 'manager'@'%';
REVOKE GRANT OPTION ON *.* FROM 'manager'@'%';
REVOKE REFERENCES ON *.* FROM 'manager'@'%';
REVOKE EVENT ON *.* FROM 'manager'@'%';
REVOKE TRIGGER ON *.* FROM 'manager'@'%';
REVOKE CREATE ROUTINE ON *.* FROM 'manager'@'%';
REVOKE ALTER ROUTINE ON *.* FROM 'manager'@'%';
REVOKE EXECUTE ON *.* FROM 'manager'@'%';
REVOKE FILE ON *.* FROM 'manager'@'%';
REVOKE CREATE TABLESPACE ON *.* FROM 'manager'@'%';
REVOKE CREATE USER ON *.* FROM 'manager'@'%';
REVOKE REPLICATION SLAVE ON *.* FROM 'manager'@'%';
REVOKE SHUTDOWN ON *.* FROM 'manager'@'%';
REVOKE SUPER ON *.* FROM 'manager'@'%';

-- opeユーザー作成
CREATE USER 'ope'@'%' IDENTIFIED BY "xxxxxxx" ;
GRANT SELECT ON *.* TO 'ope'@'%';
GRANT SHOW VIEW ON *.* TO 'ope'@'%';
GRANT PROCESS ON *.* TO 'ope'@'%';
GRANT REPLICATION CLIENT ON *.* TO 'ope'@'%';
GRANT SHOW DATABASES ON *.* TO 'ope'@'%';
FLUSH PRIVILEGES ;

-- 作業後確認
SELECT host, user FROM mysql.user ;
SET GLOBAL validate_password_length=8;
SET GLOBAL validate_password_policy=LOW;

上記については3台それぞれで行ってください。(Group Replication設定完了後なら1台でもOK)


MySQL Shellのインストール

sudo yum install -y mysql-shell

構築時点(2019/03)では8(8.0.15)が入ったが設定上、特に問題は無かった

MySQL Clientのインストール

sudo yum install -y mysql-community-client

クライアントは5.7.25が入った。

Group Replication のセットアップ

リモート接続可能なrootユーザー作成

create user 'root'@'%' identified by 'xxxxxxxxx!';
grant all on \*.\* to 'root'@'%' with grant option;

-- localhostのパスワードも変更するなら
ALTER USER root@localhost IDENTIFIED BY 'xxxxxxxxx!';

my.cnfの設定追加

事前にIPアドレスは確認しとく

MySQLサーバ

MySQLサーバの構成は次のとおりとする。

対象 server_id IPaddress(Private) 設定するPort AZ
Primary 1 10.0.0.1 33061 ap-northeast-1a
Secondary1 2 10.0.1.1 33062 ap-northeast-1c
Secondary2 3 10.0.2.1 33063 ap-northeast-1d

my.cnf(Node1、Node2と3は前述の表に沿って差し替える)

# For Replication
server_id=1
# server_id=2 #Node2
# server_id=3 #Node3
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW
relay-log=relaylog

# For Group Replication
transaction_write_set_extraction=XXHASH64
loose-group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
loose-group_replication_start_on_boot=off
loose-group_replication_local_address="10.0.0.1:33061" ### Node1
#loose-group_replication_local_address="10.0.1.1:33062" ### Node2
#loose-group_replication_local_address="10.0.2.1:33063" ### Node3
loose-group_replication_group_seeds="10.0.0.1:33061,10.0.1.1:33062,10.0.2.1:33063"
loose-group_replication_bootstrap_group=off

MySQL再起動

sudo systemctl restart mysqld
systemctl list-units | grep mysqld.service

Group Replication Pluginの有効化、ユーザーーの作成

レプリケーション用ユーザーを作り、REPLICATION SLAVE権限をつける

mysql -uroot -p
INSTALL PLUGIN group_replication SONAME 'group_replication.so';
SET SQL_LOG_BIN=0;
UNINSTALL PLUGIN validate_password;
CREATE USER <任意のユーザー名>@'%';
GRANT REPLICATION SLAVE ON \*.\* TO <任意のユーザー名>@'%' IDENTIFIED BY '<任意のパスワード>';
SET SQL_LOG_BIN=1;
CHANGE MASTER TO MASTER_USER='<任意のユーザー名>', MASTER_PASSWORD='<任意のパスワード>' FOR CHANNEL 'group_replication_recovery';

1行目のINSTALL PLUGIN group_replication~でgroup_replicationパラメータに値が入り、グループレプリケーションが使える準備ができる。

クラスタ構築

1. グループレプリケーションの設定チェック

  1. mysqlsh
  2. dba.checkInstanceConfiguration('cluster用ユーザー@PrimaryDBのIPアドレス')

直すべき項目を現在値と必要な値を比較して表示してくれる。

実行例

mysqlsh
dba.checkInstanceConfiguration('root@10.0.0.1')

パスワードの入力を求められるので入力

Please provide the password for 'root@10.0.0.1': **********

入力したパスワードを保存するか聞かれるがそれは各人にお任せ

Save password for 'root@10.0.0.1'? [Y]es/[N]o/Ne[v]er (default No): Y

2. 設定追加

以下をMySQL Shell経由で各DBサーバに対して行う 1. dba.configureInstance('cluster用ユーザー@DBサーバのIPアドレス') 2. Do you want to perform the required configuration changes? [y/n]: と聞かれるので、 y と入力 3. Do you want to restart the instance after configuring it? [y/n]: と聞かれるので、 y と入力

dba.checkInstanceConfiguration('root@10.0.0.1')
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

dba.checkInstanceConfiguration('root@10.0.1.1')
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

dba.checkInstanceConfiguration('root@10.0.2.1')
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

最初のcheckInstanceConfigurationで次のように設定の問題箇所が表示される

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             | <unique ID>    | Update read-only variable and restart the server |
+--------------------------+---------------+----------------+--------------------------------------------------+

ただし、ここであわててmy.cnfいじって再起動する必要はない。 直後にDo you want to perform the required configuration changes?と聞かれているのでyとすることで書き換えてくれるし、 そのさらに後でDo you want to restart the instance after configuring it?と聞いてくるのでyとすることで再起動までしてくれている。

いやあ、楽だわー。

これで準備完了。

3. クラスタ構築

  1. \c cluster用ユーザー@PrimaryDBサーバのIP
  2. cluster = dba.createCluster('クラスタ名', {ipWhitelist:'DBサーバのプライベートサブネットワーク,ローカルホスト'});
  3. Do you want to disable super_read_only and continue? [y/N]: と聞かれるので、 y と入力
  4. cluster.addInstance('cluster用ユーザー@Secondary1DBサーバのIP', {ipWhitelist:'DBサーバのプライベートサブネットワーク,ローカルホスト'});
  5. cluster.addInstance('cluster用ユーザー@Secondary2DBサーバのIP', {ipWhitelist:'DBサーバのプライベートサブネットワーク,ローカルホスト'});

確認

MySQL Shellから

mysqlsh
\c root@10.0.0.1
cluster = dba.getCluster("<作成時のクラスタ名>")
cluster.status()

こんな風に表示されればOK(クラスタ名をmycluster57とした場合)

{
    "clusterName": "mycluster57",
    "defaultReplicaSet": {
        "name": "default",
        "primary": "10.0.1.1:3306",
        "ssl": "REQUIRED",
        "status": "OK",
        "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
        "topology": {
            "10.0.1.1:3306": {
                "address": "10.0.1.1:3306",
                "mode": "R/O",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            },
            "10.0.0.1:3306": {
                "address": "10.0.0.1:3306",
                "mode": "R/W",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            },
            "10.0.2.1:3306": {
                "address": "10.0.2.1:3306",
                "mode": "R/O",
                "readReplicas": {},
                "role": "HA",
                "status": "ONLINE"
            }
        },
        "topologyMode": "Single-Primary"
    },
    "groupInformationSourceMember": "ip-10-0-0-1.ap-northeast-1.compute.internal:3306"
}
cluster.describe();
{
    "clusterName": "mycluster57",
    "defaultReplicaSet": {
        "name": "default",
        "topology": [
            {
                "address": "10.0.0.1:3306",
                "label": "10.0.0.1:3306",
                "role": "HA"
            },
            {
                "address": "10.0.2.1:3306",
                "label": "10.0.2.1:3306",
                "role": "HA"
            },
            {
                "address": "10.0.1.1:3306",
                "label": "10.0.1.1:3306",
                "role": "HA"
            }
        ],
        "topologyMode": "Single-Primary"
    }
}

MySQL Clientから

mysql -uroot -p
SELECT * FROM performance_schema.replication_group_members;

3台がONLINEであることを確認

+---------------------------+--------------------------------------+-----------------------------------------------+-------------+--------------+-------------+----------------+
| CHANNEL_NAME              | MEMBER_ID                            | MEMBER_HOST                                   | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |
+---------------------------+--------------------------------------+-----------------------------------------------+-------------+--------------+-------------+----------------+
| group_replication_applier | acd307e1-2ea4-11e9-9ed2-0accfe5d2d32 | ip-10-0-0-1.ap-northeast-1.compute.internal  |        3306 | ONLINE       | PRIMARY     | 5.7.25         |
| group_replication_applier | e5f221eb-2ea1-11e9-bbad-06ebf30711b8 | ip-10-0-1-1.ap-northeast-1.compute.internal  |        3306 | ONLINE       | SECONDARY   | 5.7.25         |
| group_replication_applier | e6bd8f9d-2ea1-11e9-98fb-0e2703b1e534 | ip-10-0-2-1.ap-northeast-1.compute.internal  |        3306 | ONLINE       | SECONDARY   | 5.7.25         |
+---------------------------+--------------------------------------+-----------------------------------------------+-------------+--------------+-------------+----------------+
3 rows in set (0.00 sec)

参考

https://qiita.com/himatani/items/e1e650992dbc12b8a9b3 https://www.s-style.co.jp/blog/2018/12/2962/ https://mysqlserverteam.com/innodb-cluster-in-opc/ https://qiita.com/huato/items/52871aeeb88d783ff5e1 https://dev.mysql.com/doc/refman/5.7/en/mysql-innodb-cluster-working-with-cluster.html#create-whitelist-servers https://dev.mysql.com/doc/refman/8.0/en/mysql-innodb-cluster-working-with-cluster.html#create-whitelist-servers