As you can see, there is not much infrastructure to set up. To makes things even simpler, you will use the AWS Serverless Application Model (AWS SAM) to reduce the lines of your CloudFormation template to a minimum. All CloudFormation resource types that start with AWS::Serverless:: are transformed by SAM.
AWS Velocity Series
Most of our clients use AWS to reduce time-to-market following an agile approach. But AWS is only one part of the solution. In this article series, I show you how we help our clients to improve velocity: the time from idea to production. Discover all posts!
Let’s start to describe the needed infrastructure.
Serverless app infrastructure
The serverless app infrastructure for the factorial app consists of two parts:
API Gateway: Provides a configurable HTTPS REST Endpoint that can trigger integrations such as Lambda when a request arrives.
Lambda function: Lambda provides a fully managed (aka Serverless) runtime for Node.js, Java, Python, and C# code. You upload your code and Lambda runs the code for you.
--- AWSTemplateFormatVersion:'2010-09-09' Transform:'AWS::Serverless-2016-10-31'# this line activates the SAM transformations! Description:'Serverless' Parameters: # S3Bucket and S3Key where the zipped code is located. This will be created with CodeBuild S3Bucket: Type:String S3Key: Type:String Resources: GetFactorialLambda: Type:'AWS::Serverless::Function' Properties: Handler:'app/handler.factorial' Runtime:'nodejs6.10' CodeUri: Bucket:!RefS3Bucket Key:!RefS3Key Events: Http: Type:Api Properties: Path:/{n} Method:get RestApiId:!RefApiGateway
Lambda dictates an interface that you have to follow. So far, the factorial app is based on express and comes with its own web server. This is no longer needed. Instead, we can have a simpler entry point into the application. Create a file app/handler.js with the following content.
AdminEmail: Description:'The email address of the admin who receives alerts.' Type:String
Alerts are triggered by a CloudWatch Alarm which can send an alert to an SNS topic. You can subscribe to this topic via an email address to receive the alerts. Let’s create an SNS topic and two alarms in the Resources section:
# A SNS topic is used to send alerts via Email to the value of the AdminEmail parameter Alerts: Type:'AWS::SNS::Topic' Properties: Subscription: -Endpoint:!RefAdminEmail Protocol:email # This alarm is triggered, if the Node.js function returns or throws an Error GetFactorialLambdaLambdaErrorsAlarm: Type:'AWS::CloudWatch::Alarm' Properties: AlarmDescription:'GET /{n} lambda errors' Namespace:'AWS/Lambda' MetricName:Errors Dimensions: -Name:FunctionName Value:!RefGetFactorialLambda Statistic:Sum Period:60 EvaluationPeriods:1 Threshold:1 ComparisonOperator:GreaterThanOrEqualToThreshold AlarmActions: -!RefAlerts # This alarm is triggered, if the there are too many function invocations GetFactorialLambdaLambdaThrottlesAlarm: Type:'AWS::CloudWatch::Alarm' Properties: AlarmDescription:'GET /{n} lambda throttles' Namespace:'AWS/Lambda' MetricName:Throttles Dimensions: -Name:FunctionName Value:!RefGetFactorialLambda Statistic:Sum Period:60 EvaluationPeriods:1 Threshold:1 ComparisonOperator:GreaterThanOrEqualToThreshold AlarmActions: -!RefAlerts
Let’s recap what you implemented: A Lambda function that is connected to an API Gateway for GET /{number} requests. In the case of errors, you will receive an Email. All Lambda functions automatically save their logs in CloudWatch Logs.
Now, you can improve the API Gateway setup and add input validation.
API Gateway
An implicit API Gateway is created and configured automatically when using SAM. But if you want to validate the input on the API Gateway, you have to define the API Gateway explicitly to add the API specification in more details by using the open standard Swagger / OpenAPI Spec. Let’s do this in the Resources section:
ApiGateway: Type:'AWS::Serverless::Api' Properties: StageName:Prod DefinitionBody: swagger:'2.0' basePath:'/' info: title:Serverless schemes: -https # We want to validate the body and request parameters x-amazon-apigateway-request-validators: basic: validateRequestBody:true validateRequestParameters:true paths: '/{n}': parameters:# we expect one parameter in the path of type number -name:'n' in:path description:'N' required:true type:number get: produces: -'text/plain' responses: '200': description:'factorial calculated' schema: type:number x-amazon-apigateway-request-validator:basic# enable validation for this resource x-amazon-apigateway-integration:# this section connect the Lambda function with the API Gateway httpMethod:POST type:'aws_proxy' uri:!Sub'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetFactorialLambda.Arn}/invocations' passthroughBehavior:when_no_match
If you are familiar with Swagger / OpenApi Spec you will find nothing special besides the x-* parameters which are API Gateway specific. You can also monitor the API Gateway. To do so, append the following section the Resources section of your template:
You will now receive alerts via email if the API Gateway returns an 5XX HTTP status code. API Gateway can also save logs to CloudWatch Logs but SAM lacks support to enable logging at the moment.
Let’s add some outputs to the stack to make it easier to connect with the API Gateway later on.
# A CloudFormation stack can return information that is needed by other stacks or scripts. Outputs: DNSName: Description:'The DNS name for the API gateway.' Value:!Sub'${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com' Export: Name:!Sub'${AWS::StackName}-DNSName' # The URL is needed to run the acceptance test against the correct endpoint URL: Description:'URL to the API gateway.' Value:!Sub'https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/Prod' Export: Name:!Sub'${AWS::StackName}-URL'
Now you have a production ready Serverless infrastructure defined in CloudFormation with the help of SAM. It’s time to deploy the Serverless app.
Serverless app CI/CD pipeline
The pipeline is based on the CI/CD Pipeline as Code part of this series. The pipeline so far stops when the application artifact (Zip file) is created. An acceptance test artifact is also created. What is missing?
Create or update an acceptance environment based on the CloudFormation+SAM template serverless.yml
Run the acceptance tests
Create or update a production environment based on the CloudFormation+SAM template serverless.yml
This is how the pipeline looks from the beginning to the end:
In the EC2 and ECS examples, I used CREATE_UPDATE in the CodePipeline to create or update the CloudFormation stack. At this point, SAM works only if you use CloudFormation Change Sets. Therefore I have to switch from a single CREATE_UPDATE step to CreateChangeSet and ApplyChangeSet in this example.
Copy the deploy/pipeline.yml file to deploy/pipeline_serverless.yml to get the starting point right. If you don’t have the deploy/pipeline.yml file you can download it from https://github.com/widdix/aws-velocity.
Acceptance stage
The acceptance stage consists of a CloudFormation stack based on infrastructure/serverless.yml and the execution of the acceptance tests. To create the CloudFormation stack, you first have to provide a few parameters. Create a file infrastructure/serverless.json with the following content:
Make sure to change the value of the AdminEmail parameter. Look at the S3Bucket and S3Key parameter value. This is the way of getting the artifact location in CodePipeline.
To run the acceptance tests, you need another CodeBuild project, add the following resources to the Resources section of deploy/pipeline_serverless.yml:
The production stage is pretty simple, just one CloudFormation stack. Change the Pipeline resource in the file deploy/pipeline_serverless.yml to add a new stage that looks familiar to the acceptance stage:
Now the application is deployed to production with confidence and without disturbing the users. Try it and run the pipeline!
Summary
Let’s use my production-ready definition to summarize how each point is implemented:
Highly available: API Gateway and Lambda are both highly available services out of the box. So your solution is HA as well.
Scalable: API Gateway and Lambda are both scaled automatically by AWS for you. You have not to manage anything here!
Frictionless deployment: The new zip file created by CodeBuild is deployed with CloudFormation by changing the value of the parameter which is passed down to the Lambda resource.
Secure: HTTPS by default. AWS cares about patching. You only need to care about the IAM permissions of your Lambda function. In this case is has only the default permissions to write to CloudWatch Logs.
Operations: All logs are stored in CloudWatch Logs, important metrics are monitored, and alarms are defined.
If you now have the impression that deploying and running a Serverless app is easy you are right.