AWS RDS冗長化

AWS RDS冗長化

はじめに

RDSを冗長化する方法としてマルチAZ配置とリードレプリカがあります。

マルチAZ配置は同一リージョンの別AZに同期的にバックアップを行います。

リードレプリカは別リージョンに非同期的にバックアップを行います。

マルチAZをONにすると無料利用枠から外れてしまうため気をつける必要があります。

今回はこの辺りについて見ていくこととします。前回作成したDBを使用します。

テスト用DBの作成

きちんとバックアップができているか確認するため次のようなDBを作成します。デフォルトでTestDBがありましたので、その中にWordListというテーブルを作成します。

[ec2-user@ip-10-1-0-75 ~]$ mysql -h testdb-1.hoge.ap-northeast-1.rds.amazonaws.com -P 3306 -u admin -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 47
Server version: 10.2.21-MariaDB-log Source distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| TestDB             |
| information_schema |
| innodb             |
| mysql              |
| performance_schema |
+--------------------+
5 rows in set (0.00 sec)

MariaDB [(none)]> use TestDB;
Database changed
MariaDB [TestDB]> create table WordList(
    -> Timestamp datetime,
    -> Word varchar(30)
    -> );
Query OK, 0 rows affected (0.02 sec)
MariaDB [TestDB]> exit;
Bye

テスト用プログラムの作成

次に5秒ごとにランダムなワードをDBにInsertしていくプログラムを作成します。

まずはPythonモジュールを追加しておきます。

$sudo yum install python-pip
$sudo pip install mysql-connector

AmazonLinux2のデフォルトのPythonのバージョンが2.7というのはちょっと気に入らないですが、面倒なのでPython2.7用のソースで作成します。

#!/usr/bin/python
import requests
import time
import mysql.connector
import random

# DB Connect
con = mysql.connector.connect(
        host='testdb-1.hoge.ap-northeast-1.rds.amazonaws.com',
        db='TestDB',
        user='admin',
        passwd='adminpass'
        )
cur = con.cursor(dictionary=True)

# Get Wordlist
word_site = "http://svnweb.freebsd.org/csrg/share/dict/words?view=co&content-type=text/plain"
response = requests.get(word_site)
WORDS = response.content.splitlines()

# DB Insert
try:
    while True:
        timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
        word = random.choice(WORDS)
        print "%s %s" % (timestamp, word)
        cur.execute('insert into WordList (Timestamp, Word) values ("%s", "%s")' % (timestamp, word))
        con.commit()
        time.sleep(5)
except KeyboardInterrupt:
    cur.close()
    con.close()

マルチAZ配置をテスト

テスト用プログラムを実行しながらDBを停止してみたらどうなるか実行してみたいと思います。

まずはマルチAZを有効にします。

$ aws rds modify-db-instance --db-instance-identifier testdb-1 --multi-az --apply-immediately

数分間待ちます。

$ aws rds describe-db-instances --db-instance-identifier testdb-1 --query DBInstances[].DBInstanceStatus
[
    "modifying"
]

設定完了です。

$ aws rds describe-db-instances --db-instance-identifier testdb-1 --query DBInstances[].DBInstanceStatus
[
    "available"
]

それではフェイルオーバーさせてみます。

まずはPythonスクリプトを起動させておきます。

[ec2-user@ip-10-1-0-75 ~]$ python insert-random-word.py 

次にtestdb-1を再起動します。

$ aws rds reboot-db-instance --db-instance-identifier testdb-1 --force-failover 
{
    "DBInstances": [
        {
            "DBInstanceIdentifier": "testdb-1",
            "DBInstanceClass": "db.t2.micro",
            "Engine": "mariadb",
            "DBInstanceStatus": "rebooting",
            "MasterUsername": "admin",
            "DBName": "TestDB",
            "Endpoint": {
                "Address": "testdb-1.hoge.ap-northeast-1.rds.amazonaws.com",
                "Port": 3306,
                "HostedZoneId": "Z24O6O9L7SGTNB"
            },
            "AllocatedStorage": 20,
            "InstanceCreateTime": "2019-11-19T05:43:25.198Z",
            "PreferredBackupWindow": "19:34-20:04",
            "BackupRetentionPeriod": 7,
            "DBSecurityGroups": [],
            "VpcSecurityGroups": [
                {
                    "VpcSecurityGroupId": "sg-0fc1435eb1b8b4cc1",
                    "Status": "active"
                }
            ],
            "DBParameterGroups": [
                {
                    "DBParameterGroupName": "default.mariadb10.2",
                    "ParameterApplyStatus": "in-sync"
                }
            ],
            "AvailabilityZone": "ap-northeast-1a",
            "DBSubnetGroup": {
                "DBSubnetGroupName": "default-vpc-04e72e6413b624d29",
                "DBSubnetGroupDescription": "Created from the RDS Management Console",
                "VpcId": "vpc-04e72e6413b624d29",
                "SubnetGroupStatus": "Complete",
                "Subnets": [
                    {
                        "SubnetIdentifier": "subnet-0b99e52d17314979d",
                        "SubnetAvailabilityZone": {
                            "Name": "ap-northeast-1a"
                        },
                        "SubnetStatus": "Active"
                    },
                    {
                        "SubnetIdentifier": "subnet-0f2edab09ccc98c0f",
                        "SubnetAvailabilityZone": {
                            "Name": "ap-northeast-1c"
                        },
                        "SubnetStatus": "Active"
                    }
                ]
            },
            "PreferredMaintenanceWindow": "mon:18:18-mon:18:48",
            "PendingModifiedValues": {},
            "LatestRestorableTime": "2019-11-20T01:10:07Z",
            "MultiAZ": true,
            "EngineVersion": "10.2.21",
            "AutoMinorVersionUpgrade": true,
            "ReadReplicaDBInstanceIdentifiers": [],
            "LicenseModel": "general-public-license",
            "OptionGroupMemberships": [
                {
                    "OptionGroupName": "default:mariadb-10-2",
                    "Status": "in-sync"
                }
            ],
            "SecondaryAvailabilityZone": "ap-northeast-1c",
            "PubliclyAccessible": false,
            "StorageType": "gp2",
            "DbInstancePort": 0,
            "StorageEncrypted": false,
            "DbiResourceId": "db-PSTE7IM2ADXWR3IHIKVLWOXTOQ",
            "CACertificateIdentifier": "rds-ca-2015",
            "DomainMemberships": [],
            "CopyTagsToSnapshot": true,
            "MonitoringInterval": 0,
            "DBInstanceArn": "arn:aws:rds:ap-northeast-1:281230433728:db:testdb-1",
            "IAMDatabaseAuthenticationEnabled": false,
            "PerformanceInsightsEnabled": false,
            "DeletionProtection": false,
            "AssociatedRoles": [],
            "MaxAllocatedStorage": 1000
        }
    ]
}

再起動中もPythonスクリプトは止まらずに動作していました。ただ、途中までしかデータがInsertされていません。

MariaDB [TestDB]> select * from WordList;
+---------------------+-----------+
| Timestamp           | Word      |
+---------------------+-----------+
| 2019-11-20 01:16:03 | Pradesh   |
| 2019-11-20 01:16:08 | spiky     |
| 2019-11-20 01:16:13 | intensify |
| 2019-11-20 01:16:18 | pimp      |
| 2019-11-20 01:16:23 | acclimate |
| 2019-11-20 01:16:28 | trance    |
| 2019-11-20 01:16:33 | Holland   |
| 2019-11-20 01:16:38 | bode      |
+---------------------+-----------+
8 rows in set (0.01 sec)

どうやらconnectionを貼りっぱなしにしておくと止まってしまうようです。すぐに接続しなおしたら問題ありませんでした。フェイルオーバーを実行しないでDBを再起動した場合はPythonスクリプトは次のエラーとなりました。

$ python insert-random-word.py 
Traceback (most recent call last):
  File "insert-random-word.py", line 27, in <module>
    cur.execute('insert into WordList (Timestamp, Word) values ("%s", "%s")' % (timestamp, word))
  File "/usr/lib/python2.7/site-packages/mysql/connector/cursor.py", line 551, in execute
    self._handle_result(self._connection.cmd_query(stmt))
  File "/usr/lib/python2.7/site-packages/mysql/connector/connection.py", line 490, in cmd_query
    result = self._handle_result(self._send_cmd(ServerCmd.QUERY, query))
  File "/usr/lib/python2.7/site-packages/mysql/connector/connection.py", line 395, in _handle_result
    raise errors.get_exception(packet)
mysql.connector.errors.DatabaseError: 1927 (70100): Connection was killed

あとAZの状態を確認しましたが、すぐには表示は変わらないようです。しばらくしたら表示が切り替わりました。

$ aws rds describe-db-instances --db-instance-identifier testdb-1 --query 'DBInstances[].{"Status":DBInstanceStatus, "PrimaryAZ":AvailabilityZone,"SecondaryAZ":SecondaryAvailabilityZone}'
[
    {
        "Status": "rebooting",
        "PrimaryAZ": "ap-northeast-1c",
        "SecondaryAZ": "ap-northeast-1a"
    }
]

リードレプリカのテスト

マルチAZを適用した状態でもリードレプリカを使用できるようですが、ここは簡単にするためにシングル構成でリードレプリカを使用したいと思います。

まずはリードレプリカをバージニア北部のリージョンに作成します。source-db-instance-identifier は Arnで指定する必要があるので注意です。

$ aws rds create-db-instance-read-replica --db-instance-identifier testdb-1 --region us-east-1 --source-db-instance-identifier arn:aws:rds:ap-northeast-1:281230433728:db:testdb-1

作成されるまでしばらく待ちます。

$ aws rds describe-db-instances --db-instance-identifier testdb-1 --query DBInstances[].DBInstanceStatus
[
    "modifying"
]
$ aws rds describe-db-instances --db-instance-identifier testdb-1 --query DBInstances[].DBInstanceStatus
[
    "available"
]
$ aws rds describe-db-instances --db-instance-identifier testdb-1 --query DBInstances[].ReadReplicaDBInstanceIdentifiers
[
    [
        "arn:aws:rds:us-east-1:281230433728:db:testdb-1"
    ]
]

先ほどテストでInsertしたデータは削除しておきます。

$ mysql -h testdb-1.cjtmv0gt9jvq.ap-northeast-1.rds.amazonaws.com -P 3306 -u admin -p TestDB

MariaDB [TestDB]> delete from WordList;
Query OK, 23 rows affected (0.01 sec)

それではPythonスクリプトを実行しながらDB を切り替えてみます。

$ python insert-random-word.py 
2019-11-20 03:43:27 bigotry
2019-11-20 03:43:32 officious
2019-11-20 03:43:37 Trinidad
2019-11-20 03:43:42 slot
2019-11-20 03:43:47 shopworn
2019-11-20 03:43:52 maelstrom
2019-11-20 03:43:57 Hoff
2019-11-20 03:44:02 perspicacious
2019-11-20 03:44:07 maid
2019-11-20 03:44:12 Rhenish
2019-11-20 03:44:17 imitable
2019-11-20 03:44:22 detach
2019-11-20 03:44:27 fallible
2019-11-20 03:44:32 quiescent
2019-11-20 03:44:37 me
2019-11-20 03:44:42 that
2019-11-20 03:44:47 winter
2019-11-20 03:44:52 applicant
2019-11-20 03:44:57 opponent
2019-11-20 03:45:02 antacid
2019-11-20 03:45:07 levee

DBを切り替えます。

