Go Serverless - Run Spring Cloud Function App on AWS Lambda with Serverless Framework and Gitlab Deploy
Spring Cloud Function App, AWS Lambda, Serverless Framework, Gitlab CI CD (Images taken from internet, official logos)

Go Serverless - Run Spring Cloud Function App on AWS Lambda with Serverless Framework and Gitlab Deploy

Source code kept here:

Objective:

  • I want to develop a simple Spring Cloud Function App
  • I want to use Serverless framework to deploy
  • I want to use Gitlab to build and deploy
  • I want deploy to AWS lambda

Who this is for:

  • This can act as starting point for the developers, cloud engineers who is interested in Serverless platforms.
  • Can be useful for people who already understands spring want to make use of their pre-existing spring knowledge to develop and deploy Spring Function.

Background:

Four things involved here:

  1. Spring Cloud Function
  2. AWS lambda
  3. Serverless Framework
  4. Gitlab CI


Spring Cloud Function

Let me give a quick summary.

  • Spring Cloud Function is a new thing in Spring Eco System to promote FaaS. i.e. Function as a service.
  • AWS lambda is one such platform capable of running just one single function.
  • It is on-demand invocation i.e. it won't run until it gets a trigger.
  • Spring Cloud Function can be used for writing small functions or a full fledged Rest API. It has all the capabilities of a traditional Spring Boot features like (auto-configuration, dependency injection, metrics etc)
  • However, the way we develop Spring Cloud Functions are bit different than traditional Spring Boot API. For one, there is no controller. All we have are functions expressed as beans.

Read through this to understand more on it.


AWS lambda:

As stated earlier it is an AWS offering to support FaaS i.e. Function as a Service.


Serverless Framework

  • A Framework specifically developed to handle build and deployment of Functions
  • It supports functions developed on all the major programming languages like, Go, Python, java, Node http
  • It supports, deployment to major cloud platforms like AWS, Azure and GCP.

Read through this to learn more on the topic:


Gitlab CI CD:

  • A CI CD platform, to manage the build and deployment of your application.
  • Similar to GitHub Actions.


Steps to take:

We will do the below in the series of steps:

  1. Build the Spring Cloud Function App locally
  2. Run locally.
  3. Build and deploy to AWS lambda using Serverless Framework
  4. Run from Cloud
  5. Do the same from Gitlab CI. i.e. Build, deploy.

Let's start.


First Step: Create the Spring Cloud Function App

  • Use Spring Initializer to create a maven Spring Cloud Function

No alt text provided for this image

Add dependencies

  1. Spring Cloud Function
  2. Spring Web

Spring Version: 3.0.4

Java version: 17


  • Your POM.xml file should have these two dependencies:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-function-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-function-adapter-aws</artifactId>
        </dependency>
        

Notice there is a third dependency i.e. spring-cloud-function-adapter-aws

  • This dependency is what is required to run the app in AWS lambda. If you are using other serverless platform i.e. Azure or GCP then this will have to be changed accordingly. We will keep it like this for now since we are doing this on AWS lambda.
  • Add three functions in the class carrying the main class and mark those function as '@Bean'. By the way, you can create another class and add these methods there. or you can also have a package called "function" and add classes and methods there. More on this in the documentation, but for simplicities sake lets keep it in the main class itself

@Bean
public Supplier<String> hello(){
    return ()-> "Hello World";
}

@Bean
public Function<String, String> helloName() {
    return (name)-> "Hello '" + name + "'";
}

@Bean
public Consumer<String> helloJustLog() {
    return (name)-> log.info("Hello '" + name + "'");
}        

  • The?spring-cloud-function-web?module has autoconfiguration that activates when it is included in a Spring Boot web application (with MVC support).
  • Bean annotation, is a marker that these functions are to be automatically converted in to rest end points.
  • What's important here is the return type of these methods. Spring Cloud function app utilizes these functional interface available since the Java 8
  • ??Supplier<T> : In java a supplier Returns something. So, automatically gets mapped to "GET" request.
  • ??Consumer<T>: As name suggests, Consumes things. So, automatically gets mapped to a "POST" request.
  • ??Function<I,O>: Does both, accepts and returns, can either be GET or POST or other.
  • This auto-configuration happen automatically by spring mvc. Generally below rules are followed:

