この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
今回は最後のリソース、RDS の DB インスタンスを作成します。
前回作成した DB クラスターに紐付けます。
前回の記事はこちら。
AWS 構成図
今回でこの構成が完成します。
2 台の DB インスタンスを立ち上げ、そのうち 1 台が Aurora レプリカとなります。
設計
プロパティは以下の通り。
devio-stg-rds-instance-1a
項目 | 値 |
---|---|
Availability Zone | ap-northeast-1a |
エンジン | Aurora MySQL |
DB インスタンスクラス | db.r5.large |
DB クラスター ID | devio-stg-rds-cluster |
サブネットグループ | devio-stg-rds-sng |
パラメータグループ | 前々回作ったやつ |
モニタリング詳細度 | 60 秒 |
モニタリングロール | devio-stg-role-rds |
パフォーマンスインサイト | 有効 |
パフォーマンスインサイト保持期間 | 7 日 |
マイナーバージョン自動アップグレード | 無効 |
メンテナンスウィンドウ | 月曜 5:00-5:30 JST(日曜 20:00-20:30 UTC) |
devio-stg-rds-instance-1c
項目 | 値 |
---|---|
Availability Zone | ap-northeast-1c |
エンジン | Aurora MySQL |
DB インスタンスクラス | db.r5.large |
DB クラスター ID | devio-stg-rds-cluster |
サブネットグループ | devio-stg-rds-sng |
パラメータグループ | 前々回作ったやつ |
モニタリング詳細度 | 60 秒 |
モニタリングロール | devio-stg-role-rds |
パフォーマンスインサイト | 有効 |
パフォーマンスインサイト保持期間 | 7 日 |
マイナーバージョン自動アップグレード | 無効 |
メンテナンスウィンドウ | 月曜 5:30-6:00 JST(日曜 20:30-21:00 UTC) |
2 つのインスタンスで異なる部分は AZ とメンテナンスウィンドウくらいです。
メンテナンスウィンドウに関してはクラスターとインスタンスそれぞれで 30 分ずつ時間をずらしました。
実装
RDS に関する処理を行うクラスに、ハイライト部分を追記しました。
lib/resource/rds.ts
import * as cdk from '@aws-cdk/core';
import { CfnDBSubnetGroup, CfnDBClusterParameterGroup, CfnDBParameterGroup, CfnDBCluster, CfnDBInstance } from '@aws-cdk/aws-rds';
import { CfnSubnet, CfnSecurityGroup } from '@aws-cdk/aws-ec2';
import { CfnSecret } from '@aws-cdk/aws-secretsmanager';
import { CfnRole } from '@aws-cdk/aws-iam';
import { Resource } from './abstract/resource';
import { SecretsManager, OSecretKey } from './secretsManager';
interface InstanceInfo {
readonly id: string;
readonly availabilityZone: string;
readonly preferredMaintenanceWindow: string;
readonly resourceName: string;
readonly assign: (instance: CfnDBInstance) => void;
}
export class Rds extends Resource {
public dbCluster: CfnDBCluster;
public dbInstance1a: CfnDBInstance;
public dbInstance1c: CfnDBInstance;
private static readonly engine = 'aurora-mysql';
private static readonly databaseName = 'devio';
private static readonly dbInstanceClass = 'db.r5.large';
private readonly subnetDb1a: CfnSubnet;
private readonly subnetDb1c: CfnSubnet;
private readonly securityGroupRds: CfnSecurityGroup;
private readonly secretRdsCluster: CfnSecret;
private readonly iamRoleRds: CfnRole;
private readonly instances: InstanceInfo[] = [
{
id: 'RdsDbInstance1a',
availabilityZone: 'ap-northeast-1a',
preferredMaintenanceWindow: 'sun:20:00-sun:20:30',
resourceName: 'rds-instance-1a',
assign: instance => this.dbInstance1a = instance
},
{
id: 'RdsDbInstance1c',
availabilityZone: 'ap-northeast-1c',
preferredMaintenanceWindow: 'sun:20:30-sun:21:00',
resourceName: 'rds-instance-1c',
assign: instance => this.dbInstance1c = instance
}
];
constructor(
subnetDb1a: CfnSubnet,
subnetDb1c: CfnSubnet,
securityGroupRds: CfnSecurityGroup,
secretRdsCluster: CfnSecret,
iamRoleRds: CfnRole
) {
super();
this.subnetDb1a = subnetDb1a;
this.subnetDb1c = subnetDb1c;
this.securityGroupRds = securityGroupRds;
this.secretRdsCluster = secretRdsCluster;
this.iamRoleRds = iamRoleRds;
};
createResources(scope: cdk.Construct) {
const subnetGroup = this.createSubnetGroup(scope);
const clusterParameterGroup = this.createClusterParameterGroup(scope);
const parameterGroup = this.createParameterGroup(scope);
this.dbCluster = this.createCluster(scope, subnetGroup, clusterParameterGroup);
for (const instanceInfo of this.instances) {
const instance = this.createInstance(scope, instanceInfo, this.dbCluster, subnetGroup, parameterGroup);
instanceInfo.assign(instance);
}
}
private createSubnetGroup(scope: cdk.Construct): CfnDBSubnetGroup {
const subnetGroup = new CfnDBSubnetGroup(scope, 'RdsDbSubnetGroup', {
dbSubnetGroupDescription: 'Subnet Group for RDS',
subnetIds: [this.subnetDb1a.ref, this.subnetDb1c.ref],
dbSubnetGroupName: this.createResourceName(scope, 'rds-sng')
});
return subnetGroup;
}
private createClusterParameterGroup(scope: cdk.Construct): CfnDBClusterParameterGroup {
const clusterParameterGroup = new CfnDBClusterParameterGroup(scope, 'RdsDbClusterParameterGroup', {
description: 'Cluster Parameter Group for RDS',
family: 'aurora-mysql5.7',
parameters: { time_zone: 'UTC' }
});
return clusterParameterGroup;
}
private createParameterGroup(scope: cdk.Construct): CfnDBParameterGroup {
const parameterGroup = new CfnDBParameterGroup(scope, 'RdsDbParameterGroup', {
description: 'Parameter Group for RDS',
family: 'aurora-mysql5.7'
});
return parameterGroup;
}
private createCluster(scope: cdk.Construct, subnetGroup: CfnDBSubnetGroup, clusterParameterGroup: CfnDBClusterParameterGroup): CfnDBCluster {
const cluster = new CfnDBCluster(scope, 'RdsDbCluster', {
engine: Rds.engine,
backupRetentionPeriod: 7,
databaseName: Rds.databaseName,
dbClusterIdentifier: this.createResourceName(scope, 'rds-cluster'),
dbClusterParameterGroupName: clusterParameterGroup.ref,
dbSubnetGroupName: subnetGroup.ref,
enableCloudwatchLogsExports: ['error'],
engineMode: 'provisioned',
engineVersion: '5.7.mysql_aurora.2.10.0',
masterUserPassword: SecretsManager.getDynamicReference(this.secretRdsCluster, OSecretKey.MasterUserPassword),
masterUsername: SecretsManager.getDynamicReference(this.secretRdsCluster, OSecretKey.MasterUsername),
port: 3306,
preferredBackupWindow: '19:00-19:30',
preferredMaintenanceWindow: 'sun:19:30-sun:20:00',
storageEncrypted: true,
vpcSecurityGroupIds: [this.securityGroupRds.attrGroupId]
});
return cluster;
}
private createInstance(scope: cdk.Construct, instanceInfo: InstanceInfo, cluster: CfnDBCluster, subnetGroup: CfnDBSubnetGroup, parameterGroup: CfnDBParameterGroup): CfnDBInstance {
const instance = new CfnDBInstance(scope, instanceInfo.id, {
dbInstanceClass: Rds.dbInstanceClass,
autoMinorVersionUpgrade: false,
availabilityZone: instanceInfo.availabilityZone,
dbClusterIdentifier: cluster.ref,
dbInstanceIdentifier: this.createResourceName(scope, instanceInfo.resourceName),
dbParameterGroupName: parameterGroup.ref,
dbSubnetGroupName: subnetGroup.ref,
enablePerformanceInsights: true,
engine: Rds.engine,
monitoringInterval: 60,
monitoringRoleArn: this.iamRoleRds.attrArn,
performanceInsightsRetentionPeriod: 7,
preferredMaintenanceWindow: instanceInfo.preferredMaintenanceWindow,
});
return instance;
}
}
特に複雑なことはしておらず。いつものようにリソースを作成するためのメソッドを追加しています。
メインのプログラムはこちら。
ハイライト部分を追記しました。
lib/devio-stack.ts
~ 省略 ~
export class DevioStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
~ 省略 ~
// RDS
const rds = new Rds(
subnet.db1a,
subnet.db1c,
securityGroup.rds,
secretsManager.secretRdsCluster,
iamRole.rds
);
rds.createResources(this);
}
}
モニタリングロールを Rds クラスに渡すためにコンストラクタにパラメータを追加しました。
テスト
テストコードはこちら。
ハイライト部分を追記しました。
test/resource/rds.test.ts
import { expect, countResources, haveResource, anything } from '@aws-cdk/assert';
import * as cdk from '@aws-cdk/core';
import * as Devio from '../../lib/devio-stack';
test('Rds', () => {
const app = new cdk.App();
const stack = new Devio.DevioStack(app, 'DevioStack');
expect(stack).to(countResources('AWS::RDS::DBSubnetGroup', 1));
expect(stack).to(haveResource('AWS::RDS::DBSubnetGroup', {
DBSubnetGroupDescription: 'Subnet Group for RDS',
SubnetIds: anything(),
DBSubnetGroupName: 'undefined-undefined-rds-sng'
}));
expect(stack).to(countResources('AWS::RDS::DBClusterParameterGroup', 1));
expect(stack).to(haveResource('AWS::RDS::DBClusterParameterGroup', {
Description: 'Cluster Parameter Group for RDS',
Family: 'aurora-mysql5.7',
Parameters: { time_zone: 'UTC' }
}));
expect(stack).to(countResources('AWS::RDS::DBParameterGroup', 1));
expect(stack).to(haveResource('AWS::RDS::DBParameterGroup', {
Description: 'Parameter Group for RDS',
Family: 'aurora-mysql5.7'
}));
expect(stack).to(countResources('AWS::RDS::DBCluster', 1));
expect(stack).to(haveResource('AWS::RDS::DBCluster', {
Engine: 'aurora-mysql',
BackupRetentionPeriod: 7,
DatabaseName: 'devio',
DBClusterIdentifier: 'undefined-undefined-rds-cluster',
DBClusterParameterGroupName: anything(),
DBSubnetGroupName: anything(),
EnableCloudwatchLogsExports: ['error'],
EngineMode: 'provisioned',
EngineVersion: '5.7.mysql_aurora.2.10.0',
MasterUsername: anything(),
MasterUserPassword: anything(),
Port: 3306,
PreferredBackupWindow: '19:00-19:30',
PreferredMaintenanceWindow: 'sun:19:30-sun:20:00',
StorageEncrypted: true,
VpcSecurityGroupIds: anything()
}));
expect(stack).to(countResources('AWS::RDS::DBInstance', 2));
expect(stack).to(haveResource('AWS::RDS::DBInstance', {
DBInstanceClass: 'db.r5.large',
AutoMinorVersionUpgrade: false,
AvailabilityZone: 'ap-northeast-1a',
DBClusterIdentifier: anything(),
DBInstanceIdentifier: 'undefined-undefined-rds-instance-1a',
DBParameterGroupName: anything(),
DBSubnetGroupName: anything(),
EnablePerformanceInsights: true,
Engine: 'aurora-mysql',
MonitoringInterval: 60,
MonitoringRoleArn: anything(),
PerformanceInsightsRetentionPeriod: 7,
PreferredMaintenanceWindow: 'sun:20:00-sun:20:30',
}));
expect(stack).to(haveResource('AWS::RDS::DBInstance', {
DBInstanceClass: 'db.r5.large',
AutoMinorVersionUpgrade: false,
AvailabilityZone: 'ap-northeast-1c',
DBClusterIdentifier: anything(),
DBInstanceIdentifier: 'undefined-undefined-rds-instance-1c',
DBParameterGroupName: anything(),
DBSubnetGroupName: anything(),
EnablePerformanceInsights: true,
Engine: 'aurora-mysql',
MonitoringInterval: 60,
MonitoringRoleArn: anything(),
PerformanceInsightsRetentionPeriod: 7,
PreferredMaintenanceWindow: 'sun:20:30-sun:21:00',
}));
});
以下を確認しています。
- DB インスタンスのリソースが 2 つあること
- 各リソースのプロパティが正しいこと
確認
マネジメントコンソール上でリソースを確認してみましょう。
前回作成したクラスター配下に、今回作成したインスタンス 2 台が紐付いています。
前回は 作成中
だった各エンドポイントのステータスも 利用可能
に。
パフォーマンスインサイトも確認可能です。
続いて(少し長くなりますが)EC2 インスタンスからの疎通確認を行います。
MySQL クライアントのインストール
SSM のセッションマネージャーから EC2 にログインし、公式のインストール手順 を参考に MySQL のクライアントをインストールします。
以下のコマンドを実行し、MySQL の yum リポジトリをシステムのリポジトリリストに追加します。
$ sudo yum -y install https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
次に現在設定されているサブリポジトリのバージョンを確認します。
$ yum repolist all | grep mysql
現状バージョン 8.0
が有効化されているので、こちらを無効化し 5.7
を有効化します。
$ sudo yum-config-manager --disable mysql80-community
$ sudo yum-config-manager --enable mysql57-community
再度ステータスの確認。
$ yum repolist all | grep mysql
MySQL クライアントをインストールします。
$ sudo yum -y install mysql-community-client
インストールできました。
ここまでの MySQL クライアントインストール処理も EC2 の UserData に追加しておきます。
lib/script/ec2/userData.sh
#!/bin/bash
# Apache のインストール
sudo yum -y install httpd
sudo systemctl enable httpd
sudo systemctl start httpd
# MySQL クライアントのインストール
sudo yum -y install https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
sudo yum-config-manager --disable mysql80-community
sudo yum-config-manager --enable mysql57-community
sudo yum -y install mysql-community-client
RDS インスタンスへの接続
クラスター(ライター)エンドポイントをターゲットに以下のコマンドを実行します。(ここでのクラスターエンドポイントは devio-stg-rds-cluster.cluster-cndvsdcsdzja.ap-northeast-1.rds.amazonaws.com
)
パスワードは Secrets Manager に保存されているものを入力します。
$ mysql -h devio-stg-rds-cluster.cluster-cndvsdcsdzja.ap-northeast-1.rds.amazonaws.com -P 3306 -u admin -p
接続できました!
クラスター作成時に指定した DB devio
も作成されています。
DB devio
に user
テーブルを作成してみます。
$ create table devio.user (id int, name varchar(8));
テーブルの作成(書き込み)も OK ですね。
なお RDS への接続時にリーダーエンドポイントを指定した場合、書き込み処理は行なえません。その名の通り、書き込み処理を行う場合はクラスター(ライター)エンドポイントを、読み込み処理のみを行う場合はリーダーエンドポイントを指定するようにしましょう。(ここでのリーダーエンドポイントは devio-stg-rds-cluster.cluster-ro-cndvsdcsdzja.ap-northeast-1.rds.amazonaws.com
)
CloudFormation 版
今回のコードを CFn で書くと以下のようになります。
RdsDbInstance1a:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceClass: db.r5.large
AutoMinorVersionUpgrade: false
AvailabilityZone: ap-northeast-1a
DBClusterIdentifier:
Ref: RdsDbCluster
DBInstanceIdentifier: devio-stg-rds-instance-1a
DBParameterGroupName:
Ref: RdsDbParameterGroup
DBSubnetGroupName:
Ref: RdsDbSubnetGroup
EnablePerformanceInsights: true
Engine: aurora-mysql
MonitoringInterval: 60
MonitoringRoleArn:
Fn::GetAtt:
- RoleRds
- Arn
PerformanceInsightsRetentionPeriod: 7
PreferredMaintenanceWindow: sun:20:00-sun:20:30
RdsDbInstance1c:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceClass: db.r5.large
AutoMinorVersionUpgrade: false
AvailabilityZone: ap-northeast-1c
DBClusterIdentifier:
Ref: RdsDbCluster
DBInstanceIdentifier: devio-stg-rds-instance-1c
DBParameterGroupName:
Ref: RdsDbParameterGroup
DBSubnetGroupName:
Ref: RdsDbSubnetGroup
EnablePerformanceInsights: true
Engine: aurora-mysql
MonitoringInterval: 60
MonitoringRoleArn:
Fn::GetAtt:
- RoleRds
- Arn
PerformanceInsightsRetentionPeriod: 7
PreferredMaintenanceWindow: sun:20:30-sun:21:00
GitHub
今回のソースコードは コチラ です。
おわりに
今回で当初目標としていた構成を AWS CDK ですべて実装することができました!
プログラムを書きながら学べたことがとても多く、非常に充実した取り組みでした。
本シリーズはこの回をもって 一部完 とさせていただきます。
以降はもう少しのんびりとした周期で、構成の拡張
や AWS CDK の小ワザ
などをお伝えしていきたいと思います。
ありがとうございました。
リンク
- class CfnDBInstance (construct) | AWS CDK API Reference
- AWS::RDS::DBInstance | AWS CloudFormation User Guide
- DB インスタンスクラス | Amazon Aurora User Guide
- Amazon Aurora でのレプリケーション | Amazon Aurora User Guide
- MySQL データベースエンジンを実行している DB インスタンスへの接続 | Amazon RDS User Guide
- Installing MySQL on Linux Using the MySQL Yum Repository | MySQL 8.0 Reference Manual
- MySQL Yum Repository | MySQL Community Downloads
- Amazon Linux2にMySQLクライアントを4バージョンいれてみた。Ansible Playbookを添えて | DevelopersIO