GCEとMySQLで実現する高可用性システム(HA)

Taiga Murakami
google-cloud-jp
Published in
21 min readNov 23, 2018

TL;DR

マルチゾーンのGCE上にInnoDB Cluster(MySQL Group Replication) + MySQL Routerを構成することで、高可用性システムが簡単に実現できます

はじめに

本記事で取り扱う内容は執筆時点(2018年11月21日時点)の情報となります。特にSLAの内容は変更されている可能性がありますので、必ずオリジナルのSLAをご確認ください。

クラウドにおける稼働率の考え方

クラウドでシステムを構築する際に重要となる一つのポイントがサービス毎に設定されているアップタイム(稼働率)のSLAとなります。RDBのマネージドサービスであるCloudSQLのアップタイムのSLAを確認してみると、99.95%である事がわかります(ダウンタイムの定義についてはSLAサイトをご確認ください)。

DBを構築する際、Cloud SQLで保証されている稼働率では不足している場合は以下3パターンで対応することになります。今回はパターン1についてご紹介します。

  1. GCE ( 稼働率 99.99% ) 上にDBを構築する
  2. 稼働率 99.99% 以上 のマネージドDBサービスを利用する(Spanner)
  3. アプリケーションの工夫により、DBがダウンしているときも処理を継続できるようにする(ローカルキャッシュの活用、非同期処理など)

※ SLAで定義されるアップタイムやダウンタイムの定義は各サービス毎に異なりますので、詳細は各SLAのページをご参照ください。

GCEのメリット

Google Compute Engine(GCE)とはGCPで提供されているIaaSサービスであり、主な特徴は以下の通りです。

  1. 月間の利用時間に応じて自動的に割引が適用される ( 継続利用割引 )
  2. CPUとメモリ量を自由に設定可能 ( カスタムマシンタイプ )
  3. ライブマイグレーションにより、GCP都合の計画停止が無い

GCEを利用してDBの稼働率99.99%を実現する

GCEの稼働率に関するSLAを確認すると稼働率 99.99%が適用されるには、1 リージョン内の複数ゾーンにインスタンスを配置する必要がある事がわかります。そのため、GCEを使って高可用性のDBを構築する場合はマルチゾーン構成が必須となります。

(抜粋) the Covered Service will provide a Monthly Uptime Percentage to Customer of at least 99.99%

(抜粋) For Instances: Loss of external connectivity or persistent disk access for all running Instances, when Instances are placed across two or more Zones in the same Region.

MySQL on GCEでマルチゾーン構成を構成する

Webサービス等のバックエンドDBとして利用される事の多いMySQLを利用して、マルチゾーン構成をするやり方をご紹介します。

マルチゾーン構成にする際、重要な事のひとつにマスターのフェイルオーバーがあります。今までの技術ではリードレプリカを増やす事は簡単でしたが、マスターを増やすこと・冗長化する事は困難でした。例えばマスターを冗長化する場合は、マスターをActive-Stanbyの構成にして障害が発生した時に切り替えを行うというやり方が一般的でした。ただし今までのやり方は、MySQL以外の プロダクト(Heartbeat, pacemaker, MHA等)を組み合わせる方法が一般的でした。しかしMySQL5.7よりGroup Replication、InnoDB Cluster、MySQL RouterによりMySQLのエコシステムのみを利用してマスターの冗長化が簡単に実現できるようになりました。それでは実際のアーキテクチャや設定をご紹介します。

設定1 : GCEインスタンスを作成する。

Single-Primary構成で高可用性システムを構築していきます。DBは3台構成で1台目がPrimary、2台目がSecondary(Primaryがエラー時にPrimaryに昇格)、3台目がSecondaryという構成になります。まず、CentOS 7のGCEインスタンスを4台(DB3つ、クライアント1つ)起動します。

今回は検証目的ですので、細かい設定は気にせずに行きましょう。本番環境での運用を行う場合は、データを永続化ディスク(Persistent Disk)をアタッチし、MySQLのデータを永続化ディスク上に保存するなどの考慮が必要になります。

設定2: サーバーのセットアップを行う。

次に各DBサーバにログインして、基本的な設定を行います。最後のコマンドで取得できるMySQLログイン用の一時的なパスワードはこの後必要になりますのでメモしておきます。

$ sudo su -
# setenforce 0
# vi /etc/selinux/config # optional. disable selinux permanenlty
# yum -y localinstall http://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm
# yum -y install mysql-community-server mysql-shell
# systemctl start mysqld
# systemctl enable mysqld
# grep “temporary password” /var/log/mysqld.log
2018-11-16T02:17:35.828690Z 5 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: y:Iamq%C-57M
# mysql_secure_installation #optional. if you want to secure install

記事執筆時に使用したMySQLのパッケージはこちらになります

[root@db01 ~]# rpm -qi mysql-community-server
Name : mysql-community-server
Version : 8.0.13
Release : 1.el7
Architecture: x86_64
Install Date: Fri 16 Nov 2018 05:18:41 AM UTC
Group : Applications/Databases
Size : 1787656018
License : Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. Under GPLv2 license as shown in the Description field.
Signature : DSA/SHA1, Mon 08 Oct 2018 09:57:12 AM UTC, Key ID 8c718d3b5072e1f5
Source RPM : mysql-community-8.0.13-1.el7.src.rpm
Build Date : Sun 07 Oct 2018 09:26:43 AM UTC
Build Host : siv28.no.oracle.com
Relocations : (not relocatable)
Packager : MySQL Release Engineering <mysql-build@oss.oracle.com>
Vendor : Oracle and/or its affiliates
URL : http://www.mysql.com/
Summary : A very fast and reliable SQL database server

設定3: MySQL(Group Replication)の設定を行う。

設定2で基本的な設定を行いましたので、次にMySQLの設定を行っていきます。各データベースサーバにログインし、設定ファイル(/etc/my.cnf)を開き、以下の設定を追加します。@TODOと書いてある部分はご自身の環境に合わせて設定してください。

server_id=1 # @TODO 各サーバー毎に異なる値を設定する(1,2,3など)
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_checksum=NONE

log_bin=binlog
log_slave_updates=ON
binlog_format=ROW
master_info_repository=TABLE
relay_log_info_repository=TABLE

transaction_write_set_extraction=XXHASH64

plugin_load=group_replication.so

group_replication_group_name="72F1B8D3-F416-466C-BD07-B3935BCBE76A" # @TODO uuidgen コマンドなどでUUIDを生成して設定。全サーバで同じ値を設定

group_replication_start_on_boot=off # @TODO MySQL起動後、自動的にGroup Replicationを開始したい場合はOnに設定

group_replication_local_address= "10.146.0.2:6606" # @TODO 各サーバーで自身の内部IPを設定する。ポート番号は任意
group_replication_group_seeds= "10.146.0.2:6606,10.146.0.3:6606,10.146.0.4:6606" # @TODO 各サーバーで設定したgroup_replication_local_addressの値を列挙
group_replication_ip_whitelist="10.0.0.0/8" # @TODO 作成したDBからの接続を許可するようにwhite listを設定
group_replication_member_weight = 50 # @TODO Primaryを選択する際のPrioriy。各サーバーで設定する。今回はdb01は60, db02は50, db03は40とする。

group_replication_bootstrap_group=off

group_replication_recovery_get_public_key=ON

次に各DBサーバーで以下のSQLを実行し、初期設定を行います。行っていることはgroup replicationプラグインのインストール、replication用のユーザー作成、DBの管理者ユーザー(dbadmin)、マスターサーバーへのコネクション設定です。

# mysql -u root -py:Iamq%C-57M # パスワードがy:Iamq%C-57Mの場合
mysql> INSTALL PLUGIN group_replication SONAME 'group_replication.so';
mysql> CREATE USER replicator@'%' IDENTIFIED BY 'Replicator123@';
mysql> GRANT REPLICATION SLAVE ON *.* TO replicator@'%';
mysql> CHANGE MASTER TO MASTER_USER='replicator', MASTER_PASSWORD='Replicator123@' FOR CHANNEL 'group_replication_recovery';
mysql> CREATE USER dbadmin@'%' IDENTIFIED BY 'Dbadmin123@';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'dbadmin'@'%' WITH GRANT OPTION;
mysql> FLUSH PRIVILEGES;
mysql> RESET MASTER;

次にdb01(優先度を最も高く設定しているprimary)で以下のコマンドを実行し、Group replicationを開始します。

mysql> SET GLOBAL group_replication_bootstrap_group=ON;
mysql> START GROUP_REPLICATION;
mysql> SET GLOBAL group_replication_bootstrap_group=OFF;

次にdb02, db03で以下コマンドを実行し、こちらのサーバーでもGroup replicationを開始します。

mysql> START GROUP_REPLICATION;

設定が終わったことを以下コマンドで確認できます。ここまで設定ができると、db01(primary)に入った更新情報がdb02, db03でも読み込みができるようになります。またdb01が落ちた場合はdb02がPrimaryに昇格することが確認できます。

mysql> SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE, MEMBER_ROLE FROM performance_schema.replication_group_members;+-------------+-------------+--------------+-------------+
| MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE |
+-------------+-------------+--------------+-------------+
| db03 | 3306 | ONLINE | SECONDARY |
| db01 | 3306 | ONLINE | PRIMARY |
| db02 | 3306 | ONLINE | SECONDARY |
+-------------+-------------+--------------+-------------+
3 rows in set (0.03 sec)

設定4: InnoDB Clusterの設定を行う

mysqlsh経由でクラスターの作成を行います。この作業はdb01上でのみ実施します。実際に実行するコマンドは以下の太字部分となります。パスワードの入力が求めらた場合はDB管理者ユーザー(dbadmin)のパスワード(Dbadmin123@)を入力します。

# mysqlsh
MySQL JS > \connect dbadmin@localhost
MySQL localhost:33060+ ssl JS > var cluster = dba.createCluster('prodCluster', {adoptFromGR: true});
A new InnoDB cluster will be created based on the existing replication group on instance 'dbadmin@localhost:3306'.

Creating InnoDB cluster 'prodCluster' on 'dbadmin@localhost:3306'...
Adding Seed Instance...
Adding Instance 'db03:3306'...
Adding Instance 'db02:3306'...

Cluster successfully created based on existing replication group.

MySQL localhost:33060+ ssl JS > cluster.status()
{
"clusterName": "prodCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "db01:3306",
"ssl": "DISABLED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"db01:3306": {
"address": "db01:3306",
"mode": "R/W",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"db02:3306": {
"address": "db02:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"db03:3306": {
"address": "db03:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
}
}
},
"groupInformationSourceMember": "mysql://dbadmin@localhost:3306"
}

設定5: クライアントの設定

最後にクライアントの設定を行います。クライアントの設定において重要な部分はMySQL Routerの設定になります。まず以下のコマンド実行し、初期設定を行います。

$ sudo su -
# setenforce 0
# vi /etc/selinux/config # optional. disable selinux permanenlty
# yum -y localinstall http://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm
# yum -y install mysql mysql-router

つぎにMySQL Routerのセットアップを行います。セットアップは以下太字部分のコマンド実行で完了します。パスワードの入力が求められますので、DB管理者ユーザー(dbadmin)のパスワード(Dbadmin123@)を入力します。

# mysqlrouter --force --user mysqlrouter --bootstrap dbadmin@db01:3306
Please enter MySQL password for dbadmin: <パスワードを入力>

Reconfiguring system MySQL Router instance...
Checking for old Router accounts
Found old Router accounts, removing
Creating account mysql_router1_zgsmyn3hdlaq@'%'
MySQL Router has now been configured for the InnoDB cluster 'prodCluster'.

The following connection information can be used to connect to the cluster after MySQL Router has been started with generated configuration..

Classic MySQL protocol connections to cluster 'prodCluster':
- Read/Write Connections: localhost:6446
- Read/Only Connections: localhost:6447
X protocol connections to cluster 'prodCluster':
- Read/Write Connections: localhost:64460
- Read/Only Connections: localhost:64470

出力結果にある、Read/Write Connectionsが書き込み(write)に利用するための接続先、Read/Only Connectionsが読み込み(read)をする際に利用する接続先となります。コマンドが完了したら最後にMySQL Routerを起動します。

# systemctl start mysqlrouter

動作確認

セットアップが完了しましたのでフェイルオーバーが動作するか確かめてみんましょう。クライアント上からこちらのコマンドを実行するとどのデータベースサーバにログインしているか確認できます。一度確認が終わった後、PrimaryとなっているDBサーバーのmysqldプロセスを停止すると、Primaryが他のDBサーバーに移り再び同じコマンドを実行した時、別のサーバーにアクセスする事が確認できます。

# mysql -u dbadmin -pDbadmin123@ -h 127.0.0.1 -P 6446 -e "select @@hostname;" 2>&1 | grep db
db01
# mysql -u dbadmin -pDbadmin123@ -h 127.0.0.1 -P 6446 -e "select @@hostname;" 2>&1 | grep db
db02

またこちらのようなスクリプトを用意し、スクリプトを実行中にDBサーバーのmysqldプロセスを落とすとPrimaryが移っていく事をリアルタイムに確認できます。スクリプトの中のポート番号(6446)を6447に変更すると読み込み専用の接続先に繋がります。また一度落としたプロセスを再度起動すると、今度はSecondaryとして読み込みのリクエストが流れ始めることも確認できます。

#!/bin/bash

while true; do
NOW=`date --iso-8601=seconds`
DB=`mysql -u dbadmin -pDbadmin123@ -h 127.0.0.1 -P 6446 -e "select @@hostname;" 2>&1 | grep db`
echo $NOW $DB
sleep 1
done

出力例:書き込み時のフェイルオーバー

2018-11-22T06:31:52+0000 db01
2018-11-22T06:31:53+0000 db01
2018-11-22T06:31:54+0000 db01
2018-11-22T06:31:55+0000 db02 <= このタイミングでPrimaryが切り替わった
2018-11-22T06:32:01+0000 db02
2018-11-22T06:32:02+0000 db02
2018-11-22T06:32:03+0000 db02

出力例 : 読み込み時のフェイルオーバー

2018-11-22T08:33:41+0000 db03
2018-11-22T08:33:43+0000 db03
2018-11-22T08:33:44+0000 db03
2018-11-22T08:33:45+0000 db01 <= ここでdb01復旧した
2018-11-22T08:33:46+0000 db03
2018-11-22T08:33:47+0000 db01

まとめ

長文となってしましたが、GCEとMySQL(Group Replication, InnoDB Cluster, MySQL Router)を使って高可用性のシステムを実現する方法をご紹介しました。

このアーキテクチャは主にオンプレミスにある高可用性システムをまずクラウドに移行する(Lift&Shiftで言うLift)をする際に役に立つやり方です。GCEではリソース(CPU, Memory)のカスタマイズができますので、移行時にリソース割り当ての見直しをする事もオススメです。この構成にしますと、サーバーを1台づつ停止してメンテナンスができますので、ユーザーが増えてリソースが枯渇した時、オンデマンドでリソースの割当量を変更する事も可能です。

また本番環境でご利用いただく際はGCEのストレージとしてどれを利用するかと良いかをこちらの情報でご確認いただき、最適なタイプ・サイズのストレージをご選択ください。

みなさんもぜひ一度今回紹介した内容を実践し、イメージを掴んでもらえると嬉しいです。

--

--

Taiga Murakami
google-cloud-jp

Google Cloud Customer Engineer. All views and opinions are my own(すべての見解や意見は私見です).