Skip to content

Instantly share code, notes, and snippets.

@lobster1234
Last active January 23, 2024 21:18
Show Gist options
  • Save lobster1234/201fb83dc2847a1e2a106a098636bc1f to your computer and use it in GitHub Desktop.
Save lobster1234/201fb83dc2847a1e2a106a098636bc1f to your computer and use it in GitHub Desktop.
Tutorial for running the templated maven-java serverless project using the serverless framework.

Working with Serverless and Java - Part 1

In this tutorial, we will create and deploy a java-maven based serverless service using the serverless project (https://serverless.com/). In this part we will not modify any code, or even look at the generated code. We will focus on the deployment and the command line interface provided by serverless. Serverless is a node.js based framework that makes creating, deploying, and managing serverless functions a breeze. We will use AWS as our FaaS (Function-as-a-Service) provider.

Pre-requisites

Here is what the setup on my Mac looks like (Sierra)

  • brew (1.1.10) - you will need this if you do not have node/npm installed already.
  • node (v7.6.0)
  • npm (4.1.2)
  • Apache Maven (3.2.5)
  • Oracle JDK (1.8.0_121)

Install Serverless

If you do not have node installed, use brew install node to install node and npm.

Manishs-MacBook-Pro:~ mpandit$ npm install serverless -g
/usr/local/bin/serverless -> /usr/local/lib/node_modules/serverless/bin/serverless
/usr/local/bin/slss -> /usr/local/lib/node_modules/serverless/bin/serverless
/usr/local/bin/sls -> /usr/local/lib/node_modules/serverless/bin/serverless

> [email protected] postinstall /usr/local/lib/node_modules/serverless
> node ./scripts/postinstall.js

Configure Serverless for AWS

Serverless needs to use AWS credentials for an IAM user. I recommend creating a user with no access, and we can add permissions as we go.

We create a programmatic-access-only user called serverless, and added it to a new group called Serverless. The group should have no permissions associated with it.

Next, copy paste the AWS credentials of this user in the command below.

Manishs-MacBook-Pro:serverless-java mpandit$ serverless config credentials --provider aws --key <key> --secret <secret>

Serverless: Setting up AWS...
Serverless: Saving your AWS profile in "~/.aws/credentials"...
Serverless: Success! Your AWS access keys were stored under the "default" profile.

Serverless uses us-east-1 region by default, which can be overriden by providing the --region parameter.

Create the Java project

Manishs-MacBook-Pro:~ mpandit$ cd ~/work
Manishs-MacBook-Pro:work mpandit$ mkdir serverless-java
Manishs-MacBook-Pro:work mpandit$ cd serverless-java
Manishs-MacBook-Pro:serverless-java mpandit$ serverless create --template aws-java-maven

Serverless: Generating boilerplate...
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.7.0
 -------'

Serverless: Successfully generated boilerplate for template: "aws-java-maven"
Serverless: NOTE: Please update the "service" property in serverless.yml with your service name

Set up IAM Policies

If you run serverless info, it will report an error as our user does not really have any permissions.

Manishs-MacBook-Pro:serverless-java mpandit$ serverless info

  Serverless Error ---------------------------------------

     User: arn:aws:iam::123456789209:user/serverless is not
     authorized to perform: cloudformation:DescribeStacks
     on resource: arn:aws:cloudformation:us-east-1:123456789209:stack/aws-java-maven-dev/*

  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues

  Your Environment Information -----------------------------
     OS:                 darwin
     Node Version:       7.6.0
     Serverless Version: 1.7.0

Lets add CloudFormation policy to this user. We will do this via adding the permission to the associated group.

You will notice that there is no such thing as AmazonCloudFormationFullAccess. Hence, we will need to create a custom policy.

Click the Group name on the IAM console, and click the Permissions tab. Expand Inline Policies, and click the link that allows creation of an inline policy.

In the page that opens up, select Policy Generator and proceed and make these selections -

  • Effect : Allow
  • AWS Service: AWS CloudFormation
  • Actions - All Actions Selected
  • ARN - *

Click Add Statement, and click Next Step. This will show the policy we just created. Rename it to serverless-cloudformation-policy. Click Apply.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1488265872000",
            "Effect": "Allow",
            "Action": [
                "cloudformation:*"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

We will also need to give S3 access to this user, as serverless would need to upload the artifact to S3 for deployment via CloudFormation. We now add AmazonS3FullAccess to this group, and our user will inherit it. This time we will click Attach Policy under Managed Policies instead of Inline Policies.

Another Managed Policy is for CloudWatch Logs access, so we will select CloudWatchLogsFullAccess and attach to the group.

As a part of the deployment process, serverless would need to associate the lambda with the invocation role. In order to do that, attach IAMFullAccess policy from the Managed Policies to the group as well.

Finally, we add AWSLambdaFullAccess to this group so the serverless framework can manage the services (lambda functions).

To summarize, our Serverless IAM Group should have following policies -

Managed -

  • AmazonS3FullAccess
  • CloudWatchLogsFullAccess
  • IAMFullAccess
  • AWSLambdaFullAccess

Inline - serverless-cloudformation-policy for CloudFormation.

One thing to keep in mind is that for functions that are triggered by an event, the event source would need access to invoke the function. This is managed via function policies, which are set up when the trigger is configured. For instance, if you want the lambda to be triggered on an S3 event on a bucket, the association between the S3 bucket and the function will need to have a function policy.

Now you'll notice that the serverless info command returns no services.

Manishs-MacBook-Pro:serverless-java mpandit$ serverless info

  Serverless Error ---------------------------------------

     Stack with id aws-java-maven-dev does not exist

  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues

  Your Environment Information -----------------------------
     OS:                 darwin
     Node Version:       7.6.0
     Serverless Version: 1.7.0

Deploy the stack

Since deployment of the stack is essentially setting up our lambda function (the template has a simple function), we need to create the artifact (jar).

Since this is a maven project, we use maven to build the project.

Manishs-MacBook-Pro:serverless-java mpandit$ mvn clean install
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building hello dev
[INFO] ------------------------------------------------------------------------
...
...
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 14.709 s
[INFO] Finished at: 2017-02-27T23:26:00-08:00
[INFO] Final Memory: 23M/145M
[INFO] ------------------------------------------------------------------------

Now that we have the artifact in target folder (hello-dev.jar), we can go ahead deploy it.

Manishs-MacBook-Pro:serverless-java mpandit$ serverless deploy
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading service .zip file to S3 (1.98 MB)...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..................
Serverless: Stack update finished...
Service Information
service: aws-java-maven
stage: dev
region: us-east-1
api keys:
  None
endpoints:
  None
functions:
  aws-java-maven-dev-hello

You can check the AWS CloudFormation section in the console to view details of the stack that has just been created.

Now that our service is deployed, it wil show up under serverless info

Manishs-MacBook-Pro:serverless-java mpandit$ serverless info
Service Information
service: aws-java-maven
stage: dev
region: us-east-1
api keys:
  None
endpoints:
  None
functions:
  aws-java-maven-dev-hello

Next, lets try to invoke it.

Manishs-MacBook-Pro:serverless-java mpandit$ serverless invoke --function  hello
{
    "statusCode": 200,
    "body": "{\"message\":\"Go Serverless v1.x! Your function executed successfully!\",\"input\":{}}",
    "headers": {
        "X-Powered-By": "AWS Lambda & serverless"
    },
    "isBase64Encoded": false
}

The function can also be invoked with an input. The input is essentially a Map, so we can pass it a JSON string with the --data parameter.

Manishs-MacBook-Pro:serverless-java mpandit$ serverless invoke --function hello --data '{"key":"value"}'
{
    "statusCode": 200,
    "body": "{\"message\":\"Go Serverless v1.x! Your function executed successfully!\",\"input\":{\"key\":\"value\"}}",
    "headers": {
        "X-Powered-By": "AWS Lambda & serverless"
    },
    "isBase64Encoded": false
}

A good idea would be to explore the CloudWatch Logs and Lambda metrics for our function from the console.

The same can be done command line, like so -

Manishs-MacBook-Pro:serverless-java mpandit$ serverless logs --function hello
START RequestId: f8390071-fd8d-11e6-9195-554889504121 Version: $LATEST
2017-02-28 08:14:48 <f8390071-fd8d-11e6-9195-554889504121> INFO  com.serverless.Handler:17 - received: {}
END RequestId: f8390071-fd8d-11e6-9195-554889504121
REPORT RequestId: f8390071-fd8d-11e6-9195-554889504121	Duration: 445.52 ms	Billed Duration: 500 ms 	Memory Size: 1024 MB	Max Memory Used: 57 MB

We can also look at the metrics associated with our functions (The 1 error is because I had passed a malformed JSON input).

Manishs-MacBook-Pro:serverless-java mpandit$ serverless metrics
Service wide metrics
February 27, 2017 1:59 AM - February 28, 2017 1:59 AM

Invocations: 7 
Throttles: 0 
Errors: 1 
Duration (avg.): 276.55ms
Manishs-MacBook-Pro:serverless-java mpandit$ serverless metrics --function hello
hello
February 27, 2017 1:59 AM - February 28, 2017 1:59 AM

Invocations: 7 
Throttles: 0 
Errors: 1 
Duration (avg.): 276.55ms

Now that we have successfully deployed and tested the templated service, we are now ready to dive deeper into the actual code, configuration, and constructs of serverless framework. That will be Part-2 of this tutorial.

@jamestharpe
Copy link

Any steps for supporting sls invoke local? When I follow the steps in this article, then try to invoke locally, I get an error:

Serverless: In order to get human-readable output, please implement "toString()" method on your "ApiGatewayResponse" object.
Error: Unable to access jarfile c:\Users\jt\AppData\Roaming\npm\node_modules\serverless\lib\plugins\aws\invokeLocal\java\target\invoke-bridge-1.0.jar

Thanks,
-James

@smitkothale
Copy link

Go to the path c:\Users\jt\AppData\Roaming\npm\node_modules\serverless\lib\plugins\aws\invokeLocal\java
and run mvn install to create the target directory and download the jar files

@jconlon
Copy link

jconlon commented Jul 27, 2018

Great example. When will you do a part 2?

@immahesh1
Copy link

Very helpful....great!!!

@JaspreetChhabra
Copy link

After running the sls invoke local --function functionName command I am getting the following error

Error: Artifact undefined doesn't exists, please compile it first.
at /usr/local/lib/node_modules/serverless/lib/plugins/aws/invokeLocal/index.js:647:21

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment