My colleague were just discussing about the new architecture serverless most of the time and out of curiosity I fiddle around with this term and learned what this really is? Then I found out that it is just a way to execute your normal functions and from URL and you do not need your server to be online all the time which results in super cost effective unlike normal server where you pay for every month/hour/second of its availability. The term looks super easy to understand. Execute my function foo()
when https://mydomain.com/foo
is called via HTTP protocol. The term serverless without any server is just for the developers but beneath this architecture there will still be a fully managed server who handles your request and warms up your code (jar file). So I came to think of demonstrating how to run your minimal functions as serverless . But before that lets learn some basics via image.
Long thing in short : If any POST request in /foo
URI happen the Function handler will call to the appropriate function of a class which is predefined in serverless.yml
. We developers do not have to worry about all those API endpoints handling because these are handled by serverless itself, this is the beauty of serverless. In an Amazon Web Service world serverless is handled from AWS Lambda service.
Now it is the time to catchup and write our very first and simple serverless project.
Requirements
Create a new serverless maven project
1 |
serverless create --template aws-java-maven --name serverless-example --path serverless-example |
Edit some codes inside Handler.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public class Handler implements RequestHandler<Map<String, Object>, ApiGatewayResponse> { private static final Logger LOG = LogManager.getLogger(Handler.class); @Override public ApiGatewayResponse handleRequest(Map<String, Object> input, Context context) { LOG.info("received: {}", input); Response responseBody = new Response("Hello this is just a /foo [POST] response ", input); return ApiGatewayResponse.builder() .setStatusCode(200) .setObjectBody(responseBody) .setHeaders(Collections.singletonMap("X-Powered-By", "AWS Lambda & serverless")) .build(); } public ApiGatewayResponse barRequest(Map<String, Object> input, Context context) { LOG.info("received: {}", input); Response responseBody = new Response("Hello this is just a /bar response ", input); return ApiGatewayResponse.builder() .setStatusCode(200) .setObjectBody(responseBody) .setHeaders(Collections.singletonMap("X-Powered-By", "AWS Lambda & serverless")) .build(); } public ApiGatewayResponse infoRequest(Map<String, Object> input, Context context) { LOG.info("received: {}", input); Response responseBody = new Response("Hello this is just a /info response ", input); return ApiGatewayResponse.builder() .setStatusCode(200) .setObjectBody(responseBody) .setHeaders(Collections.singletonMap("X-Powered-By", "AWS Lambda & serverless")) .build(); } } |
You can directly override RequestHandler
‘s handleRequest()
method/function or you can create just simple method which is also supported for serverless function execution. The only difference is you have to use fully qualified Classname as well as the method name while mapping it to the URI in serverless.yml
Now there is one final piece of puzzle which is serverless.yml
which will do the mappings of all functions to uri .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
service: serverless-example # NOTE: update this with your service name # You can pin your service to only deploy with a specific Serverless version # Check out our docs for more details # frameworkVersion: "=X.X.X" provider: name: aws runtime: java8 stage: ${opt:stage, 'dev'} # you can add packaging information here package: artifact: target/${self:service}-${self:provider.stage}.jar functions: fooHandler: handler: com.serverless.Handler events: - http: path: /foo method: post barHandler: handler: com.serverless.Handler::barRequest events: - http: path: /bar method: get infoHandler: handler: com.serverless.Handler::barRequest events: - http: path: /info method: get |
And it is just simple is that. You are ready for deploying your first serverless using simple Java class which will handle your HTTP request directly. You can deploy your codes directly using your serverless command. But make sure you have proper AWS credentials and policy to deploy your code from commandline.
1 |
serverless deploy -v |
Once you deploy it the verbose log will look something like this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
Serverless: Packaging service... Serverless: Creating Stack... Serverless: Checking Stack create progress... CloudFormation - CREATE_IN_PROGRESS - AWS::CloudFormation::Stack - serverless-example-dev CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket CloudFormation - CREATE_COMPLETE - AWS::S3::Bucket - ServerlessDeploymentBucket CloudFormation - CREATE_COMPLETE - AWS::CloudFormation::Stack - serverless-example-dev Serverless: Stack create finished... Serverless: Uploading CloudFormation file to S3... Serverless: Uploading artifacts... Serverless: Uploading service .zip file to S3 (3.06 MB)... Serverless: Validating template... Serverless: Updating Stack... Serverless: Checking Stack update progress... CloudFormation - UPDATE_IN_PROGRESS - AWS::CloudFormation::Stack - serverless-example-dev CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - FooHandlerLogGroup CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - BarHandlerLogGroup CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::RestApi - ApiGatewayRestApi CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - BarHandlerLogGroup CloudFormation - CREATE_COMPLETE - AWS::Logs::LogGroup - BarHandlerLogGroup CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - FooHandlerLogGroup CloudFormation - CREATE_COMPLETE - AWS::Logs::LogGroup - FooHandlerLogGroup CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - InfoHandlerLogGroup CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::RestApi - ApiGatewayRestApi CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::RestApi - ApiGatewayRestApi CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceFoo CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceFoo CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceFoo CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceBar CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceBar CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceBar CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Role - IamRoleLambdaExecution CloudFormation - CREATE_COMPLETE - AWS::IAM::Role - IamRoleLambdaExecution CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - FooHandlerLambdaFunction CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - FooHandlerLambdaFunction CloudFormation - CREATE_COMPLETE - AWS::Lambda::Function - FooHandlerLambdaFunction CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodFooPost CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodFooPost CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Method - ApiGatewayMethodFooPost CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - FooHandlerLambdaVersionRvpza0AxazbfodlvLjTW5RMEtOabbVAUM6Kv3eZOc CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - FooHandlerLambdaVersionRvpza0AxazbfodlvLjTW5RMEtOabbVAUM6Kv3eZOc CloudFormation - CREATE_COMPLETE - AWS::Lambda::Version - FooHandlerLambdaVersionRvpza0AxazbfodlvLjTW5RMEtOabbVAUM6Kv3eZOc CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - BarHandlerLambdaFunction CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - BarHandlerLambdaFunction CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Permission - FooHandlerLambdaPermissionApiGateway CloudFormation - CREATE_COMPLETE - AWS::Lambda::Function - BarHandlerLambdaFunction CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - BarHandlerLambdaVersionDUOu7SzmBiE0rIrKErPa3oYBn1ScFtUBuIYsiLbxPbo CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - BarHandlerLambdaVersionDUOu7SzmBiE0rIrKErPa3oYBn1ScFtUBuIYsiLbxPbo CloudFormation - CREATE_COMPLETE - AWS::Lambda::Version - BarHandlerLambdaVersionDUOu7SzmBiE0rIrKErPa3oYBn1ScFtUBuIYsiLbxPbo CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Permission - FooHandlerLambdaPermissionApiGateway CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Permission - BarHandlerLambdaPermissionApiGateway CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Permission - BarHandlerLambdaPermissionApiGateway CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodBarGet CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodBarGet CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Method - ApiGatewayMethodBarGet CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceInfo CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Resource - ApiGatewayResourceInfo CloudFormation - CREATE_IN_PROGRESS - AWS::Logs::LogGroup - InfoHandlerLogGroup CloudFormation - CREATE_COMPLETE - AWS::Lambda::Permission - FooHandlerLambdaPermissionApiGateway CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceInfo CloudFormation - CREATE_COMPLETE - AWS::Logs::LogGroup - InfoHandlerLogGroup CloudFormation - CREATE_COMPLETE - AWS::Lambda::Permission - BarHandlerLambdaPermissionApiGateway CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - InfoHandlerLambdaFunction CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Function - InfoHandlerLambdaFunction CloudFormation - CREATE_COMPLETE - AWS::Lambda::Function - InfoHandlerLambdaFunction CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Permission - InfoHandlerLambdaPermissionApiGateway CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Permission - InfoHandlerLambdaPermissionApiGateway CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - InfoHandlerLambdaVersionWyrUkgiAPbvAPftZYomdZSxSh71cDRCPVZfcmx2a8p0 CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodInfoGet CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Method - ApiGatewayMethodInfoGet CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Method - ApiGatewayMethodInfoGet CloudFormation - CREATE_IN_PROGRESS - AWS::Lambda::Version - InfoHandlerLambdaVersionWyrUkgiAPbvAPftZYomdZSxSh71cDRCPVZfcmx2a8p0 CloudFormation - CREATE_COMPLETE - AWS::Lambda::Version - InfoHandlerLambdaVersionWyrUkgiAPbvAPftZYomdZSxSh71cDRCPVZfcmx2a8p0 CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1536642655899 CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1536642655899 CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Deployment - ApiGatewayDeployment1536642655899 CloudFormation - CREATE_COMPLETE - AWS::Lambda::Permission - InfoHandlerLambdaPermissionApiGateway CloudFormation - UPDATE_COMPLETE_CLEANUP_IN_PROGRESS - AWS::CloudFormation::Stack - serverless-example-dev CloudFormation - UPDATE_COMPLETE - AWS::CloudFormation::Stack - serverless-example-dev Serverless: Stack update finished... Service Information service: serverless-example stage: dev region: us-east-1 stack: serverless-example-dev api keys: None endpoints: POST - https://0manjxyoi0.execute-api.us-east-1.amazonaws.com/dev/foo GET - https://0manjxyoi0.execute-api.us-east-1.amazonaws.com/dev/bar GET - https://0manjxyoi0.execute-api.us-east-1.amazonaws.com/dev/info functions: fooHandler: serverless-example-dev-fooHandler barHandler: serverless-example-dev-barHandler infoHandler: serverless-example-dev-infoHandler Stack Outputs FooHandlerLambdaFunctionQualifiedArn: arn:aws:lambda:us-east-1:309168395754:function:serverless-example-dev-fooHandler:1 BarHandlerLambdaFunctionQualifiedArn: arn:aws:lambda:us-east-1:309168395754:function:serverless-example-dev-barHandler:1 ServiceEndpoint: https://0manjxyoi0.execute-api.us-east-1.amazonaws.com/dev ServerlessDeploymentBucketName: serverless-example-dev-serverlessdeploymentbucket-1xfrwmkvql3mj InfoHandlerLambdaFunctionQualifiedArn: arn:aws:lambda:us-east-1:309168395754:function:serverless-example-dev-infoHandler:1 |
Now it is time to do small testing from Postman . As provided in the url : https://0manjxyoi0.execute-api.us-east-1.amazonaws.com/dev/foo [POST]
It is quiet so easy isn’t it ? Just code what you want and deploy it. But there are some limitation of the serverless. It is not suitable for all kind of application and it definitely does not supersede the normal server deployment because of its nature and limits.
1. Maximum execution duration per request
We just cannot use request for long task like more than 5 minutes. AWS has a hard limit of 5 minutes for every request execution.
2. Memory resources limit
There are some limits of memory to be used by every single function . It is like 3008M in aws lambda as of now (2018-09-11).
3. Coldstart
Your function will actually sleep after unused for some minutes. When you request after the sleep time it need to warm up the server again which takes some seconds and it might not be good for those who wants to get the request instantly (realtime). There are some way which will keep your server warm all time described How to keep desired amount of AWS Lambda function containers warm
4. Payload size limit
You might not want to send a large data from your http request because there is a limit for request payload as well . As per AWS there is 6MB limit for payload however you can use event driven aws lambda execution for avoid sending payload directly to lambda.
5. Ephemeral disk capacity
You just cannot use your disk space more than 512 mb which is /tmp
directory of server while you do any data processing or any process which involves usage of hard disk space.
After talking about the limitations I would still prefer this serverless for some of my application like generating the pdf invoices for customer, doing scheduled tasks and others. In next blog post I would talk about how to run serverless locally and we can discuss about the overall complication and solutions.
Stay cool , be happy and code 😉
Thanks