How to create an RDS Aurora serverless instance with CDK
Cameron Jenkinson
Posted on February 2, 2020
Amazon Aurora and Aurora Serverless is a cloud computing database service that allows applications using ephemeral computing like lambda to be able to use schema based databases like SQL and Postgres without having to worry about connection pools.
That is the main benefit for me at least alongside a straightforward SDK using HTTP based 'Data API' queries to read and write data.
Before Aurora there were limited options for database services with serverless infrastructure because managing connection pools was difficult and posed risks.
Amazon Aurora Serverless is an on-demand, auto-scaling configuration for Amazon Aurora (MySQL-compatible and PostgreSQL-compatible editions), where the database will automatically start up, shut down, and scale capacity up or down based on your application's needs.
Let's go ahead and create an Aurora Serverless instance on AWS with CDK.
Here is the entire stack upfront where I dive into each section in further detail below.
const cdk = require('@aws-cdk/core');
const rds = require('@aws-cdk/aws-rds');
const secretsManager = require('@aws-cdk/aws-secretsmanager');
const ssm = require('@aws-cdk/aws-ssm');
class DBStack extends cdk.Stack {
constructor(app, id, { serviceName = 'movies', stage, accountId, }) {
super(app, id);
const databaseUsername = 'movies-database';
const databaseCredentialsSecret = new secretsManager.Secret(this, 'DBCredentialsSecret', {
secretName: `${serviceName}-${stage}-credentials`,
generateSecretString: {
secretStringTemplate: JSON.stringify({
username: databaseUsername,
}),
excludePunctuation: true,
includeSpace: false,
generateStringKey: 'password'
}
});
new ssm.StringParameter(this, 'DBCredentialsArn', {
parameterName: `${serviceName}-${stage}-credentials-arn`,
stringValue: databaseCredentialsSecret.secretArn,
});
const isDev = stage !== "production";
const dbConfig = {
dbClusterIdentifier: `main-${serviceName}-${stage}-cluster`,
engineMode: 'serverless',
engine: 'aurora-postgresql',
engineVersion: '10.7',
enableHttpEndpoint: true,
databaseName: 'main',
masterUsername: databaseCredentialsSecret.secretValueFromJson('username').toString(),
masterUserPassword: databaseCredentialsSecret.secretValueFromJson('password'),
backupRetentionPeriod: isDev ? 1 : 30,
finalSnapshotIdentifier: `main-${serviceName}-${stage}-snapshot`,
scalingConfiguration: {
autoPause: true,
maxCapacity: isDev ? 4 : 384,
minCapacity: 2,
secondsUntilAutoPause: isDev ? 3600 : 10800,
}
};
const rdsCluster = new rds.CfnDBCluster(this, 'DBCluster', { dbConfig,
deletionProtection: isDev ? false : true,
});
const dbClusterArn = `arn:aws:rds:${this.region}:${this.account}:cluster:${rdsCluster.ref}`;
new ssm.StringParameter(this, 'DBResourceArn', {
parameterName: `${serviceName}-${stage}-resource-arn`,
stringValue: dbClusterArn,
});
}
}
module.exports = { DBStack };
Breaking it down
Authentication credentials
Storing the authentication credentials in secrets manager makes it easy to access the instance from different services e.g a lambda function as well as when doing manual queries from the console UI.
We define a new secret here and store the database credentials in SSM parameter store. An option has been passed to generate a random password for the password field which is pretty handy.
const databaseCredentialsSecret = new secretsManager.Secret(this, 'DBCredentialsSecret', {
secretName: `${serviceName}-${stage}-credentials`,
generateSecretString: {
secretStringTemplate: JSON.stringify({
username: databaseUsername,
}),
excludePunctuation: true,
includeSpace: false,
generateStringKey: 'password'
}
});
new ssm.StringParameter(this, 'DBCredentialsArn', {
parameterName: `${serviceName}-${stage}-credentials-arn`,
stringValue: databaseCredentialsSecret.secretArn,
});
Aurora instance configuration
The main configuration for the Aurora instance takes mostly from the RDS cdk package where we can define explicit cloud formation resources. In this case we need to define a RDS Cluster and pass in the options to turn it into an Aurora Serverless instance.
There are a couple of essential options to pass including engineMode, enableHttpEndpoint and the scalingConfiguration.
When choosing between the underlying database engine you can either use Postgres or SQL, the default is SQL.
I've set a different scaling configuration depending on the deployment stage, in the case of the development is makes sense to keep the maxCapacity to a low number and higher for production.
const dbConfig = {
dbClusterIdentifier: `main-${serviceName}-${stage}-cluster`,
engineMode: 'serverless',
engine: 'aurora-postgresql',
engineVersion: '10.7',
enableHttpEndpoint: true,
databaseName: 'main',
masterUsername: databaseCredentialsSecret.secretValueFromJson('username').toString(),
masterUserPassword: databaseCredentialsSecret.secretValueFromJson('password'),
backupRetentionPeriod: isDev ? 1 : 30,
finalSnapshotIdentifier: `main-${serviceName}-${stage}-snapshot`,
scalingConfiguration: {
autoPause: true,
maxCapacity: isDev ? 4 : 384,
minCapacity: 2,
secondsUntilAutoPause: isDev ? 3600 : 10800,
}
};
const isDev = stage !== "production";
const rdsCluster = new rds.CfnDBCluster(this, 'DBCluster', { dbConfig,
deletionProtection: isDev ? false : true,
});
Resource ARN
The CFNCluster does not output the Resource ARN unlike other CDK constructors so we need to construct it manually.
We can then store the ARN in SSM parameter store which can be used alongside the authentication credentials ARN to execute queries through the relevant Aurora SDK.
const dbClusterArn = `arn:aws:rds:${this.region}:${this.account}:cluster:${rdsCluster.ref}`;
new ssm.StringParameter(this, 'DBResourceArn', {
parameterName: `${serviceName}-${stage}-resource-arn`,
stringValue: dbClusterArn,
});
Get mentored by me and succeed with serverless: https://bit.ly/2P4IgHu
Posted on February 2, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.