Shift from Containers to Serverless Computing using AWS Lambda - Part 1

Using Containers and Container Orchestration tools to run Microservices is really cool. However it comes with it’s own overhead in terms of keeping services running. Typically depending on environment and service criticality there will be at least two instances running per Microservice for high availability(HA) which can obviously scale up based on load. Cost goes up quickly if you add up all Microservices, environments(dev, int, stage, prod), availability zones and so on. So there will be always a certain cost for keeping environment running even if there’s no load.

Come Serverless and cost can go down drastically as you don’t need to provision and manage infrastructure any more and you pay only for what you consume. For example for AWS Lambda you pay every GB-second and number of requests. However if service is dealing with bursty traffic and millions of requets then it’s best to factor in regional concurrency limits, ramp up time and cost before going Lambda route.

In this post, we’ll focus on Function as a Service(FAAS). All major cloud providers support FaaS. AWS Lambda is clearly best one at least at this point in time. Over 15 AWS services can act as a Event Source for Lambda. This event sources includes on demand over HTTPS using Amazon API Gateway, SNS notifications and several other event sources. This ecosystem of event sources and Lambda makes them ideal for both Synchronous and Asynchronous processing. Bonus, just like other AWS resources, you can spin up complete CloudFormation stack including SNS topic, Subscription, Lambda using CloudFormation template, Terraform or other IaC frameworks.

So let’s build a very simple Node.JS Lambda function. It will subscribe to SNS topic and log the events. We’ll use following CloudFormation template to create SNS topic, Lambda and subscribing Lambda to SNS topic. It also takes care of creating required Roles and Policies. For the sake of demonstration Lambda source code is specified as an inline text in CloudFormation template.

 1AWSTemplateFormatVersion: 2010-09-09
 3  Topic:
 4    Type: 'AWS::SNS::Topic'
 5  LambdaInvokePermission:
 6    Type: 'AWS::Lambda::Permission'
 7    Properties:
 8      Action: 'lambda:InvokeFunction'
 9      Principal:
10      FunctionName: !GetAtt
11        - FunctionTopicSubscription
12        - Arn
13      SourceArn: !Ref Topic
14  Subscription:
15    Type: 'AWS::SNS::Subscription'
16    Properties:
17      Endpoint: !GetAtt
18        - FunctionTopicSubscription
19        - Arn
20      Protocol: lambda
21      TopicArn: !Ref Topic
22    DependsOn:
23      - FunctionTopicSubscription
24  FunctionTopicSubscription:
25    Type: 'AWS::Lambda::Function'
26    Properties:
27      Handler: index.handler
28      Runtime: nodejs6.10
29      Code:
30        ZipFile:
31          Fn::Join:
32          - '
34            '
35          - - exports.handler = function(event, context, callback) {
36            - '  console.log(''REQUEST RECEIVED:\n'', JSON.stringify(event));'
37            - '  callback(null, ''Request processed'');'
38            - '};'
39      Role: !GetAtt
40        - LambdaExecutionRole
41        - Arn
42      Timeout: '30'
43    DependsOn:
44      - LambdaExecutionRole
45  LambdaExecutionRole:
46    Type: 'AWS::IAM::Role'
47    Properties:
48      Policies:
49        - PolicyName: LambdaPolicy
50          PolicyDocument:
51            Version: 2012-10-17
52            Statement:
53              - Action:
54                  - 'logs:CreateLogGroup'
55                  - 'logs:CreateLogStream'
56                  - 'logs:PutLogEvents'
57                Resource:
58                  - 'arn:aws:logs:*:*:*'
59                Effect: Allow
60              - Action:
61                  - 'sns:Subscribe'
62                  - 'sns:Receive'
63                  - 'sns:Unsubscribe'
64                Resource: !Ref Topic
65                Effect: Allow
66      AssumeRolePolicyDocument:
67        Version: 2012-10-17
68        Statement:
69          - Action:
70              - 'sts:AssumeRole'
71            Effect: Allow
72            Principal:
73              Service:
74                -

We need to package it before creating CloudFormation Stack. Package command takes care of uploading local artifacts to s3 bucket and replacing there references with s3 location. Following AWS CLI command will take care of packaging -

$ aws cloudformation package --template-file <your-temaplate.yaml> --s3-bucket <your-s3-bucket-name> --output-template-file packaged-template.yaml

 Successfully packaged artifacts and wrote output template to file packaged-template.yaml.
 Execute the following command to deploy the packaged template
 aws cloudformation deploy --template-file /template-path/packaged-template.yaml --stack-name

Now we can create Stack using following command -

$ aws cloudformation deploy --template-file packaged-template.yaml --stack-name <your-stack-name> --capabilities CAPABILITY_IAM

  Waiting for changeset to be created..
  Waiting for stack create/update to complete
  Successfully created/updated stack - your-stack-name

Once stack is created we can list stack resources using following command -

$ aws cloudformation list-stack-resources --stack-name your-stack-name

You will see list of resources. Please copy SNS topic(AWS::SNS::Topic) ARN (PhysicalResourceId). We’ll need it to publish message to SNS topic.

Now that everything is in place, let’s publish a message to SNS topic using following command -

$ aws sns publish --topic-arn <your-sns-topic-arn> --message "Hello World"

This will result in invoking Lambda which will in turn print sns message. You can verify this using CloudWatch logs.

Source code is available on GitHub

That’s it. In next blog we’ll do something more advanced and meaningful using Lambda.

comments powered by Disqus