Aurora MySQL -> Aurora MySQLへのレプリケーション

概要

Aurora MySQLクラスターでは、共有ストレージを使用することでクラスター配下のDBインスタンス間のレプリカラグをミリ秒レベルまで抑えています。
しかし、Auroraでは通常のMySQLのレプリケーション機能を使用することもでき、これを使用すると、Aurora MySQLクラスターから別のAurora MySQLクラスターへデータ同期することができます。

これは例えば、読み取りのみ必要とするバッチサーバがいるなどして、バッチサーバが読み取り負荷を掛けると、同じリーダーエンドポイントを参照しているサービス側にもレスポンスに影響してしまうおそれがある、などといった状況を回避するのに有効です。

MySQLの標準のレプリケーションを使用してデータが同期された別のAurora MySQLクラスターを作成し、バッチサーバはそちらにアクセスさせるようにすることで、影響を抑えることができます。

公式ドキュメントは、「[Aurora と MySQL との間、または Aurora と別の Aurora DB クラスターとの間のレプリケーション](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/AuroraMySQL.Replication.MySQL.html"Aurora と MySQL との間、または Aurora と別の Aurora DB クラスターとの間のレプリケーション")」になります。ここを参考にすすめていきます。


レプリケーション元を用意

適当にcliで作っていきます。

クラスターの作成

適当にcluster01という名前で作ります。パラメータグループは事前に作成前提です。

$ aws rds create-db-cluster \
> --db-cluster-identifier cluster01 \
> --db-cluster-parameter-group-name cluster-param \
> --vpc-security-group-ids sg-xxxxxxxx \
> --db-subnet-group-name aurora-subnet-group \
> --engine aurora \
> --engine-version 5.6.10a \
> --port 3306 \
> --master-username <username> \
> --master-user-password <password>

クラスターにインスタンスを追加

適当にdb01で作っています。

$ aws rds create-db-instance \
> --db-instance-identifier db01 \
> --db-instance-class db.r4.2xlarge \
> --engine aurora \
> --db-parameter-group-name db-param \
> --option-group-name default:aurora-5-6 \
> --no-publicly-accessible \
> --db-cluster-identifier cluster01 \
> --no-storage-encrypted \
> --copy-tags-to-snapshot

レプリケーション元でバイナリログ記録を有効にする

レプリケーション元に紐づくDBクラスターパラメータグループのbinlog_formatMIXEDにします。

デフォルトでは

$ aws rds describe-db-cluster-parameters \
> --db-cluster-parameter-group-name cluster-param  \
> | jq -r '.Parameters[] | select(.ParameterName == "binlog_format")'
{
  "ApplyMethod": "pending-reboot",
  "Description": "Binary logging format for replication",
  "DataType": "string",
  "IsModifiable": true,
  "AllowedValues": "ROW,STATEMENT,MIXED,OFF",
  "Source": "system",
  "ParameterValue": "OFF",
  "ParameterName": "binlog_format",
  "ApplyType": "static"
}

で、OFFになっていますので、MIXEDへ変更します。

$ aws rds modify-db-cluster-parameter-group \
> --db-cluster-parameter-group-name cluster-param  \
> --parameters ParameterName=binlog_format,ParameterValue=MIXED,ApplyMethod=pending-reboot
{
    "DBClusterParameterGroupName": "cluster-param"
}

MIXEDになりました。

$ aws rds describe-db-cluster-parameters \
> --db-cluster-parameter-group-name cluster-param  \
> | jq -r '.Parameters[] | select(.ParameterName == "binlog_format")'
{
  "ApplyMethod": "pending-reboot",
  "Description": "Binary logging format for replication",
  "DataType": "string",
  "IsModifiable": true,
  "AllowedValues": "ROW,STATEMENT,MIXED,OFF",
  "Source": "user",
  "ParameterValue": "MIXED",
  "ParameterName": "binlog_format",
  "ApplyType": "static"
}

しかしこのパラメータは再起動するまで適用されません(pending-reboot)。

$ aws rds describe-db-clusters \
> --db-cluster-identifier cluster01 \
> | jq -r '.DBClusters[].DBClusterMembers'
[
  {
    "IsClusterWriter": true,
    "DBClusterParameterGroupStatus": "pending-reboot",
    "PromotionTier": 1,
    "DBInstanceIdentifier": "db01"
  }
]

再起動します。

$ aws rds reboot-db-instance \
> --db-instance-identifier db01

適用されました(in-syncになっています)。

$ aws rds describe-db-clusters \
--db-cluster-identifier cluster01 \
| jq -r '.DBClusters[].DBClusterMembers'
[
  {
    "IsClusterWriter": true,
    "DBClusterParameterGroupStatus": "in-sync",
    "PromotionTier": 1,
    "DBInstanceIdentifier": "db01"
  }
]

レプリケーション元のバイナリログの保持期間を設定する

デフォルトでは、バイナリログの保持期間が設定されておらず、NULL になっています。

$ mysql -u<username> -p<password> -h cluster01.cluster-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com -e'CALL mysql.rds_show_configuration\G'
*************************** 1. row ***************************
       name: binlog retention hours
      value: NULL
description: binlog retention hours specifies the duration in hours before binary logs are automatically deleted.

これを設定します。ドキュメントには、

Aurora MySQL DB クラスターのバイナリログファイルは最大 90 日間、保持できます。

とありますが、90日間(2160時間)を指定すると、

mysql> CALL mysql.rds_set_configuration('binlog retention hours', 2160);
ERROR 1644 (45000): For binlog retention hours the value must be between 1 and 168 inclusive or be NULL

となります。168時間(7日間)が上限のようです。

よくわからないので上限の7日にセットしておきます。。

mysql> CALL mysql.rds_set_configuration('binlog retention hours', 168);
Query OK, 0 rows affected (0.01 sec)

mysql> CALL mysql.rds_show_configuration\G
*************************** 1. row ***************************
       name: binlog retention hours
      value: 168
description: binlog retention hours specifies the duration in hours before binary logs are automatically deleted.
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

レプリケーション元にレプリケーション用ユーザを作成する

mysql> CREATE USER 'repl_user'@'10.0.0.0/255.255.0.0' IDENTIFIED BY '<password>';
mysql> GRANT REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO 'repl_user'@'10.0.0.0/255.255.0.0' IDENTIFIED BY '<password>';

のような感じで、レプリケーション用ユーザを作成しておきます。


レプリケーション先ー>レプリケーション元へのアクセスを許可する

レプリケーション先のインスタンスは、前項で作成したレプリケーション用ユーザでレプリケーション元にアクセスしますので、セキュリティグループで通信を許可しておきます。


レプリケーション元のスナップショットを作成する

レプリケーション元のスナップショットを作成します。

$ aws rds  create-db-cluster-snapshot \
> --db-cluster-snapshot-identifier cluster01-snapshot \
> --db-cluster-identifier cluster01

少し待つと出来上がります(available)。

$ aws rds describe-db-cluster-snapshots \
> --db-cluster-snapshot-identifier cluster01-snapshot \
> --db-cluster-identifier cluster01 \
> | jq '.DBClusterSnapshots[].Status'
"available"

スナップショットからレプリケーション先となるAurora MySQLクラスターを作成

つくります。

$ aws rds restore-db-cluster-from-snapshot \
> --availability-zones ap-northeast-1a ap-northeast-1c \
> --db-cluster-identifier cluster02 \
> --snapshot-identifier arn:aws:rds:ap-northeast-1:123456789012:cluster-snapshot:cluster01-snapshot  \
> --engine aurora \
> --engine-version 5.6.10a \
> --port 3306 \
> --db-subnet-group-name aurora-subnet-group \
> --vpc-security-group-ids sg-xxxxxxxx

クラスターパラメータグループがデフォルトになってしまうので修正します。

$ aws rds modify-db-cluster \
> --db-cluster-identifier 'cluster02' \
> --db-cluster-parameter-group-name 'cluster-param'

クラスターへインスタンスを追加します。

$ aws rds create-db-instance \
> --db-instance-identifier db02 \
> --db-instance-class db.r4.2xlarge \
> --engine aurora \
> --db-parameter-group-name db-param \
> --option-group-name default:aurora-5-6 \
> --no-publicly-accessible \
> --db-cluster-identifier cluster02 \
> --no-storage-encrypted \
> --copy-tags-to-snapshot

レプリケーション先インスタンスでレプリケーション設定をする

レプリケーション設定をします。まず、スナップショット作成時点の、レプリケーション元のバイナリログファイル名とpositionを調べます。

