Skip to content

Instantly share code, notes, and snippets.

@zrzka
Created November 2, 2016 22:00
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save zrzka/47c224effa76d45135b1790a7d063c04 to your computer and use it in GitHub Desktop.
Save zrzka/47c224effa76d45135b1790a7d063c04 to your computer and use it in GitHub Desktop.
AWS Keep Alive lambda function
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Keep alive functions",
"Resources": {
"LambdaPolicy": {
"Type": "AWS::IAM::ManagedPolicy",
"Properties": {
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:*"
],
"Effect": "Allow",
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": [
"*"
]
}
]
}
}
},
"LambdaRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"ManagedPolicyArns": [
{ "Ref": "LambdaPolicy" }
],
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sts:AssumeRole"
],
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
}
}
]
}
}
},
"KeepAliveFunction": {
"Type" : "AWS::Lambda::Function",
"DependsOn": [ "LambdaRole" ],
"Properties" : {
"Code" : {
"S3Bucket": { "your.s3.bucket.with.functions" },
"S3Key": "lambda/keep-alive-functions.zip"
},
"Description" : "Keep alive lambda containers function",
"FunctionName" : "KeepAliveLambdaContainers",
"Handler" : "keep_alive_handler.handler",
"MemorySize" : 128,
"Role" : { "Fn::GetAtt": [ "LambdaRole", "Arn" ] },
"Runtime" : "python2.7",
"Timeout" : "300"
}
},
"ScheduledLambdaContainersKeepAliveRule": {
"Type": "AWS::Events::Rule",
"Properties": {
"Description": "ScheduledRule",
"ScheduleExpression": "rate(10 minutes)",
"State": "ENABLED",
"Targets": [{
"Arn": { "Fn::GetAtt": ["KeepAliveFunction", "Arn"] },
"Id": "KeepAliveFunction"
}]
}
},
"PermissionForEventsToInvokeLambda": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": { "Ref": "KeepAliveFunction" },
"Action": "lambda:InvokeFunction",
"Principal": "events.amazonaws.com",
"SourceArn": { "Fn::GetAtt": ["ScheduledLambdaContainersKeepAliveRule", "Arn"] }
}
}
},
"Outputs": {
"KeepAliveLambdaFunctionArn": {
"Description": "Keep alive lambda function ARN",
"Value": { "Fn::GetAtt": [ "KeepAliveFunction", "Arn" ] }
}
}
}
from __future__ import print_function
import boto3
import logging
import json
import sys
from multiprocessing.pool import ThreadPool
from contextlib import closing
#
# Logging
#
class LessThanFilter(logging.Filter):
def __init__(self, exclusive_maximum, name=""):
super(LessThanFilter, self).__init__(name)
self.max_level = exclusive_maximum
def filter(self, record):
return int(record.levelno < self.max_level)
log = logging.getLogger()
log.setLevel(logging.INFO)
# Log to stdout < WARNING level
stdout = logging.StreamHandler(sys.stdout)
stdout.addFilter(LessThanFilter(logging.WARNING))
stdout.setLevel(logging.DEBUG)
# Log to stderr >= WARNING level
stderr = logging.StreamHandler(sys.stderr)
stderr.setLevel(logging.WARNING)
log.addHandler(stdout)
log.addHandler(stderr)
#
# Configuration
#
with open("config/functions.json") as input:
config = json.load(input)
region = config.get("region", "eu-west-1")
#
# Keep alive handler
#
def ping_function(func):
try:
name = func["name"]
event = func["event"]
payload = json.dumps(event)
logging.info("Invoking function {}".format(name))
client = boto3.client('lambda', region_name=region)
r = client.invoke(FunctionName=name, InvocationType="RequestResponse", Payload=payload)
if r.get("StatusCode", 0) != 200:
log.error("Failed to invoke {}, response: {}".format(name, r))
return 0
payload = json.loads(r["Payload"].read().decode("utf-8"))
if payload.get("statusCode", 0) != 200:
log.error("Invalid lambda {} response: {}".format(name, payload))
return 0
logging.info("Function {} invoked successfully".format(name))
return 1
except Exception as e:
logging.error("Failed to invoke {}, exception: {}".format(func, e))
return 0
def keep_alive_function(func):
try:
containers = func.get("containers", 1)
with closing(ThreadPool(containers)) as pool:
mp = pool.map_async(ping_function, [func] * containers)
_ = mp.get(300)
pool.terminate()
return containers
except Exception as e:
logging.error("Exception in keep_alive_function: {}".format(e))
return 0
def handler(event, context):
try:
functions = config.get("functions", [])
logging.debug("Functions {}".format(functions))
if len(functions) == 0:
logging.info("Skipping, no functions")
return "OK"
with closing(ThreadPool(len(functions))) as pool:
mp = pool.map_async(keep_alive_function, functions)
_ = mp.get(300)
pool.terminate()
return "OK"
except Exception as e:
log.error("Exception in handler: {}".format(e))
return "Internal Server Error"
if __name__ == "__main__":
handler(None, None)
{
"region" : "eu-west-1",
"functions": [
{
"name": "YourLambdaFunctionName",
"containers": 1,
"event": {
"resource":"/frank/{proxy+}",
"headers": {
"Accept": "application/json"
},
"httpMethod": "GET",
"requestContext": {
"stage": "v1"
},
"path": "/v1/frank/"
}
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment