Introduction
As you might know, AWS recently added an ability to start and stop RDS instances, which can be really helpful for anyone who wants to save some costs on AWS bill. Let's use AWS Lambda function with CloudWatch events to stop RDS instances used by testing environment at night and on weekends.
Using the Code
First, let's create policy, which is in AWS IAM console. Go to Services -> Policies -> Create Policy -> Create Your Own Policy.
Let's call it RDSManagement
. Put the code given below to the 'Policy Document' field:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"rds:StopDBInstance",
"rds:StartDBInstance"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
Press 'Create policy', Create Your Own Policy put name, description and JSON to the appropriate fields and press Save. Eventually, you have to get something like on the screenshot below:
The second step is to create a role which will be associated with lambda function and allow it to manage RDS instances.
Navigate to Services -> Roles -> Create New Role. Select 'AWS Lambda' in 'AWS Service Role' section. Search for the policy we created previously, select it, and press 'Next'. Put LambdaRDSManagement
as a Role Name, set some description and press 'Create Role'.
Now we are ready to go and create lambda function which will manage our instances. Navigate to Services -> Lambda -> Create a Lambda function -> Blank function. Let's call it 'ManageRDSInstances
', select latest Node js 6.x as a runtime. Ignore lambda function code for now, and select 'Choose an existing role' in 'Role
' field. You have to be able to find the previously created role in 'Existing role' field. Press 'Next' -> 'Create function'.
Good, a puppet for the function is created and ready to go. You can check out a repo on github, clone it, run 'npm install
', archive the whole folder to a '.ZIP' file. (Or just download it - rds-lambda-stop-start.zip). We are using original aws-sdk for node js provided by Amazon.
It contains mainly three files:
- index.js - which is an actual lambda function which will handle your
CloudWatch
event:
var startInstance = require('./start');
var stopInstance = require('stop');
exports.handler = (event, context, callback) => {
event.instances.forEach((instance) => {
switch (event.action) {
case 'stop':
console.log(`Stopping instance '${instance}'...`);
stopInstance(instance);
break;
case 'start':
console.log(`Starting instance '${instance}'...`);
startInstance(instance);
break;
default:
throw `Invalid action ${event.action}`;
}
})
callback(null, 'Done!');
};
- stop.js, which stops running instance and create RDS snapshot in format "{instanceId}-{day}-{month}-{year}-{ticks}":
var AWS = require('aws-sdk');
module.exports = (instanceId) => {
var rds = new AWS.RDS();
var today = new Date();
var params = {
DBInstanceIdentifier: instanceId,
DBSnapshotIdentifier: `${instanceId}-${today.getDate()}-
${today.getMonth() + 1}-${today.getFullYear()}-
${today.getTime()}`
};
rds.stopDBInstance(params, function (err, data) {
if (err) console.log(err, err.stack);
else console.log(data);
});
};
- start.js which starts stopped instance.
var AWS = require('aws-sdk');
module.exports = (instanceId) => {
var rds = new AWS.RDS();
var params = {
DBInstanceIdentifier: instanceId
};
rds.startDBInstance(params, function (err, data) {
if (err) console.log(err, err.stack);
else console.log(data);
});
};
Now let's upload our archive to newly created lambda function. Services -> Lambda -> ManageRDSInstances, and change Code entry type to 'Upload a .ZIP file'. Press 'Upload', select your zip file and press 'Save'. Now we need to configure test event: Actions -> Configure test event.
{
"instances":[
"some-test-instance-1",
"some-test-instance-2"
],
"action":"stop"
}
Where some-test-instance-1
and some-test-instance-2
are your testing RDS instances. After pressing 'Save and Test', you will see that your RDS instances changed state to 'Stopping' and soon to 'Stopped'. Presto!
After they are stopped, you can run the same test with action 'start
', which will run change state of instances to running.
The last thing is to set up CloudWatch rules to trigger these function on schedule. Services -> CloudWatch -> Rules -> Create Rule. Select Schedule instead of default Event Pattern. Now you need to set up cron time, you can read more about it here. Keep in mind that time must be set in GMT timezone. For instance, to correctly start instances every morning Monday to Friday at 8 am in GMT+12 timezone cron time will look like this: '0 20 ? * SUN-THU *'.
After you set cron time for waking up your instances, select Lambda function as a Target and pick your newly create lambda function. Then in Configure Input section, put your JSON to Constant(JSON text) field:
{ "instances": ["some-test-instance-1",
"some-test-instance-2"], "action":"start" }
Configure Details -> Update rule. Done!
Now your instances will be woken every morning from Monday to Friday. Create a similar rule with correct cron time for stopping them, do not forget to change action from start to stop in the json:
{ "instances": ["some-test-instance-1",
"some-test-instance-2"], "action":"stop" }
Points of Interest
Starting/stopping RDS instances was a feature which was expected for a long while, and finally, guys from Amazon provided this functionality. Hopefully, this will reduce your AWS bill as well. :)
History
- 2nd June, 2017 - Article created
- 3rd June, 2017 - Fixed small mistakes, added images