CloudFormationを使用して、RDSクロスリージョンリードレプリカを作成する
AWS SAAの出題範囲の1つである、弾力性に優れたアーキテクチャの設計に関する内容です。
以下のページで、RDSのリードレプリカをご紹介しました。
上記ページではリードレプリカを同一リージョン上に配置しましたが、AWS公式では、レプリカの配置場所について以下のように言及しています。
Amazon RDS では、リードレプリカをソース DB インスタンスとは異なる AWS リージョン に作成できます。
別の AWS リージョン でのリードレプリカの作成
本ページでは、CloudFormationを使用して、クロスリージョンリードレプリカを作成します。
構築する環境
2つのリージョンにVPCを作成します。
一方のリージョンにはRDSのDBインスタンスを作成します。
もう一方のリージョンには、このインスタンスのリードレプリカを作成します。
なおDBインスタンスのエンジンはMySQLとします。
各VPCにEC2インスタンスを配置します。
DBインスタンスに接続するクライアントとして使用します。
インスタンスは最新版のAmazon Linux 2です。
リードレプリカ側のリージョンに、Lambda関数を作成し、これをCloudFormationカスタムリソースとして設定します。
この関数の働きは、リードレプリカのセキュリティグループを変更することです。
関数のランタイム環境はPython3.8とします。
2種類のVPCエンドポイントを作成します。
1つ目はSSM用です。
SSM Session Managerを使用して、プライベートサブネット内のEC2インスタンスに接続するためです。
2つ目はS3用です。
S3バケット上に構築されたyumリポジトリにアクセスするためです。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
https://github.com/awstut-an-r/awstut-saa/tree/main/01/004
テンプレートファイルのポイント解説
クロスリージョンリードレプリカ
Resources:
DBInstance:
Type: AWS::RDS::DBInstance
DeletionPolicy: Delete
Properties:
AvailabilityZone: !Sub "${AWS::Region}${AvailabilityZone}"
DBInstanceIdentifier: !Sub "${Prefix}-dbinstance"
DBSubnetGroupName: !Ref DBSubnetGroup
SourceDBInstanceIdentifier: !Sub "arn:aws:rds:${SourceDBInstanceRegion}:${AWS::AccountId}:db:${SourceDBInstanceIdentifier}"
Code language: YAML (yaml)
リードレプリカに関する基本的な事項は、冒頭でご紹介したページをご確認ください。
クロスリージョンリードレプリカを作成する上で、ポイントとなる設定はSourceDBInstanceIdentifierプロパティです。
本プロパティに、別リージョンに配置されているプライマリインスタンスのARNを指定します。
カスタムリソースとLambda関数
カスタムリソースを使用して、CloudForamtionスタック作成時に、リードレプリカのセキュリティグループを変更します。
これは以下の仕様が原因です。
リードレプリカは、デフォルトのセキュリティグループを使用します。
別の AWS リージョン でのリードレプリカの作成
つまりリードレプリカ作成時には、セキュリティグループは設定できず、デフォルトのものが適用されてしまうということです。
ですからリードレプリカ作成後に、手動で適切なセキュリティグループを設定し直す必要があります。
今回はこれをカスタムリソースに関連付けたLambda関数で実施します。
カスタムリソース
Resources:
CustomResource:
Type: Custom::CustomResource
Properties:
ServiceToken: !GetAtt Function.Arn
Code language: YAML (yaml)
カスタムリソースに関する基本的な事項については、以下のページをご確認ください。
カスタムリソースを作成し、後述のLambda関数を指定します。
Lambda関数
Resources:
Function:
Type: AWS::Lambda::Function
Properties:
Environment:
Variables:
DB_INSTANCE: !Ref DBInstance
DB_SECURITY_GROUP: !Ref DBSecurityGroup
REGION: !Ref AWS::Region
Code:
ZipFile: |
import boto3
import cfnresponse
import os
db_instance = os.environ['DB_INSTANCE']
db_security_group = os.environ['DB_SECURITY_GROUP']
region = os.environ['REGION']
CREATE = 'Create'
response_data = {}
def lambda_handler(event, context):
try:
if event['RequestType'] == CREATE:
client = boto3.client('rds', region_name=region)
response = client.modify_db_instance(
DBInstanceIdentifier=db_instance,
VpcSecurityGroupIds=[
db_security_group
]
)
print(response)
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
except Exception as e:
print(e)
cfnresponse.send(event, context, cfnresponse.FAILED, response_data)
FunctionName: !Sub "${Prefix}-function"
Handler: index.lambda_handler
Runtime: !Ref Runtime
Role: !GetAtt FunctionRole.Arn
Timeout: !Ref Timeout
Code language: YAML (yaml)
Lambda関数で実行するコードをインライン形式で記載します。
詳細につきましては、以下のページをご確認ください。
実行するコードの内容ですが、RDS クライアントオブジェクトのmodify_db_instanceメソッドを実行し、リードレプリカのセキュリティグループを変更するものです。
この処理がスタック作成時に実行されるように実装します。
IAMロール
Resources:
FunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
Policies:
- PolicyName: ModifyDBInstancePolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- rds:ModifyDBInstance
Resource:
- !Sub "arn:aws:rds:${AWS::Region}:${AWS::AccountId}:pg:*"
- !Sub "arn:aws:rds:${AWS::Region}:${AWS::AccountId}:secgrp:*"
- !Sub "arn:aws:rds:${AWS::Region}:${AWS::AccountId}:og:*"
- !Sub "arn:aws:rds:${AWS::Region}:${AWS::AccountId}:db:*"
Code language: YAML (yaml)
Lambda関数用のIAMロールです。
関数がmodify_db_instanceメソッドを実行するために必要な権限を与えます。
今回は以下のページを参考に設定しました。
https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/security_iam_id-based-policy-examples.html
(参考) EC2インスタンス
Resources:
Instance:
Type: AWS::EC2::Instance
Properties:
IamInstanceProfile: !Ref InstanceProfile
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
NetworkInterfaces:
- DeviceIndex: 0
SubnetId: !Ref InstanceSubnet
GroupSet:
- !Ref InstanceSecurityGroup
UserData: !Base64 |
#!/bin/bash -xe
yum update -y
yum install -y mariadb
Code language: YAML (yaml)
EC2インスタンスからDBインスタンスにアクセスするために、クライアントパッケージを準備する必要があります。
今回はユーザデータを使用して、パッケージをインストールします。
ユーザデータに関する詳細については、以下のページをご確認ください。
Amazon Linux 2からMySQLタイプのRDSにアクセスするためには、mariadbパッケージをインストールする必要があります。
Amazon Linux 2から各種RDSに接続するためのクライアントパッケージについては、以下のページをご確認ください。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
今回は以下のコマンドで2つのリージョンにスタックを作成します。
$ aws cloudformation create-stack \
--stack-name saa-01-004-1 \
--template-url https://[bucket-url]/saa-01-004-1.yaml \
--capabilities CAPABILITY_IAM \
--region ap-northeast-1
$ aws cloudformation create-stack \
--stack-name saa-01-004-2 \
--template-url https://[bucket-url]/saa-01-004-2.yaml \
--capabilities CAPABILITY_IAM \
--region us-east-1
Code language: Bash (bash)
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- 東京リージョンのEC2インスタンス:i-01dff604f44d82f1d
- 東京リージョンのRDSのエンドポイント:saa-01-004-1-dbinstance.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
- ヴァージニアリージョンのEC2インスタンス:i-0ddbcbbbaffb35b48
- ヴァージニアリージョンのRDSのエンドポイント:saa-01-004-2-dbinstance.csx6ivwzoymt.us-east-1.rds.amazonaws.com
- CloudFormationカスタムリソース用Lambda関数:saa-01-004-2-
AWS Management Consoleから各リソースを確認します。
東京リージョンのDBインスタンスを確認します。
Roleの項目を見ると、このDBインスタンスがプライマリインスタンスとして作成されていることがわかります。
レプリケーションの項目を見ると、後述のヴァージニアリージョンのDBインスタンスがリードレプリカとして存在していることがわかります。
ヴァージニアリージョンのDBインスタンスを確認します。
Roleの項目を見ると、このDBインスタンスがリードレプリカインスタンスとして作成されていることがわかります。
レプリケーションの項目を見ると、先ほど確認したプライマリインスタンスをソースとして、レプリケーションが設定されていることがわかります。
またセキュリティグループの項目を見ると、今回作成したセキュリティグループが指定されていることがわかります。
つまりCloudFormationカスタムリソースが正常に動作したことによって、デフォルトのものではなく、適切なものに置き換わったということです。
カスタムリソース用のLambda関数の実行ログを確認します。
確かにCloudFormationカスタムリソースによって、CloudFormationスタック作成時に、Lambda関数が実行されていることがわかります。
動作確認
東京リージョンのプライマリインスタンスにアクセス
準備が整いましたので、プライマリインスタンスに接続します。
DBインスタンスへの接続は、東京リージョンのEC2インスタンスから行います。
インスタンスへのアクセスはSSM Session Managerを使用します。
% aws ssm start-session --target i-01dff604f44d82f1d --region ap-northeast-1
...
sh-4.2$
Code language: Bash (bash)
SSM Session Managerの詳細につきましては、以下のページをご確認ください。
ユーザーデータによるEC2インスタンスの初期化処理の実行状況を確認します。
sh-4.2$ sudo yum list installed | grep mariadb
mariadb.aarch64 1:5.5.68-1.amzn2 @amzn2-core
mariadb-libs.aarch64 1:5.5.68-1.amzn2 installed
sh-4.2$ mysql -V
mysql Ver 15.1 Distrib 5.5.68-MariaDB, for Linux (aarch64) using readline 5.1
Code language: Bash (bash)
正常にMySQLクライアントパッケージがインストールされていることがわかります。
このクライアントパッケージを使用して、DBインスタンスに接続します。
sh-4.2$ mysql -h saa-01-004-1-dbinstance.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com -P 3306 -u testuser -p testdb
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 26
Server version: 8.0.27 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.
MySQL [testdb]>
Code language: Bash (bash)
正常に接続できました。
試しにテストテーブルを作成し、テストデータを保存します。
MySQL [testdb]> CREATE TABLE planet (id INT UNSIGNED AUTO_INCREMENT, name VARCHAR(30), PRIMARY KEY(id));
Query OK, 0 rows affected (0.03 sec)
MySQL [testdb]> INSERT INTO planet (name) VALUES ("Mercury");
Query OK, 1 row affected (0.01 sec)
MySQL [testdb]>
MySQL [testdb]> select * from planet;
+----+---------+
| id | name |
+----+---------+
| 1 | Mercury |
+----+---------+
1 row in set (0.01 sec)
Code language: Bash (bash)
正常にデータを保存できました。
ヴァージニアリージョンのリードレプリカインスタンスにアクセス
続いてクロスリージョンリードレプリカインスタンスに接続します。
こちらもヴァージニアリージョンのEC2インスタンスからリードレプリカに接続します。
% aws ssm start-session --target i-0ddbcbbbaffb35b48 --region us-east-1
...
sh-4.2$
Code language: Bash (bash)
インスタンスにアクセスできました。
リードレプリカインスタンスに接続します。
sh-4.2$ mysql -h saa-01-004-2-dbinstance.csx6ivwzoymt.us-east-1.rds.amazonaws.com -P 3306 -u testuser -p testdb
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 22
Server version: 8.0.27 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.
MySQL [testdb]>
Code language: Bash (bash)
リードレプリカに接続できました。
試しに読み込み/書き込み時の動作を確認します。
MySQL [testdb]> show tables;
+----------------+
| Tables_in_test |
+----------------+
| planet |
+----------------+
1 row in set (0.00 sec)
MySQL [testdb]> select * from planet;
+----+---------+
| id | name |
+----+---------+
| 1 | Mercury |
+----+---------+
1 row in set (0.01 sec)
MySQL [testdb]> INSERT INTO planet (name) VALUES ("Venus");
ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement
Code language: Bash (bash)
読み込みは成功しました。
東京リージョンに設置されているプライマリインスタンスで書き込んだ内容が、ヴァージニアリージョンに設置されているリードレプリカにも反映されています。
一方で、書き込みには失敗しました。
これはこのインスタンスが読み込み専用であるためです。
まとめ
RDSクロスリージョンリードレプリカの作成方法や、動作を確認しました。