$ aws rds promote-read-replica --db-instance-identifier testdb-1 --region us-east-1
$  aws rds describe-db-instances --db-instance-identifier testdb-1 --query DBInstances[].DBInstanceStatus --region us-east-1
[
    "rebooting"
]
$  aws rds describe-db-instances --db-instance-identifier testdb-1 --query DBInstances[].DBInstanceStatus --region us-east-1
[
    "backing-up"
]
$  aws rds describe-db-instances --db-instance-identifier testdb-1 --query DBInstances[].DBInstanceStatus --region us-east-1
[
    "available"
]

切り替えが終わったのでDBのテーブルを見て見ます。切り替えの間、Pythonスクリプトはconnectionが切れることもなく動作しつづけました。

MariaDB [TestDB]> select * from WordList;
+---------------------+---------------+
| Timestamp           | Word          |
+---------------------+---------------+
| 2019-11-20 03:43:27 | bigotry       |
| 2019-11-20 03:43:32 | officious     |
| 2019-11-20 03:43:37 | Trinidad      |
| 2019-11-20 03:43:42 | slot          |
| 2019-11-20 03:43:47 | shopworn      |
| 2019-11-20 03:43:52 | maelstrom     |
(途中省略)
| 2019-11-20 03:53:03 | decryption    |
| 2019-11-20 03:53:08 | jurisdiction  |
| 2019-11-20 03:53:13 | topgallant    |
| 2019-11-20 03:53:18 | syllogistic   |
| 2019-11-20 03:53:23 | nitrogenous   |
| 2019-11-20 03:53:28 | classroom     |
| 2019-11-20 03:53:33 | dynamism      |
| 2019-11-20 03:53:38 | Indoeuropean  |
| 2019-11-20 03:53:43 | slump         |
+---------------------+---------------+
124 rows in set (0.00 sec)

データの欠落もありません。でもよく考えたら、途中からバージニア北部のDBに切り替わっているはずです。

バージニア北部のセキュリティグループにもポート3306を追加して接続してみることにしました。

$ mysql -h testdb-1.hoge.us-east-1.rds.amazonaws.com -P 3306 -u admin -p TestDB
Enter password: 
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 13
Server version: 10.2.21-MariaDB-log Source distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [TestDB]> select * from WordList;
+---------------------+---------------+
| Timestamp           | Word          |
+---------------------+---------------+
| 2019-11-20 03:43:27 | bigotry       |
| 2019-11-20 03:43:32 | officious     |
| 2019-11-20 03:43:37 | Trinidad      |
| 2019-11-20 03:43:42 | slot          |
| 2019-11-20 03:43:47 | shopworn      |
| 2019-11-20 03:43:52 | maelstrom     |
| 2019-11-20 03:43:57 | Hoff          |
| 2019-11-20 03:44:02 | perspicacious |
| 2019-11-20 03:44:07 | maid          |
| 2019-11-20 03:44:12 | Rhenish       |
| 2019-11-20 03:44:17 | imitable      |
| 2019-11-20 03:44:22 | detach        |
| 2019-11-20 03:44:27 | fallible      |
| 2019-11-20 03:44:32 | quiescent     |
| 2019-11-20 03:44:37 | me            |
| 2019-11-20 03:44:42 | that          |
| 2019-11-20 03:44:47 | winter        |
| 2019-11-20 03:44:52 | applicant     |
| 2019-11-20 03:44:57 | opponent      |
| 2019-11-20 03:45:02 | antacid       |
| 2019-11-20 03:45:07 | levee         |
+---------------------+---------------+
21 rows in set (0.15 sec)

途中で切れてます。。。

もしかしてポートを開けておかなかったことが原因かもしれません。もう一度両リージョンのデータを削除してテストしてみます。

もう一度実行してみましたが、同じでした。

まとめ

マルチAZ、リードレプリカの2つを試しましたが、フェイルオーバーの際の処理には注意が必要なようです。