Serverless Framework: Creating an RDS Serverless Database Example
Posted by Miguel Lopez on Sat 10 June 2023 in serverless
Technical Stack: AWS, Lambda, Serverless Framework, Python, RDS, MySQL
Read: 10 minutes
Pre-Requisites
To deploy the following Serverless Framework template, you will need the following:
- An AWS account.
- AWS CLI installed locally.
- API credentials for your AWS account configured in your AWS CLI locally by running
aws configure
. - Serverless framework installed locally via
npm -g install serverless
.
Creating a Serverless RDS Cluster
Using Serverless Framwework, the following serverless.yml
example creates these resources in AWS:
In addition to the Database, the template also creates a secret and SSM parameter to store the database password and host address. Your lambda can use these values to connect to the database.
service: derezzed-db-infrastructure
configValidationMode: error
custom:
stage: ${opt:stage, 'dev'}
params:
default:
securityGroupId: [ 'sg-xxxxxxxx' ]
DBSubnetGroupName: default-vpc-xxxxxxxxx
db_name: derezzeddb
master_username: derezzed
provider:
name: aws
stage: ${opt:stage, 'dev'}
runtime: 'python3.9'
region: 'us-west-2'
deploymentBucket:
name: serverless-deployments
serverSideEncryption: AES256
tags:
stage: ${opt:stage, 'dev'}
resources:
Resources:
AuroraDerezzedSecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Join ['/', ["${param:db_name}", "${self:custom.stage}"]]
Description: !Join ["", ["secret for derezzed database ", !Ref "AWS::StackName"]]
GenerateSecretString:
SecretStringTemplate: '{"username": "${param:master_username}"}'
GenerateStringKey: "password"
ExcludeCharacters: '"@/\'
PasswordLength: 16
DerezzedDBCluster:
Type: AWS::RDS::DBCluster
Properties:
MasterUsername: ${param:master_username}
MasterUserPassword: !Join ['', ['{{resolve:secretsmanager:', !Ref AuroraDerezzedSecret, ':SecretString:password}}' ]]
DatabaseName: ${param:db_name}
Engine: aurora-postgresql
EngineMode: serverless
EnableHttpEndpoint: true
DBClusterIdentifier: DerezzedDBCluster-${self:custom.stage}
VpcSecurityGroupIds: ${param:securityGroupId}
DBSubnetGroupName: ${param:DBSubnetGroupName}
ScalingConfiguration:
AutoPause: false
MaxCapacity: 4
MinCapacity: 2
DatabaseHostParam:
Type: AWS::SSM::Parameter
DependsOn:
- DerezzedDBCluster
Properties:
Description: "Host Address for the Derezzed DB"
Name: "/derezzed/${self:custom.stage}/db-host"
Type: String
Value: !GetAtt DerezzedDBCluster.Endpoint.Address
Outputs:
AuroraDerezzedSecretArn:
Value: !GetAtt AuroraDerezzedSecret.Arn
AuroraDerezzedSecretName:
Value: !Ref AuroraDerezzedSecret
DatabaseHostParam:
Value: !Ref DatabaseHostParam
DatabaseClusterName:
Value: !Ref DerezzedDBCluster
Deploying the Serverless RDS Cluster
Run the following command to deploy the Serverless RDS Cluster:
sls deploy --stage dev
You'll see the following output when creating this cluster. A fresh database deployment can take up to ~5 minutes.
Running "serverless" from node_modules
Deploying to stage dev
✔ derezzed-db-infrastructure › deployed › 219s
Run sls outputs --stage dev
to see the outputs of the deployment. You'll see the following:
Running "serverless" from node_modules
derezzed-db-infrastructure:
DatabaseHostParamName: /derezzed/dev/db-host
DatabaseClusterName: derezzeddbcluster-dev
AuroraDerezzedSecretName: arn:aws:secretsmanager:us-west-2:xxxxxxx:secret:derezzeddb/dev-DldvfU
ServerlessDeploymentBucketName: serverless-deployments
Connecting to the Cluster from your local machine
Once the cluster is created, you can connect to the database using the following command:
psql -h $(aws ssm get-parameter --name /derezzed/dev/db-host --query Parameter.Value --output text) -U derezzed -d derezzeddb
You can find the password value in Secrets Manager with the following command:
aws secretsmanager get-secret-value --secret-id derezzed/dev --query SecretString --output text
Connecting to the Cluster from Lambda
To connect to the cluster from Lambda, you can use the following code:
import boto3
import os
import psycopg2
password = boto3.client('secretsmanager').get_secret_value(SecretId=os.environ['SECRET_NAME'])['SecretString']
host = boto3.client('ssm').get_parameter(Name=os.environ['DB_HOST_PARAM_NAME'])['Parameter']['Value']
conn = psycopg2.connect(
host=host
database="derezzeddb",
user="derezzed",
password=password)
def handler(event, context):
with conn.cursor() as cur:
cur.execute("SELECT * FROM users")
rows = cur.fetchall()
for row in rows:
print(row)
Don't forget to fetch Secrets and Parameters outside of your lambda handler. This will prevent your lambda from fetching these values on every invocation. Saving you $$$$$.
Troubleshooting Connections
If you can't connect, check the following:
- Do you have the correct security group attached to the RDS Cluster?
- Are you invoking the CLI command from a machine that has access to the security group?
Other Serverless Framework Examples
Check out the other Serverless Framework example projects I've created: