AWS Lambda with Serverless Framework and Java/Maven - Part 1
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, although Serverless supports IBM OpenWhisk and Microsoft Azure as well.
In this tutorial, we will create and deploy a java-maven based AWS Lambda function. In Part-1 we will not modify any code, or even look at the generated code. We will focus on the deployment and the command line interface to manage lambda, provided out of the box by serverless framework.
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
> serverless@1.7.0 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.