4 minutes read

POSTED Mar, 2021 dot IN Serverless

Migrating Spring Boot Applications to AWS Lambda with No Code Change

Oguzhan Gencel

Written by Oguzhan Gencel


Software Developer @Thundra

 X

Migrating Spring Boot Applications to AWS Lambda with No Code Change

According to a survey conducted by Jetbrains, Spring Boot is the top Java framework for developing web applications. Spring Boot applications are usually deployed on a dedicated server and/or container via necessary orchestration. Normally, this inevitably includes the work of managing the infrastructure and scalability of the application. Deploying Spring Boot applications on AWS Lambda can be an alternative with comparatively less time spent on operational aspects of maintaining a Spring Boot application. Thanks to the pay-per-use nature and highly lessened operational burden of AWS Lambda, application teams can have many advantages when they migrate their existing Spring Boot applications to AWS Lambda. However, developers have hesitations about switching to AWS Lambda because of cold starts and other limitations of AWS Lambda. In this blog post, we will eliminate some common myths and prove that migrating your Spring Boot application to AWS Lambda will not degrade (and will instead boost) the performance of your applications.  

(So-Called) Problems Associated with Lambda

The first issue is the need to make Spring Boot applications event-driven. When an AWS Lambda function is invoked, it sends a request object to the handler. However, Spring Boot cannot handle this request, so we need to change our app. To solve this problem, AWS has published a library that enables developers to create a custom handler (follow this tutorial). Thundra’s agent has an in-built HTTP API handler for your Spring Boot 2 application. So, if you add Thundra to your application, you don’t need to change your code at all. That means you can use your current Spring Boot application as-is without any modifications on AWS Lambda.

The second issue is cold starts. It’s a general problem of serverless, but its impact intensifies with Spring Boot applications due to a long initialization time: AWS Lambda has a 10-second initialization duration limit for cold start, but some complex Spring Boot applications take more than 10 seconds during the start-up process. Fortunately, with Thundra’s custom Java runtime, enabling the thundra_agent_lambda_jvm_optimizeForFastStartup environment variable helps to decrease your Spring Boot AWS Lambda app initialization duration by 50%. You can set  the thundra_agent_lambda_jvm_optimizeForFastStartup environment variable as true to achieve a fast start-up and less of a cold start overhead. Of note, your application can still have problems getting initialized in 10 seconds while loading dependencies. But good news: Thundra has another layer of  built-in solutions for this issue. If you set the thundra_agent_lambda_container_springboot_app_initasync environment variable as true, Thundra initializes the application asynchronously without blocking Lambda initialization, but assures that initialization is completed before handling the request.

The most frequently asked question we receive is about the monitoring overhead for applications like Thundra APM. With its collectors on every AWS region, Thundra’s agent adds (at most) 5 ms latency when it reports monitoring data in synchronous mode. However, there’s still an overhead associated with loading security-related JDK classes, due to security components being initialized because of initial HTTPS handshake at cold start invocations. As a result, there will be 1-2 seconds of cold start overhead. If you are curious about this topic, you may find our blog post on this subject interesting. To get rid of this initialization overhead and network delay at each invocation, we also support asynchronous monitoring for ZER0 overhead. To enable asynchronous monitoring, you can set the thundra_agent_lambda_report_cloudwatch_enable  environment variable as true. In this mode, reporting data will be done separately from function execution and saves you some more time.

Want to subscribe to our newsletter?

Thanks for your interest. You have been subscribed!

Benchmark with a PetClinic Application

In this blog post, we are using the infamous Spring Boot Pet Clinic application with the Spring Boot 2.1.11 version. This application has four endpoints; three use DynamoDB with Spring Data. The other serves to make an HTTP request to an external web service with Apache HttpClient. When the application starts, the Spring Data DynamoDB sync application models with a remote database. The sample application looks like a microservice app that contains real-world cases. Of course, our application is monitored by Thundra APM.

For this benchmark, we have three main scenarios, depending on the deployment method and Thundra configuration. Each deployment method has four memory configurations. In total, we have 12 different test cases. For each test case, we run the same invocation seven times and take into consideration the best six results for comparison.

In the scope of our tests, we have used two different modes of deployment.

Zipped Spring Boot Fat Jar

  • Create a fat (bootable) jar by following this tutorial using Spring Boot Maven or Gradle plugin.
  • Upload the generated jar to AWS Lambda in the zip archive.
  • Add the Thundra layer by following this doc.
  • Set Spring Boot fat jar name in the thundra_agent_lambda_container_springboot_app_artifact environment variable.

Docker Image

Follow this tutorial to create and publish a docker image based on the Lambda function.

Benchmarks

The graph shows three test cases:

  1. The first case used AWS Serverless Java Container - Spring Boot 2 library to expose Spring Web Rest endpoints as the Lambda handler (API Gateway request -> Lambda handler -> AWS Serverless Java Container - Spring Boot 2-> Spring Web Rest Controller) and didn’t include any Thundra components. It was deployed with AWS SAM CLI tools without the Spring Boot build plugin.
  2. For the second test case, the application was bundled (zipped) as a fat (bootable) jar with the Spring Boot Gradle plugin and uploaded to AWS Lambda as a zip archive. Then, the Thundra Java layer was added and added the following configurations:
  3. In the third test case, the application was packaged as a Docker Image and published to Lambda AWS ECR to be used by AWS Lambda. Next, the following configurations are applied:
  • Add Thundra’s API key.
  • Set the thundra_agent_lambda_container_springboot_app_artifact environment variable with the application jar name (e.g., petlinic.jar).
  • Set the thundra_agent_lambda_jvm_optimizeForFastStartup environment variable to true so the Lambda runtime JVM process will be started with configured VM arguments optimized for fast start-up (see docs).
  • Set the thundra_agent_lambda_report_cloudwatch_enable environment variable totrue so collected telemetry data will be reported asynchronously over AWS CloudWatch logs without any network overhead (see docs).
  • Set runtime to Custom Runtime. 
  • Set handler to io.thundra.agent.container.springboot2.ContainerHandler .
  • Set the thundra_agent_lambda_container_springboot_app_artifact environment variable with the application jar name (e.g., petlinic.jar).
  • Set the thundra_agent_lambda_jvm_optimizeForFastStartup environment variable to true so Lambda runtime JVM process will be started with configured VM arguments optimized for fast start-up.
  • Set the thundra_agent_lambda_report_cloudwatch_enable environment variable to true so collected telemetry data will be reported asynchronously over AWS CloudWatch logs without any network overhead.

As seen from the benchmark results, Thundra boosts application initialization duration up to 30% and produces highly informative distributed traces automatically.

Also, according to our findings, AWS Lambda Docker images have some overhead (1-2 seconds lost) before our runtime starts.  If you want to use your Docker image in Lambda but don’t want to accept the cold start, you can read another Thundra solution for dealing with cold start.

Wrapping Up

Millions of developers all around the world pick Spring Boot as an easy way of developing web applications with Java runtime. However, they also have to deal with the operational aspect of those applications because of the infrastructure the applications run on. AWS Lambda removes the operational burden of application teams due to lessened operational tasks. Moreover, there are cost advantages due to the pay-per-use nature of AWS Lambda and from the total cost of ownership perspective. Thundra makes it possible to migrate existing VM or containerized applications to AWS Lambda platforms without need for any modification. So, with the help of Thundra, lift-and-shift to serverless is very straightforward: Just upload and add Thundra. That’s all!

Get started with Thundra APM today and don’t forget to have a look at Thundra Sidekick!