Spring Boot Rest API deployed in AWS Lambda (Serverless) Git lab CI/CD
Just to begin AWS Lambda is server less architecture, which allows you to run the code without managing the server and cost is pay per use, usually its based on number of invocation and execution time.
Usually lambda is preferred for small task or to have platform specific trigger. But when we need to maintain the entire application it becomes tedious, For example if we do it via console, First we need to create the function and upload the code to S3, and set up the HTTP gateway trigger. so each time when you update the code it becomes very difficult and time consuming, just imagine for 50 URLs of a project. so when we are starting with lambda Function or any server less design, it better to start with Automated deployment as bare minimum instead of spending most of the time in deploying or playing inside the Cloud console.
So when develop any hobby project, minimum CI & CD pipelines is mandatory for me. And I prefer GitLab as my CI tool. So this preference led me to find easier ways to deploy the the java application in to AWS lambda. So found these tools Micronaut as web framework, server less Framework for managing deployment and GitLab for CI & CD.
I am an spring boot advocate, I use spring boot in most of my applications, so this example is from on of my experiments so lets begin this example
I have used few additional tools to make my life easier to manage my lambda function Those are
- Serverless framework is Open Source, lets you develop and deploy serverless applications to AWS, Azure, GCP & more. and it has very good documentation to understand to begin with.
- GIT Lab CI/CD As this is integrated with my repo provider so I did not to use another CI tool.
- And used this dependency in my pom
<dependency>
<groupId>com.amazonaws.serverless</groupId>
<artifactId>aws-serverless-java-container-springboot2</artifactId>
<version>1.5.2</version>
</dependency>
Step 1: Create the Dummy controller or two in Spring boot (Latest version this was working is 2.2.6.RELEASE)
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/health")
public class HealthController {
@GetMapping("/check")
public HealthTO main() {
return new HealthTO();
}
}
Step 2 : Lets us create Entry point which can handle the request from AWS lambda This is main Application Class.
@SpringBootApplication
@EnableWebMvc
public class EntryPoint implements RequestStreamHandler {
private static final SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
static {
try {
handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(EntryPoint.class);
} catch (ContainerInitializationException e) {
// if we fail here. We re-throw the exception to force another cold start
e.printStackTrace();
throw new RuntimeException("Could not initialize Spring Boot application", e);
}
}
public static void main(String[] args) {
SpringApplication.run(EntryPoint.class, args);
}
@Bean
public HandlerMapping handlerMapping() {
return new RequestMappingHandlerMapping();
}
/*
* Create required HandlerAdapter, to avoid several default HandlerAdapter instances being created
*/
@Bean
public HandlerAdapter handlerAdapter() {
return new RequestMappingHandlerAdapter();
}
@Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
handler.proxyStream(inputStream, outputStream, context);
}
}
To optimise the cold start up time by manually using ComponentScan
annotations for required components. and also using `@import` instead of `Autowired`
Step 3 Changing Build mechanism
Here i have attached the pom, instead of using spring boot maven plugin, we are using shade plugin to remove tomcat from the dependency
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ashrithgn.example</groupId>
<artifactId>service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>app_name_service</name>
<description>Handels gooles auth</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws.serverless</groupId>
<artifactId>aws-serverless-java-container-springboot2</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>app_name</finalName>
<plugins>
<!--<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
<exclude>
<groupId>org.apache.tomcat.embed</groupId>>
</exclude>
</excludes>
</configuration>
</plugin>-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<excludes>
<exclude>org.apache.tomcat.embed:*</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
And package the application as jar upload the jar in AWS lambda manually or use serverless to manage the lambda and create the API gateway.
Step 4: Setting up Serverless Framework
Installing the serverless (on ubuntu)
sudo apt install nodejs
sudo apt install npm
sudo npm install -g serverless
Preparing serverless.yaml
This is yaml file more or like steps and rules for the deployment. create in the root of the project, or the place where ever the artifacts are accessible.
service: <project-name> # provide the project name
provider:
name: aws
runtime: java8
package:
artifact: <path of the jar file> # provide path of the jar file
functions:
get_news_categories:
handler: <package-name>.EntryPoint
events:
- http:
path: health/check
method: GET # Http metodod [GET,POST,etc]
cors: true # allows cors
Adding Aws Credentials to system environment variable
export AWS_ACCESS_KEY_ID=<VALUE>
export AWS_SECRET_ACCESS_KEY=<****************>
Make sure the AWs use has these permission policy IAMFullAccess
, AmazonS3FullAccess
, CloudWatchLogsFullAccess
, AmazonAPIGatewayAdministrator
, AWSCloudFormationFullAccess
, AWSLambda_FullAccess
.
CI & CD : I am using the the GitLab CI, instead u can use any CI tool. For build pipeline use `mvn clean package shade:shade` to generate the artifact. to deploy deploy u can use `serverless deploy --stage production --verbose`.
Git lab CI & CD example
image: node:latest
stages:
- package
- deploy
mvn_build:
stage: package
image: maven:3-jdk-11
script: "mvn package shade:shade"
artifacts:
paths:
- target/<jarname>.jar
production:
stage: deploy
before_script:
- npm config set prefix /usr/local
- npm install -g serverless
script:
- serverless deploy --stage production --verbose
environment: production
Also :