APIを用いてAlibaba Cloudリソースを自動構築する⑤ - RDS編

はじめに

こんにちは、エンジニアの佐藤です。
APIを用いてAlibaba Cloudリソースを自動構築するシリーズ第5回目となります。

前回の記事では、Server Load Balancerを作成し、RDS経由でECSサーバにログインしました。
今回はRDBMSサービスであるApsara DB for RDSを作成し、ECSインスタンスからクエリを発行してみたいと思います。

RDS要件

簡単な要件は以下の通りです。

⦿ DBエンジンはMySQLで構築(Apsara DB for MySQL)
⦿ リードレプリカ(読み取り専用インスタンス)を構築
⦿ マスターインスタンス/リードレプリカともにマルチゾーン(A Zone/B Zone)構成

f:id:sbc_nttd_satouruzg:20190617155557p:plain
RDS構成図

赤字の部分が今回の構築対象となります。

リードレプリカとは?

読み取り専用のDBインスタンスのことを、リードレプリカ と呼びます。
マスターインスタンスが書き込み/読み取りの双方のリクエストを受け付けるのに対し、リードレプリカは読み取りリクエストのみを受け付けます。

f:id:sbc_nttd_satouruzg:20190617155639p:plain
リードレプリカアーキテクチャ

リードレプリカを用いるメリットとして、データベースのパフォーマンス向上が挙げられます。
更新系クエリはマスターインスタンスに、参照系クエリはリードレプリカに分散させることで、マスターインスタンスの負荷を軽減することが出来ます。
例えば、大量のデータをDBに格納するアプリケーションがあり、その裏で分析部隊がガリガリデータ解析する、というようなシステムにおいて高い効果が発揮されます。 1

さらに、マスターインスタンスに比べ、料金が半額近く安くなることもポイントのひとつです。 2
読み取りリクエストの比率が高いようなシステムにおいては、リードレプリカを採用することでTCOがグッと下がる可能性があります!

ただし、リードレプリカには僅かではありますがレプリカラグが生じるため、
決済システムなどリアルタイム性が求められる参照ワークロードが存在する場合は、リードレプリカではなくマスターインスタンスを参照するよう注意してください。

RDSを作成してみよう!

手順

RDSを作成し、ECSインスタンスからクエリを発行するまでのおおまかな手順は以下となります。

  1. DBインスタンス(マスターインスタンス)を作成する
  2. 手順1で作成したマスターインスタンスにユーザアカウントを作成する
  3. 手順1で作成したマスターインスタンスにデータベース(スキーマ)を作成する
  4. 手順1で作成したマスターインスタンスに紐付くリードレプリカインスンタンスを作成する

APIリファレンス

本記事で使用するAPIを紹介します。
すべてRDS SDKのAPIです。

  1. CreateDBInstance:DBインスタンスを作成します。
  2. CreateReadOnlyDBInstance:リードレプリカを作成します。
  3. CreateDatabase:データベースを作成します。
  4. CreateAccount:ユーザアカウントを作成します。

RDS SDKをインポート

前準備として、RDS用のJava SDKをインポートしておきます。

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-rds</artifactId>
    <version>2.3.7</version>
</dependency>

DBインスタンスを作成!

ではさっそく、APIでリソースを構築していきましょう。
まずはDBインスタンスを作成します。

サンプルコード

package com.alibaba.demo.apisample;

import java.io.IOException;

import com.alibaba.demo.apisample.enums.zones.TokyoZones;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.rds.model.v20140815.CreateDBInstanceRequest;
import com.aliyuncs.rds.model.v20140815.CreateDBInstanceResponse;

public class ApiSample {

    private static final String REGION_ID = "ap-northeast-1";
    private static final String ACCESS_KEY = "xxxxxxxxxxxxxxxx";
    private static final String SECRET_ACCESS_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

    public static void main(String[] args) throws ServerException, ClientException, IOException {

        IAcsClient client = new DefaultAcsClient(
            DefaultProfile.getProfile(REGION_ID, ACCESS_KEY, SECRET_ACCESS_KEY));
/** ---------------------- ↑ここまで前回のサンプルと同様 ---------------------- */

        CreateDBInstanceRequest request = new CreateDBInstanceRequest();
        request.setDBInstanceDescription("rds-api-demo-master");  // DBインスタンス名
        request.setEngine("MySQL");                               // DBエンジン
        request.setEngineVersion("5.6");                          // エンジンバージョン
        request.setDBInstanceClass("rds.mysql.t1.small");         // DBインスタンスクラス
        request.setDBInstanceStorage(Integer.valueOf(10));        // ストレージサイズ
        request.setPayType("Postpaid");                           // 支払い方法
        request.setSecurityIPList("10.0.1.0/24, 10.0.9.0/24");    // ホワイトリスト
        request.setZoneId(TokyoZones.ZONE_MULTI_ID.getZoneId());  // ゾーンID
        request.setDBInstanceNetType("Intranet");                 // 接続タイプ
        request.setInstanceNetworkType("VPC");                    // ネットワークタイプ
        request.setVPCId("vpc-6wekbo76l1h7j66b26wxx");            // VPC ID
        request.setVSwitchId("vsw-6webaau3xpukx5jzgwk7n");        // VSwitch ID

        // DBインスタンス作成
        CreateDBInstanceResponse response = client.getAcsResponse(request);
        System.out.println("InstanceId:" + response.getDBInstanceId());
        System.out.println("Endpoint:" + response.getConnectionString());
    }
}
DBインスタンス名

DBインスタンスの名前を指定します。
必須パラメータではありませんが、指定しない場合はリソースIDがそのまま設定されるため、管理上今回は指定しています。

DBエンジン

DBインスタンスのDBエンジンを指定します。必須パラメータ です。
指定可能な値は、以下の4つです。

  1. MySQL
  2. SQLServer
  3. PostgreSQL
  4. PPAS
エンジンバージョン

DBエンジンのバージョンを指定します。必須パラメータ です。
指定可能な値はDBエンジンによって異なり、MySQLの場合は5.5/5.6/5.7の3つバージョンから選択可能です。

DBインスタンスクラス

DBインスタンスのインスタンスタイプを指定します。必須パラメータ です。
指定可能なインスタンスクラスは、DBエンジンやバージョンに依存します。詳細はこちらを確認してください。
今回はMySQL5.6における最小スペックであるrds.mysql.t1.smallを指定しています。

ストレージサイズ

ストレージのサイズを指定します(GB)。必須パラメータ です。
指定可能な値は、DBエンジン/バージョンやDBインスタンスクラスに依存します。詳細はDBインスタンスクラスと同様インスタンスタイプリストをご覧ください。
MySQL5.6 / rds.mysql.t1.smallの場合、5GBから2000GBの間で指定出来ます。

支払い方法

DBインスタンスの料金支払い方法を指定します。必須パラメータ です。
Postpaid(従量課金)またはPrepaid(サブスクリプション課金)のどちらかを指定します。

ホワイトリスト

DBインスタンスに接続可能なIPアドレスをCIDR形式で指定します。必須パラメータ です。
デフォルトでは、127.0.0.1のみ設定されており、同一VPC内のECSインスタンスからでも接続することは出来ません。
今回はA/Bゾーン双方のECSインスタンスから接続を行うため、各ゾーンのECSが属するVSwitchのCIDR IPを指定しています。
複数の値を指定する場合は、 ,(カンマ)で区切って指定します。

ゾーンID

DBインスタンスをデプロイするゾーンを指定します。
非必須パラメータで、指定しない場合はリージョンやDBエンジンに応じたデフォルトのゾーンに作成されます。
指定可能なゾーンのIDは、DescribeRegionsから取得出来ます。

今回はマルチゾーンで作成するため、
下記のようにTokyoZonesに、東京リージョンのマルチゾーン用のゾーンIDap-northeast-1MAZ1(a,b)を追加しています。

ZONE_MULTI_ID("ap-northeast-1MAZ1(a,b)");
接続タイプ

DBインスタンスへの接続方法を指定します。必須パラメータ です。
指定可能な値は、InternetまたはIntranetです。

  • ⦿ Internet:インターネットからアクセス可能なインスタンスを作成する場合に指定します。付与されるエンドポイントはパブリックIPアドレスに解決されます。
  • ⦿ Intranet:VPC内部からのみアクセス可能なインスタンスを作成する場合に指定します。付与されるエンドポイントはプライベートIPアドレスに解決されます
ネットワークタイプ

DBインスタンスをデプロイするネットワークのタイプを指定します。
指定可能な値は、Classic(クラシックネットワーク)またはVPC(VPC)のどちらかです。
非必須パラメータで、指定しない場合はClassicが設定されます。
今回はVPC内部に作成するため、VPCを指定しています。

VPC ID

DBインスタンスをデプロイするVPCのリソースIDを指定します。
ネットワークタイプがVPCの場合に必須となる条件付き必須パラメータです。

VSwitch ID

DBインスタンスをデプロイするVSwitchのリソースIDを指定します。
ネットワークタイプがVPCの場合に必須となる条件付き必須パラメータです。

実行結果

下記のように、Console上に作成したDBインスタンスのリソースID、およびエンドポイントが表示されれば成功です。
これらは今後の手順で使用するので、控えておいてください。

Instance ID:rm-e9b29d1u37boz6198
Endpoint:rm-e9b29d1u37boz6198.mysql.japan.rds.aliyuncs.com

管理コンソール上から作成したDBインスタンスを確認してみましょう。

ApsaraDB for RDS > インスタンスリストより、DBインスタンスの一覧を表示します。

f:id:sbc_nttd_satouruzg:20190617153538p:plain
DBインスタンスコンソール画面

f:id:sbc_nttd_satouruzg:20190617153611p:plain
DBインスタンスコンソール詳細画面

無事にDBインスタンスが作成されていることが確認出来ました。

ユーザ作成!!

次に、先程作成したDBインスタンス内にユーザアカウントを作成します。

AWSのRDSとは異なり、インスタンス作成時にユーザが作成されないため、
データベースを操作するためにはインスタンス作成後に別途ユーザアカウントを作成する必要があります。

サンプルコード

package com.alibaba.demo.apisample;

import java.io.IOException;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.rds.model.v20140815.CreateAccountRequest;

public class ApiSample {

   private static final String REGION_ID = "ap-northeast-1";
   private static final String ACCESS_KEY = "xxxxxxxxxxxxxxxx";
   private static final String SECRET_ACCESS_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

    public static void main(String[] args) throws ServerException, ClientException, IOException {

        IAcsClient client = new DefaultAcsClient(
            DefaultProfile.getProfile(REGION_ID, ACCESS_KEY, SECRET_ACCESS_KEY));
/** ---------------------- ↑ここまで前述のサンプルと同様 ---------------------- */

        String masterInstanceId = "rm-e9b29d1u37boz6198";

        CreateAccountRequest request = new CreateAccountRequest();
        request.setDBInstanceId(masterInstanceId);  // DBインスタンスID
        request.setAccountName("adminuser");        // ユーザ名
        request.setAccountPassword("********");     // パスワード
        request.setAccountType("Super");            // アカウントタイプ

        // ユーザ作成
        client.getAcsResponse(request);
    }
}
DBインスタンスID

ユーザを作成するDBインスタンスのリソースIDを指定します。必須パラメータ です。
先程作成したDBインスタンスのリソースIDを指定します。

ユーザ名

ユーザ名を指定します。必須パラメータ です。
以下の制約があります。

  • ⦿2文字以上16文字以内であること
  • ⦿英小文字、数字、" _ "(アンダースコア)のみで構成されること
  • ⦿英字で始まること
  • ⦿禁則文字列ではないこと
パスワード

パスワードを指定します。必須パラメータ です。
以下の制約があります。

  • ⦿8文字以上32文字以内であること
  • ⦿使用可能な特殊文字は!,@,#,$,&,%,^,*,(,),_,+,-,=
  • ⦿英大文字、英小文字、数字、および特殊文字のうち少なくとも3つが含まれていること
アカウントタイプ

ユーザの権限タイプを指定します。
指定可能な値は、Super(特権ユーザ)またはNormal(通常ユーザ)のいずれかです。
非必須パラメータで、指定しない場合はNormalで作成されます。

実行結果

エラーが発生しなければ、ユーザ作成成功です。
管理コンソール上から作成したユーザを確認しましょう。

先程のDBインスタンスの詳細画面より、アカウント管理を押下します。

f:id:sbc_nttd_satouruzg:20190617153640p:plain
ユーザ画面

正常にユーザが作成されています。

データベースを作成!!!

前手順までで、DBインスタンスに接続する準備は整いました。

ECSインスタンスからDBインスタンスにログインし、DDLを投入することはもちろん可能ですが、
せっかくなのでAPIを用いてデータベースを作成してみたいと思います。

サンプルコード

package com.alibaba.demo.apisample;

import java.io.IOException;

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.rds.model.v20140815.CreateDatabaseRequest;

public class ApiSample {

   private static final String REGION_ID = "ap-northeast-1";
   private static final String ACCESS_KEY = "xxxxxxxxxxxxxxxx";
   private static final String SECRET_ACCESS_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

    public static void main(String[] args) throws ServerException, ClientException, IOException {

        IAcsClient client = new DefaultAcsClient(
            DefaultProfile.getProfile(REGION_ID, ACCESS_KEY, SECRET_ACCESS_KEY));

        String masterInstanceId = "rm-e9b29d1u37boz6198";
/** ---------------------- ↑ここまで前述のサンプルと同様 ---------------------- */

        CreateDatabaseRequest request = new CreateDatabaseRequest();
        request.setDBInstanceId(masterInstanceId);  // DBインスタンスID
        request.setDBName("testdb");                // データベース名
        request.setCharacterSetName("utf8");        // 文字コード

        // データベース作成
        client.getAcsResponse(request);
    }
}
DBインスタンスID

ユーザを作成するDBインスタンスのリソースIDを指定します。必須パラメータ です。
先程作成したDBインスタンスのリソースIDを指定します。

データベース名

データベース名を指定します。必須パラメータ です。
以下の制約があります。

  • ⦿64文字以内であること
  • ⦿英小文字、数字、" _ "(アンダースコア)のみで構成されること
  • ⦿英字で始まること
  • ⦿禁則文字列ではないこと
文字コード

データベースで使用する文字コードを指定します。必須パラメータ です。
指定可能な値はDBエンジンに依存し、MySQLの場合は以下の4つから選択します。

  • utf8
  • gbk
  • latin1
  • utf8mb4

実行結果

エラーが発生しなければデータベース作成成功です。
管理コンソール上から作成したデータベースを確認しましょう。

先程のDBインスタンスの詳細画面より、データベース管理を押下します。

f:id:sbc_nttd_satouruzg:20190617154008p:plain
データベース画面

指定したパラメータ通り、データベースが作成されています。

接続確認

では、作成したデータベースに接続し、テーブルを作成してみましょう!

まずは、前回の記事を参考にECSインスタンスにログインします。

f:id:sbc_nttd_satouruzg:20190617163321p:plain
ECSインスタンスログイン
次に、以下のコマンドを実行し、DBインスタンスに接続するためのMySQLクライアントをインストールします。

# yum -y install mysql

f:id:sbc_nttd_satouruzg:20190617163431p:plain
MySQLインストール

完了しました!のメッセージが表示されれば、インストール成功です。

mysqlコマンドを用い、データベースに接続します。

# mysql -h rm-e9b29d1u37boz6198.mysql.japan.rds.aliyuncs.com -P 3306 -u adminuser -D testdb -p

※エンドポイントは先程控えたものに変更してください。

f:id:sbc_nttd_satouruzg:20190617154151p:plain
DBインスタンス接続

DBインスタンス、ユーザ、データベースが作成出来ていることが確認出来ました。
以下の通りDDLを発行し、RDSの管理テーブルを作成してみます。

CREATE TABLE db_instances
 (
   name varchar(64),
   id char(20),
   endpoint varchar(128)
 )

f:id:sbc_nttd_satouruzg:20190617163658p:plain
RDS管理DB作成

無事、テーブルが作成出来ました!!

リードレプリカ作成!!!!

最後に、先程作成したマスターインスタンスに紐付くリードレプリカを作成します。

サンプルコード

package com.alibaba.demo.apisample;

import java.io.IOException;

import com.alibaba.demo.apisample.enums.zones.TokyoZones;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.rds.model.v20140815.CreateReadOnlyDBInstanceRequest;
import com.aliyuncs.rds.model.v20140815.CreateReadOnlyDBInstanceResponse;

public class ApiSample {

   private static final String REGION_ID = "ap-northeast-1";
   private static final String ACCESS_KEY = "xxxxxxxxxxxxxxxx";
    private static final String SECRET_ACCESS_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

   public static void main(String[] args) throws ServerException, ClientException, IOException {

       IAcsClient client = new DefaultAcsClient(
           DefaultProfile.getProfile(REGION_ID, ACCESS_KEY, SECRET_ACCESS_KEY));

       String masterInstanceId = "rm-e9b29d1u37boz6198";
/** ---------------------- ↑ここまで前述のサンプルと同様 ---------------------- */

        CreateReadOnlyDBInstanceRequest request = new CreateReadOnlyDBInstanceRequest();
        request.setDBInstanceId(masterInstanceId);
        request.setDBInstanceDescription("rds-api-demo-master");
        request.setEngineVersion("5.6");
        request.setDBInstanceClass("rds.mysql.t1.small");
        request.setDBInstanceStorage(Integer.valueOf(10));
        request.setPayType("Postpaid");
        request.setZoneId(TokyoZones.ZONE_MULTI_ID.getZoneId());
        request.setInstanceNetworkType("VPC");
        request.setVPCId("vpc-6wekbo76l1h7j66b26wxx");
        request.setVSwitchId("vsw-6webaau3xpukx5jzgwk7n");

        CreateReadOnlyDBInstanceResponse response = client.getAcsResponse(request);
        System.out.println("ReadReplica ID : " + response.getDBInstanceId());
        System.out.println("ReadReplica Endpoint : " + response.getConnectionString());
    }
}

基本的にマスターインスタンスと同様のため、各パラメータの詳細は省略します。

ポイントとなるのは以下の3点です。

  • ⦿インスタンスクラスやストレージサイズ等、マスターインスタンスと異なるスペックで作成可能
  • ⦿現状、リードレプリカを作成可能なマスターインスタンスのDBエンジン/バージョンはMySQL5.6 / MySQL5.7 のみ 3
  • ⦿DBエンジンはマスターインスタンスと同様のエンジンでなければならず、バージョンはマスターインスタンスと同等かそれよりも新しいバージョンに限る

つまり、マスターインスタンス / リードレプリカ の組み合わせは、以下の3つの組み合わせに限られるということになります。

  1. M:MySQL5.6 / R:MySQL5.6
  2. M:MySQL5.6 / R:MySQL5.7
  3. M:MySQL5.7 / R:MySQL5.7

実行結果

下記のように、Console上に作成したリードレプリカのリソースID、およびエンドポイントが表示されれば成功です。

ReadReplica ID : rr-e9b6m610ix0na8dce
Endpoint:rr-e9b6m610ix0na8dce.mysql.japan.rds.aliyuncs.com

管理コンソール上から作成したリードレプリカを確認してみましょう。

ApsaraDB for RDS > インスタンスリストより、DBインスタンスの一覧を表示します。

リードレプリカには自身がリードレプリカであることを示すR アイコンが、
マスターインスタンスには先程までなかった負荷分散を示すアイコンが付与されていることがわかります。

f:id:sbc_nttd_satouruzg:20190617154518p:plain
リードレプリカ作成後コンソール画面

f:id:sbc_nttd_satouruzg:20190617154547p:plain
リードレプリカ詳細画面

無事にリードレプリカが作成されていることが確認出来ました。

接続確認

ではリードレプリカへの接続確認として、以下の2点を確認してみたいと思います。

  1. マスターインスタンスとレプリケーションしていること
  2. 読み取り専用であること

レプリケーション確認

レプリケーション確認として、マスターインスタンスへの変更がリードレプリカにも反映されていることを確認します。

先程と同様マスターインスタンスにログインし、
以下の通りクエリを発行し、RDS管理テーブルにマスターインスタンスの情報を登録します。

INSERT INTO db_instances
VALUES(
  'rds-api-demo-master',
  'rm-e9b29d1u37boz6198',
  'rm-e9b29d1u37boz6198.mysql.japan.rds.aliyuncs.com'
);
SELECT * FROM db_instances;

f:id:sbc_nttd_satouruzg:20190617154750p:plain
テーブル作成

マスターインスタンス上にレコードが登録されました。
では、リードレプリカにログインし、先程マスターインスタンスに登録したレコードがリードレプリカに同期されていることを確認してみます。

f:id:sbc_nttd_satouruzg:20190617154909p:plain
レプリケーション確認

正常にレプリケーションが行われていることが確認出来ました!

読み取り専用確認

読み取り専用であることの確認として、リードレプリカに対しINSERT文を発行してみたいと思います。

リードレプリカにログイン状態のまま、以下の通りINSERT文を発行してみましょう。

INSERT INTO db_instances
VALUES(
  'rds-api-demo-readreplica',
  'rr-e9b6m610ix0na8dce',
  'rr-e9b6m610ix0na8dce.mysql.japan.rds.aliyuncs.com'
);

f:id:sbc_nttd_satouruzg:20190617155014p:plain
読み取り専用確認

上記画像のように、
このサーバは読み取り専用なので、そのようなクエリは実行出来ません
といったような内容のエラー分が返却されたかと思います。

以上の2点を以て、
リードレプリカが本当にリードレプリカであることを確認出来ました!!

Tips

読み書き分離機能について

先程のサンプルでも示したように、
マスターインスタンスおよびリードレプリカにはそれぞれ個別のエンドポイントが付与されます。

そのため、アプリケーションがDBにアクセスする際には、
書き込み要求はマスターインスタンスのエンドポイントに、読み取り要求はリードレプリカのエンドポイントに、
といった制御をアプリケーション側で行わなければなりません。

また、複数台のリードレプリカを構築した場合、リードレプリカ間でどのように負荷分散を行うか、といった点も課題になってきます。

これらを解決するのが、読み書き分離機能(Read/Write Splitting Function)と呼ばれる機能です。
本機能は、各DBインスタンス個別のエンドポイントとは別に、読み書き分離エンドポイント と呼ばれる追加のエンドポイントを付与します。

f:id:sbc_nttd_satouruzg:20190617155038p:plain
RDSエンドポイント

上記図に示すように、
読み書き分離エンドポイントに送信されたリクエストは、クエリが読み取りなのか書き込みなのかを判断し、自動的に振り分けられます。
さらに、複数のリードレプリカに対し負荷分散も行ってくれます。

本機能を用いることで、アプリケーション側で複雑な設定をする必要なく、
単一のエンドポイントを設定するだけで読み書きの分離が可能となります。

こんな便利な読み書き分離機能ですが、実は日本リージョン未提供の機能となります。4
そこで、本記事では代替策としてProxySQLを用いて読み込み/書き込みリクエストを分離する方法を紹介したいと思います。

ProxySQLとは

その名の通り、MySQLのプロキシサーバとして、定義したルールに従いルーティングの振り分けを行うオープンソースのミドルウェアです。
今回は、既存ECSインスタンス上にProxySQLをインストールして、読み書き分離機能を具備してみたいと思います。

準備

前準備として、
複数のリードレプリカで負荷分散されていることを確認するため、リードレプリカをもう一台起動させておきます。

f:id:sbc_nttd_satouruzg:20190617155103p:plain
リードレプリカ②

また、ProxySQLが各DBインスタンスに接続するためのDBユーザを作成しておきます。

f:id:sbc_nttd_satouruzg:20190617155125p:plain
ProxySQL用ユーザ

ProxySQLインストール

ECSインスタンスにログインし、ProxySQLをインストールします。

リポジトリ追加

下記のコマンドを実行し、ProxySQLのリポジトリをダウンロードします。
(今回は、CentOS7の最新版をインストールしています。)

wget -P /tmp/proxysql/ https://github.com/sysown/proxysql/releases/download/v2.0.4/proxysql-2.0.4-1-centos7.x86_64.rpm
ProxySQLインストール

下記のコマンドを実行し、ProxySQLをインストールします。

yum -y install /tmp/proxysql/proxysql-2.0.4-1-centos7.x86_64.rpm
ProxySQL起動

下記コマンドを実行し、ProxySQLを起動します。

systemctl start proxysql

ProxySQL設定

ProxySQLの設定はすべて専用の管理コンソール上で行います。

管理コンソールログイン

下記のコマンドを実行し、ProxySQL管理コンソールにログインします。

mysql -h 127.0.0.1 -P 6032 -u admin -padmin
ホストグループ登録

各DBインスタンスが所属するホストグループを登録します。

今回は、
書き込みリクエストのルーティング先となるwriter_hostgroupのグループを0
読み込み専用のリクエストのルーティング先となるreader_hostgroupのグループを1
として登録します。

管理コンソール上で以下のクエリを発行します。

INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup) VALUES (0, 1);
サーバ登録

振り分け先となるDBインスタンスのエンドポイントを登録していきます。
マスターインスタンスであるrm-e9b29d1u37boz6198は書き込み用ホストグループに、
リードレプリカであるrr-e9b6m610ix0na8dcerr-e9bopaog80y48ykfiについては、読み込み専用ホストグループに登録します。

INSERT INTO mysql_servers (hostname, hostgroup_id, port) VALUES ('rm-e9b29d1u37boz6198.mysql.japan.rds.aliyuncs.com', 0, 3306);
INSERT INTO mysql_servers (hostname, hostgroup_id, port) VALUES ('rr-e9b6m610ix0na8dce.mysql.japan.rds.aliyuncs.com', 1, 3306);
INSERT INTO mysql_servers (hostname, hostgroup_id, port) VALUES ('rr-e9bopaog80y48ykfi.mysql.japan.rds.aliyuncs.com', 1, 3306);
設定の適用・永続化

下記のコマンドを実行し、登録した情報をランタイムに適用、さらにディスクへの永続化を行います。

LOAD MYSQL SERVERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;
クエリルール登録

リクエストを振り分ける際のクエリルールを登録します。

match_digestにルールを指定します。正規表現を使用可能です。
destination_hostgroupに振り分け先のホストグループを指定します。

今回はサンプルのため、簡単な例のみを定義していますが、 正規表現をうまく使うことで様々なパターンに適応可能なルールを定義することが可能です。

INSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply) VALUES (0, 1, "^SELECT", 1, 1);
INSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply) VALUES (1, 1, "^UPDATE", 0, 1);
INSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply) VALUES (2, 1, "^INSERT", 0, 1);
INSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply) VALUES (3, 1, "^DELETE", 0, 1);
設定の適用・永続化

先程と同様、登録したクエリルールの情報を適用、永続化します。

LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
実行ユーザ登録

以下のクエリを発行し、前準備で作成したProxySQL用のユーザを実行ユーザとして登録します。

INSERT INTO mysql_users (username, password, active, default_hostgroup, default_schema, transaction_persistent) VALUES ('proxyuser', '********', 1, 0, 'testdb', 1);
設定の適用・永続化

ユーザ情報の適用・永続化を行います。

LOAD MYSQL USERS TO RUNTIME;
SAVE MYSQL USERS TO DISK;
モニタリングユーザ登録

最後に、ProxySQLが各DBインスタンスのモニタリングに用いるユーザの情報を登録します。
今回は簡単のため、実行ユーザでモニタリングを行います。

UPDATE global_variables SET variable_value='proxyuser' WHERE variable_name='mysql-monitor_username';
UPDATE global_variables SET variable_value='********' WHERE variable_name='mysql-monitor_password';
設定の適用・永続化

モニタリングユーザ設定の適用・永続化を行います。

LOAD MYSQL VARIABLES TO RUNTIME;
SAVE MYSQL VARIABLES TO DISK;

以上でProxySQLの設定は完了となります。

動作確認

では、ProxySQLの動作確認を行っていきましょう!

まずは読み書き分離が行われていることを確認するため、
INSERT文とSELECT文を発行し、それぞれがマスターインスタンスとリードレプリカに割り振られていることを確認します。

コンソールをもうひとつ立ち上げ、以下の通りProxySQLに対しクエリを発行します。

mysql -h 127.0.0.1 -P 6033 -u proxyuser -p******** -e "INSERT INTO db_instances VALUES('rds-api-demo-readreplica', 'rr-e9b6m610ix0na8dce', 'rr-e9b6m610ix0na8dce.mysql.japan.rds.aliyuncs.com');"
mysql -h 127.0.0.1 -P 6033 -u proxyuser -p******** -e "SELECT * FROM db_instances;"

f:id:sbc_nttd_satouruzg:20190617211115p:plain
INSERT・SELECTクエリ発行

正常にクエリが完了しました。
なお、別のサーバからクエリを発行する場合は、下記のようにホスト部分にProxySQLをインストールしたサーバのIPアドレスを指定します。

mysql -h 10.0.1.168 -P 6033 -u proxyuser -p******** -e "SELECT * FROM db_instances;"

各クエリがどのように割り振られたかは、管理コンソール上から統計情報テーブルを参照することで確認出来ます。
stats_mysql_connection_poolテーブルでは、"どのサーバ""どのくらい" リクエストを処理したかを、
stats_mysql_query_digestテーブルでは、"どのクエリ""どのホストグループ""どのくらい" 割り振られたのか
を確認出来ます。

管理コンソール上で以下の通りクエリを発行し、読み書き分離が行われていることを確認します。

SELECT hostgroup, srv_host, Queries FROM stats_mysql_connection_pool;
SELECT hostgroup, digest_text, count_star FROM stats_mysql_query_digest;

f:id:sbc_nttd_satouruzg:20190617155312p:plain
結果確認①

INSERT文がマスターインスタンス(rm-e9b29d1u37boz6198)に、
SELECT文がリードレプリカ(rr-e9bopaog80y48ykfi)にそれぞれ割り振られていることが確認出来ました!

試しに同じINSERT文を10回発行してみます。

f:id:sbc_nttd_satouruzg:20190617155348p:plain
結果確認②

全てマスターインスタンスに割り振られていますね。



次に読み取りリクエストがリードレプリカ間で負荷分散されていることを確認してみましょう。

先程と同様のSELECT文を、66回ほど実行してみます。

f:id:sbc_nttd_satouruzg:20190617155427p:plain
結果確認③

リードレプリカ①(rr-e9b6m610ix0na8dce)とリードレプリカ②(rr-e9bopaog80y48ykfi)で、均等に33回ずつ割り振られていることが確認出来ました!!



最後に、DELETE文を発行してみます。

f:id:sbc_nttd_satouruzg:20190617211158p:plain
DELETEクエリ発行

f:id:sbc_nttd_satouruzg:20190617155513p:plain
結果確認④

登録したクエリルールが正常に適用されていることがわかります。

最後に

いかがだったでしょうか?

SLB等に比べて関連コンポーネントが少ないため、比較的スムーズに構築出来たのではないかと思います。
今回は省略しましたが、ログやバックアップの機能、DBパラメータの設定機能等、RDSには様々なパラメータや機能があるので
気になった方はドキュメントやAPIリファレンスを参照してみてください。

本記事をもって、予定していた構成図における全リソースをAPIで構築することが出来ました。
第6回では、APIとRAM(Resource Access Management)の関係について紹介したいと思います。

次回もよろしくお願いします!!


  1. なお、Alibaba CloudではMaxComputeDataV等、ビックデータ用のプロダクトを数多く提供しています。

  2. 詳細はRDSの料金表をご覧ください。

  3. 2019年6月現在

  4. 2019年6月現在