No alt text provided for this image

  • There is lots of configuration we can do here but for now lets stick to just these three functions and using the defaults. For more, check the documentation link.

  • Well thats it for the app. You can run it normally in your IDE just like the way you run your spring boot app. Then use curl to hit the end points. Notice name of the method act as the rest end point.

curl -X GET https://localhost:8084/hello
Hello World
curl -X GET https://localhost:8084/helloName/Akash
Hello 'Akash'                                                                                                                                                                                             
curl -X GET https://localhost:8084/helloJustLog/Akash        


Next Step: Create a Shaded jar

  • To run this app on a AWS lambda you need to create a shaded-jar which losely means that this jar need to have all the transitive and run time dependencies already present in the jar. A fat jar in a nutshell.
  • To do this, you would need to have a maven shaded plugin added in your pom.xml file. It would look something like below:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>3.2.4</version>
  <configuration>
      <finalName>app-aws-lambda</finalName>
      <createDependencyReducedPom>false</createDependencyReducedPom>
      <shadedArtifactAttached>true</shadedArtifactAttached>
      <shadedClassifierName>aws</shadedClassifierName>
  </configuration>
</plugin>
        

  • Notice the finalName tag. This will be name of the fat jar which you would need to upload to lambda later.

No alt text provided for this image


  • After you run 'mvn clean install', under target folder, app-aws-lambda.jar will be created, which needs to be uploaded to AWS lambda later.







Next Step: Use Serverless Framework to deploy the app in AWS Lambda

  • How do you upload this Jar on AWS lambda? Well you can upload this manually but thats not what we are looking to do at the moment. Having said that, it's good to try to upload this manually to AWS lambda first. Just go to the AWS console and go to AWS lambda and follow the steps and see if its working before you proceed further with this tutorial.
  • We are going to use Serverless Framework to automatically create a Lambda function.
  • What is a Serverless framework? It is a tool, framework what ever you wish to call it, which makes deployment to Serverless cloud platforms easy. Its by default supports AWS Lambda but also works equally with other platforms like Azure or GCP which supports serverless as well.
  • To start with serverless, you need to first install it locally. All I need to do is run below command. Offcourse you need to have npm, node etc pre installed.

npm install -g serverless        

  • Check below link for full installation details:

  • Next step, is to generate the serverless template. Since, I need java platform to run my spring cloud function, I will use below command and use any pre-existing template.

serverless create --help        

  • This will show you list of templates, select 'aws-java-maven'.
  • Your command would look like:

serverless create --template aws-java-maven        
No alt text provided for this image



  • This will create a template serverless.yml file with lot of default values and possible tags. We are not going to be needing all of that, but it gives a good enough view on what are the things we can configure.





  • Serverless.yml internally creates AWS cloud formation templates, to configure S3 bucket, permissions, url, api gateway, load balancer etc, Cloud watch etc.
  • Serverless.yml makes things easy for us to manage the AWS infra since Cloud formation templates could very quickly becomes quite daunting.
  • What we need for now, is just one serverless.yml file added in the root of our project with below text. I have explained the tags and values in the yml itself

service: my-first-serverless-fw-project

frameworkVersion: '3'

provider:
  name: aws # since we are on AWS
  runtime: java11 # we want the run time to be java 11

package:
  artifact: target/app-aws-lambda.jar # place name of the shaded jar here after mvn clean install
functions:
  cloud-app:
    url: true # a Public URL will be automatically created
    handler: org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest
    # Handler is coming from spring-cloud-function-adapter-aws maven pom dependency
    # This always remains the same and acts as main method/starting point of the Lambda function        

  • Handler is coming from spring-cloud-function-adapter-aws dependencies in maven
  • This handler always remains the same and acts as main method/starting point of the Lambda function
  • Also, before we proceed to deploy this to AWS Lambda from your local machine, you need to have AWS_ACCESS_KEY and AWS_SECRET_KEY already configured in your machine. If you do not know how to do that... Well just google it.
  • Once, you have the AWS access key and aws secret key configured, you should just run:

serverless deploy        

  • Serverless framework will then just start the creation process etc. It takes around 2-3 mins to create and upload your Lambda function.

No alt text provided for this image

  • Cloud formation stack is being created.



No alt text provided for this image

Since in "serverless.yml" we marked url: true and hence serverless fw created a public url for our function.


  • This is how it looks in your AWS Console.
  • Function URL is same what serverless displayed in the console earlier. The url is public that is accessible from any where but this can changed to aws_iam to prvide authN and authZ feature to it.

No alt text provided for this image

  • Now lets test this with Curl.

# invoke the hello GET end point
curl --header 'spring.cloud.function.definition: hello'? https://vkj52pn2crl2plojx75wnnj4zy0mkyqt.lambda-url.us-east-1.on.aws/
# invoke the helloName POST end point
curl --request POST --header 'spring.cloud.function.definition: helloName' --data 'Akash' https://vkj52pn2crl2plojx75wnnj4zy0mkyqt.lambda-url.us-east-1.on.aws/        

  • Noticed something? The way we are calling the end points are not how you would usually do i.e. <host>:<port>/endpoint.
  • Rather, you use header to enter the function name under the property, 'spring.cloud.function.definition' and this will act as our end point call.
  • The return type of the methods, Supplier/Function/Consumer would determine if this would be a GET or POST. Check the table above how it translates the end point http verb.
  • You can compose multiple end point in the same call. something like 'spring.cloud.function.defintion:foo,bar'. But this is not I have covered here in this example.


Next Step: Use Gitlab to automatically deploy the app via CI CD pipeline

  • Once you have learned to deploy this from local machine, you can move on to any CI engine like git hub actions or git lab to deploy the app with serverless.yml.
  • For this, I will use git lab.
  • Git lab ci yml would like below:

image: node:latest

stages:
  - build
  - deploy

mvn-build:
  image:  maven:3.8.6-jdk-11-slim
  stage: build
  script:
    - mvn clean install -DskipTests
  artifacts:
    paths:
      - target/app-aws-lambda.jar
    expire_in: 1 week

deploy-aws:
  stage: deploy
  dependencies:
    - mvn-build
  before_script:
    - npm config set prefix /usr/local
    - npm install -g serverless
  script:
    - serverless deploy --stage production --verbose
  environment: production
        

Full link here of the source code here:

  • Its straight forward. Lets first quickly understand the lay out of this file.
  • You start with the base image as node:latest. This acts as an agent in Gitlab inside which your jobs will run.
  • Then I have mentioned, stages. build and deploy. Build is for building and deploy is for deploying on AWS. Stages in Gitlab, run in sequence.
  • Then we have two jobs, mvn-build and deploy-aws. mvn-build job is linked to build stage. and deploy-aws job is linked with deploy stage. This is done using the tag 'stage' under job name.
  • In gitlab, you can have multiple jobs pointing to one stage. All the jobs in a stage run in parallel. However, we have two jobs linked to two different stages. So they run in sequence. Moreover, there is a dependency also mentioned here between jobs.
  • Then, I am first, building the maven project using mvn clean install. This will create my shaded aws jar.
  • Then I will upload this jar using artefact, this will be accessible to my next job i.e. deploy aws.
  • I still do not have serverless frame work here so I have to first install it under before script using npm install, exactly the same way you do it on your local.
  • In the script part, all I do is the same, that is serverless deploy. Alternatively, I can use stage tag in serverless to mark my deployment environments.
  • With out the stage, it gets treated as dev environment.

No alt text provided for this image
No alt text provided for this image

Last thing for this to work, you need to have AWS_ACCESS_KEY and AWS_SECRET_KEY set up in your Gitlab CI variable.

No alt text provided for this image

Next Steps: Clean Up!

To clean up Use below:

> serverless remove
or
> serverless remove --stage production ==> if you hvae used stage parameter to deploy, I have used it in my Gitlab CI CD        


Thats all for now! try to play around with this. There is a lot you can build using Spring Cloud Function and a lot you can configure with Serverless framework. Keep trying and let me know your experiences.

Florent Bonamis

Senior Java software engineer and mentor

1 年

Very good who explains all step by step

回复

要查看或添加评论,请登录

Akash Tyagi的更多文章

社区洞察

其他会员也浏览了