$ aws rds describe-events \
> --source-identifier db02 \
> --source-type db-instance
{
    "Events": [
        {
【略】
            "EventCategories": [],
            "SourceType": "db-instance",
            "SourceArn": "arn:aws:rds:ap-northeast-1:123456789012:db:db02",
            "Date": "2018-01-23T06:52:44.029Z",
            "Message": "Binlog position from crash recovery is mysql-bin-changelog.000004 538",
            "SourceIdentifier": "db02"
        },
【略】
}

のようにすると確認できます。

マネジメントコンソールでも確認ができます。

バイナリログファイルとposition

バイナリログファイル名がmysql-bin-changelog.000004、positionが538でした。

レプリケーション先へ接続し、

mysql> CALL mysql.rds_set_external_master ('cluster01.xxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com', 3306,  'repl_user', '<password>', 'mysql-bin-changelog.000004', 538, 0);
Query OK, 0 rows affected (0.10 sec)

mysql> CALL mysql.rds_start_replication;
+-------------------------+
| Message                 |
+-------------------------+
| Slave running normally. |
+-------------------------+
1 row in set (1.01 sec)

Query OK, 0 rows affected (1.01 sec)

で設定完了です。

間違ったときは、CALL mysql.rds_stop_replication;でレプリケーションを停止して、改めてCALL mysql.rds_set_external_masterで設定し、再度CALL mysql.rds_start_replication;すれば大丈夫です。

mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: cluster01.cluster-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com
                  Master_User: repl_user
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin-changelog.000004
          Read_Master_Log_Pos: 538
               Relay_Log_File: relaylog.000002
                Relay_Log_Pos: 293
        Relay_Master_Log_File: mysql-bin-changelog.000004
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table: mysql.rds_history,mysql.rds_monitor,mysql.rds_sysinfo,mysql.rds_replication_status
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 538
              Relay_Log_Space: 459
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File:
           Master_SSL_CA_Path:
              Master_SSL_Cert:
            Master_SSL_Cipher:
               Master_SSL_Key:
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 77834800
                  Master_UUID: 3ci19o8e-9ebd-335f-b281-10b8408311fd
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
           Master_Retry_Count: 86400
                  Master_Bind:
      Last_IO_Error_Timestamp:
     Last_SQL_Error_Timestamp:
               Master_SSL_Crl:
           Master_SSL_Crlpath:
           Retrieved_Gtid_Set:
            Executed_Gtid_Set:
                Auto_Position: 0
1 row in set (0.00 sec)

Seconds_Behind_Masterが0になっていれば、レプリケーション元に追いついている状態です。

動作の確認として、レプリケーション元にデータベースを作成して、同期されるかを確認します。

レプリケーション元にデータベースを作成します。

$ mysql -u<username> -p<password> -h cluster01.cluster-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com -e'create database replication_dekirukana'
$ mysql -u<username> -p<password> -h cluster01.cluster-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com -e'show databases'
+------------------------+
| Database               |
+------------------------+
| information_schema     |
| mysql                  |
| performance_schema     |
| replication_dekirukana |
+------------------------+

レプリケーション先でデータベース一覧を表示します。

$ mysql -u<username> -p<password> -h cluster02.cluster-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com -e'show databases'
+------------------------+
| Database               |
+------------------------+
| information_schema     |
| mysql                  |
| performance_schema     |
| replication_dekirukana |
+------------------------+

問題なく同期されました。


レプリカラグの監視について

Aurora MySQL -> Aurora MySQLへのMySQL標準レプリケーションの遅れは、CloudWatchのReplicaLagではなく、AuroraBinlogReplicaLagを見る必要があります。

これが、show slave statusSeconds_Behind_Masterを反映しています。 https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/Aurora.Monitoring.html

AuroraBinlogReplicaLag

ソース DB クラスターから Aurora MySQL DB クラスターまでの遅延時間。 このメトリクスでは、MySQL の SHOW SLAVE STATUS コマンドの Seconds_Behind_Master フィールドの値が報告され、異なる AWS リージョン間でレプリケートする Aurora DB クラスター間のレプリカの遅延を監視するのに役立ちます。


まとめ

ちょっとめんどくさいですが、無事レプリケーションを設定できました。

MySQLの標準のレプリケーションなので、レプリカラグはミリ秒単位とはならず、普通に遅れてしまう点だけ注意が必要ですが、ケースによっては有効に使えそうです。