Created
April 24, 2015 21:13
-
-
Save santtu/7e759d30f11dca78e3cf to your computer and use it in GitHub Desktop.
Example of an AWS Lambda function implementing a custom termination policy for auto scale group
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
console.log('Loading function'); | |
var minInstances = 1; | |
var region = 'us-east-1'; | |
var EventEmitter = require("events").EventEmitter; | |
var aws = require('aws-sdk'); | |
var http = require('http'); | |
var ec2 = new aws.EC2({region: region}); | |
var autoscaling = new aws.AutoScaling({region: region}); | |
var cloudwatch = new aws.CloudWatch({region: region}); | |
// This routine will select one instance to terminate based on the | |
// smallest "total time" value collected by `getTotalTime`. | |
function chooseAndTerminate(values, context) { | |
var currentId = null; | |
var currentValue = Infinity; | |
console.log("chooseAndTerminate:", values); | |
var count = Object.keys(values).length; | |
if (count <= minInstances) { | |
console.log("Only", count, "healthy instances, <= minimum of", | |
minInstances); | |
context.succeed(); | |
return; | |
} | |
for (var id in values) { | |
value = values[id]; | |
if (value < currentValue) { | |
currentValue = value; | |
currentId = id; | |
} | |
} | |
if (currentId !== null) { | |
console.log("Found candidate:", currentId, "value", currentValue); | |
console.log("Terminating instance"); | |
autoscaling.terminateInstanceInAutoScalingGroup( | |
{InstanceId: currentId, | |
ShouldDecrementDesiredCapacity: true | |
}, function(error, data) { | |
console.log("Instance termination in group"); | |
if (error) | |
context.fail(error); | |
if (data) | |
console.log("Data:", data); | |
context.succeed(); | |
}); | |
} else { | |
console.log("could not decide!"); | |
context.succeed(); | |
} | |
} | |
// This routine fetches a custom metric from the instance. In this | |
// time http://<instance>:80/total-time is assumed to return a single | |
// integer value. | |
function getTotalTime(ip) { | |
var e = new EventEmitter(); | |
var req = http.request( | |
{host: ip, path: "/total-time"}, | |
function(response) { | |
var str = ''; | |
response.on('data', | |
function(chunk) { | |
str += chunk; | |
}); | |
response.on('end', | |
function() { | |
e.emit('value', parseInt(str)); | |
e.emit('done'); | |
}); | |
}); | |
req.on('error', | |
function(error) { | |
e.emit('error', error); | |
e.emit('done'); | |
}); | |
req.end(); | |
return e; | |
} | |
exports.handler = function(event, context) { | |
console.log(JSON.stringify(event, null, 2)); | |
// Fetch alarm and group names from the CloudWatch notification. | |
var alarmData = JSON.parse(event.Records[0].Sns.Message); | |
console.log("Alarm data:", alarmData); | |
var alarmName = alarmData.AlarmName; | |
var groupName = null; | |
alarmData.Trigger.Dimensions.map(function(v) { | |
if (v.name == 'AutoScalingGroupName') | |
groupName = v.value; | |
}); | |
if (groupName === null) | |
context.fail("No auto scaling group name found"); | |
console.log("Downscale triggered for group", groupName, | |
"by alarm", alarmName); | |
// Go through all running instances in the auto scaling group. | |
ec2.describeInstances( | |
{Filters: | |
[{Name: "tag:aws:autoscaling:groupName", Values: [groupName]}, | |
{Name: "instance-state-name", Values: ["running"]}]}, | |
function(error, data) { | |
if (error) | |
context.fail(error); | |
if (data) { | |
values = {}; | |
count = data.Reservations.length; | |
if (count === 0) { | |
console.log("Strange, nothing in the auto scaling group!"); | |
context.succeed(); | |
return; | |
} | |
data.Reservations.map(function(r) { | |
r.Instances.map(function(i) { | |
var id = i.InstanceId; | |
var ip = i.PublicIpAddress; | |
console.log("Instance:", id, "at", ip); | |
getTotalTime(ip) | |
.on('value', | |
function(v) { | |
values[id] = v; | |
}) | |
.on('done', | |
function() { | |
count--; | |
if (count === 0) | |
chooseAndTerminate(values, context); | |
}); | |
}); | |
}); | |
} | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment