Serverless Computing: How to Optimize AWS Lambda Functions

8 Min Read

Cloud-native services have altered the way we build our applications and accelerated the move from monolithic applications to microservices-based architecture. This is where the idea of "serverless computing" comes into the picture. Serverless computing services offer the ability to run code and store data without having to manage the underlying servers.

AWS Lambda is one of the most popular serverless computing services. Lambda functions enable you to execute code with various programming languages and runtimes without having to manage servers.

In this article, we'll discuss how Developers and Cloud Operations teams can optimize and manage AWS Lambda functions across the three areas of excellence for cloud management: cloud operations, cloud financial management, and cloud security and compliance.

Optimize AWS Lambda functions for cloud operations

When an AWS Lambda function is triggered by an event–which could be via Amazon API Gateway, an Amazon CloudWatch Event, or simply polling SQS queues–AWS takes the following actions:

  1. Creates an isolated environment for the function code to run. This code is pulled from an Amazon S3 bucket.
  2. The environment and runtime variables are set within this isolated runtime during initialization. The environment variables stored within Lambda configuration are encrypted at rest, but you can choose to encrypt it with the help of AWS Key Management Service (KMS) by creating your own key.
  3. Assigns an IAM role to the Lambda function with the permissions provided during the configuration stage. This role can be used to give the Lambda function access to other services.
  4. Runs the code.

Below is a sample of Lambda function code in Go programming language that's triggered by an Amazon CloudWatch Event. A configuration like this is usually used for cron jobs or data processing pipelines.

package main
import (
  "context"
  "errors"
  "log"
  "os"
  "github.com/aws/aws-lambda-go/events"
  "github.com/aws/aws-lambda-go/lambda"
  "github.com/go-redis/redis"
)
var (
  redisClient*redis.Client
)
func initialize() error {
  // this is cached for the specific lambda invocation
  if redisClient==nil {
    redisSecret:=os.Getenv("REDIS_SECRET")
    if redisSecret=="" {
      return errors.New("Redis Secret not set")
     }
     redisHost:=os.Getenv("REDIS_HOST")
    redistPort:=os.Getenv("REDIS_PORT")
    redisCLient=redis.NewClient(&redis.Options{
      Addr: redistHost+":"+redisPort,
      Password:"",
      DB: 0,
    })
  }
  return nil
}
func processCartDetails()error{
  //Add your logic here
  return nil
}
func handler(ctx context.Context, cwe events.CloudWatchEvent)error{
  err:=initialize()
  if err!=nil{
    log.PrintIn("Error Initializing")
    return err
  }
  err=processCartDetails()
  if err!=nil{
    log.PrintIn("Error Processing Cart Info")
    return err
  }
  return nil
}
func main(){
  lambda.Start(handler)
}

This function connects to Amazon ElastiCache for Redis and then processes some data in the ‘processCartDetails()’ method. (This hasn't been implemented in this example.)

Reduce compute time

Even though AWS Lambda functions cost fractions of a penny per GB-seconds, costs can add up quickly! One of the best practices to reduce compute time is to implement something like the ‘initialize()’ method outside the ‘handler()’ method, which will perform a connection to the Redis database. After the first execution of this function is complete, Lambda maintains the execution environment for some time in anticipation of another function invocation.

When this function is invoked again within the same time frame, Lambda reuses the environment context. In the example above, that would mean reusing the Redis database connection. The next invocation will not establish a new connection, which saves compute time, and in turn, reduces costs because Lambda charges are based on compute time. (More on this later.)

Below is a capture of AWS CloudWatch Logs from the execution of the example Lambda function. Notice the decrease in execution time duration (highlighted in yellow).

amazon cloudwatch logs aws lambda function

This operational efficiency is achieved because all the objects declared outside of the function’s ‘handler()’ method remain initialized. Additionally, 512 MB of disk space is provided to store the execution environment.

The key point to remember is NOT to assume Lambda will automatically reuse the execution environment for a subsequent invocation. Check if RedisClient is ‘nil’ and if it is, that means the same execution environment was not used and requires re-initialization of connections.

Reduce the size of AWS Lambda code

Another way to optimize operations is to reduce the size of the application code within the Lambda function. Developers tend to use web frameworks like Django/Flask in python or Go-Gin in Golang to handle routing of different API paths within the same function. This results in a bloated Lambda function, which can take longer to set up when invoked.

aws lambda api gateway anti model

A best practice is to use Amazon API Gateway to declare routes. From there, you can forward requests to multiple smaller Lambda functions that have shorter start times.

aws lambda api gateway model

Optimize AWS Lambda functions for cloud financial management

We've covered how to optimize AWS Lambda functions for cloud operations. Now, let’s look specifically at how we can optimize the cost of Lambda functions.

Reduce Duration and memory

AWS Lambda functions are charged based on the following factors: 

  • Number of Lambda functions
  • Duration of execution (per 100 milliseconds)
  • Memory allocated to your function
  • Data transfer cost IN and OUT of your AWS Lambda functions from outside the region 

The easiest way to reduce cloud costs is to decrease the execution Duration and to allocate less memory to your function, which can be achieved by monitoring the memory usage metric.

Leverage AWS Savings Plans discounts

AWS Savings Plans are a relatively new discounting option, similar to Reserved Instances (RIs) but with more flexibility. AWS Compute Savings Plans allow you to save money on Amazon EC2, AWS Fargate, and AWS Lambda, and offer up to 71% off Duration and Provisioned Concurrency.

Choosing the right AWS Savings Plan can be challenging. AWS offers three different payment options (no upfront, partial upfront, or all upfront) and two payment terms (one year or three years). There's also added complexity if you already own RIs or Convertible RIs. CloudHealth helps you with every phase of the Savings Plan lifecycle, from planning, analysis, tracking, accounting, to making purchases directly within the platform. Learn more in our in-depth guide: The Ultimate Guide to AWS Savings Plans

eBook
The Ultimate Guide To AWS Savings Plans

Optimize AWS Lambda functions for cloud security and compliance

Finally, let’s look at cloud security and compliance, which is one of the most critical aspects of cloud management and optimization.  

When creating a Lambda function, there are various configuration parameters that need to be provided. The important ones are: 

  • Execution Role (IAM role)
  • Logging
  • Environment variables 

To ensure that the function is secure, align with the following best practices.

Enable AWS CloudWatch Logs 

This can be achieved by updating the Execution Role (IAM role) attached to the function to have Write access to AWS CloudWatch Logs.

This is the permission set:

{
  "Version":"2012-10-17",
  "Statement":[
    {
      "Effect":"Allow",
      "Action":"logs:CreateLogGroup",
      "Resource":"arn:aws:logs:us-west-1::*"
    },
    {
      "Effect":"Allow",
      "Action":[
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource":[
        "arn:aws:logs:us-west-1::log-group:/aws/lambda/smart_lambda:*"
        ]
     }
  ]
}

All the logs from within the Lambda function will be stored in CloudWatch Logs. This is useful for debugging the function and identifying anomalies.

Use the Principle of Least Privilege

This naturally brings us to the topic of the Principle of Least Privilege. DO NOT provide any additional permissions to the Lambda function than it needs. If someone accidentally assigns an Administrator Role, then this function can be used to perform any action, including deleting resources or even removing users from the account, which would result in locking the account just by using APIs/SDKs within Lambda.

Encrypt environment variables at rest and in transit

By default, environment variables are encrypted at rest for AWS Lambda functions by using AWS Key Management Service (AWS KMS). You can also use your own encryption by leveraging AWS KMS, which might be necessary for organizations where there are compliance requirements mandating you to show how the encryption keys are being maintained and audited.

Encryption in transit is NOT enabled by default, which means that when the variables are being sent (in transit) to the Lambda instances, they're in plain text. To enable encryption in transit, you need to modify the Encryption Configuration for Lambda.

aws lambda instances encryption configuration

This adds an additional layer of encryption that obfuscates the variable values for both API and Console access. Within your function code, you can decrypt it by using the AWS KMS API. This also means that you must provide permissions to the Execution Role for AWS KMS API.

Avoid misconfigurations with CloudHealth Secure State 

Misconfigurations can result in compliance and auditing standards violations, and even lead to data being exposed to the internet. When you leverage several Lambda functions, ensuring all the different configurations are set up appropriately can be a tedious task and in most cases, requires continuous monitoring.

CloudHealth Secure State is an intelligent cloud security solution that provides near real-time alerting, auto-remediation, and compliance monitoring across a broad range of frameworks including PCI, HIPPA, GDPR, and many more. It also allows you to build custom rules and frameworks across AWS, Microsoft Azure, and Google Cloud. See our technical report to learn more: Mitigating Security and Compliance Risks with CloudHealth Secure State

Technical Report
Mitigating Security and Compliance Risks with CloudHealth Secure State

Conclusion 

AWS Lambda functions are critical pieces of modern applications because of their ability to scale and respond to events. Optimizing them involves collaboration between Developers and Cloud Operations teams across cloud operations, finance, and security and compliance.

CloudHealth provides all the tools necessary to enable continuous optimization and provides the best of both worlds with highly customizable sets of pre-built reports and dashboards, with an ever-growing set of APIs to incorporate into your CI/CD pipelines.

You can learn more about how to optimize your cloud environment across cloud operations, finance, and security in our whitepaper: Benchmark Your Cloud Maturity: A Framework for Best Practices

Image
profile photo of Shrivatsa Upadhye
Shrivatsa Upadhye, Senior Technical Marketing Architect

Shrivatsa Upadhye is an experienced Cloud Architect and Technical Product Marketer with Pre-Sales skills, focused on application architecture and security in public clouds like AWS, Azure, and GCP. Currently, he is a Senior Technical Marketing Architect at CloudHealth by VMware. He works closely with customers to connect market and technology trends to influence business and engineering decisions. He holds a Master’s in Electrical and Computer Engineering from The University of Arizona.

We Think You Might Like These: