Tutorial to build Serverless application using the AWS Serverless Application Model.
Tutorial Objectives:
1. Learn to build Serverless application using AWS SAM
2. Learn to fully automate builds and deployments by building a CI/CD pipeline using the AWS CDK.
3. Learn to run a Serverless application locally using the SAM CLI.
Prerequisites:
1. Download all the necessary code from here.
Step 1: CREATE A CLOUD9 WORKSPACE
Navigate to the Cloud9 console: https://console.aws.amazon.com/cloud9 Choose Create environment.
Once you navigate to the Cloud9 console, click on the create environment button:
Chose a name for your environment.
Once you confirm creation, after a couple of minutes, your environment should look like the following image:
(Optional) If you prefer a dark theme, choose the theme in the top menu: View > Themes > Cloud9 > Cloud 9 Night.
Congratulations you have successfully created your Cloud9 Environment.
Step 2: UPGRADE SAM CLI
We have put together a bootstrap script that will make the upgrade easier for you. Download it by running the following command from your Cloud9 terminal. Download the bootstrap.sh file provided in the prerequisite. Upload the bootstrap.sh file to your cloud9 environment and run the following commands.
chmod +x bootstrap.sh
./bootstrap.sh
THIS MAY TAKE A FEW MINUTES TO COMPLETE.
Example Output:
To verify the new version, Run the following command:
sam --version
You should see SAM CLI, version 0.43.0 or greater.
Step 3: Create a new SAM Application
To initialize a new SAM Application Project, run the following command.
sam init
It will prompt for project configuration parameters:
Type 1 to select AWS Quick Start Templates
Type 1 to select Zip as the package type.
Type 14 to select the Java 8 runtime.
Type 1 to select maven
Press enter to accept the default sam-app for project name.
Type 1 to select the Hello World Example.
Project should now be initialized.
You should see a new folder sam-app created with a basic Hello World scaffolding.
Step 4: RUN PROJECT LOCALLY
Install Dependencies
Before we run the application locally, it’s a common practice to install third-party libraries or dependencies that your application might be using. In our case we need to upgrade Java and install Maven.
Install OpenJDK 8. To do this, run the yum tool with the install command, specifying the OpenJDK 8 package.
sudo yum -y install java-1.8.0-openjdk-devel
Switch or upgrade the default Java development toolset to OpenJDK 8. To do this, run the update-alternatives command with the –config option. Run this command twice to switch or upgrade the command-line versions of the Java runner and compiler.
sudo update-alternatives --config java
sudo update-alternatives --config javac
Install Maven by using the terminal to run the following commands.
sudo wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo
sudo sed -i s/\$releasever/7/g /etc/yum.repos.d/epel-apache-maven.repo
sudo yum install -y apache-maven
We can now use SAM to build our project.
cd ~/environment/sam-app
sam build
Example Output:
RUN USING SAM CLI
There are 2 ways of running a Serverless app locally: 1) By invoking an individual Lambda function or 2) By running a local HTTP server that simulates API Gateway. For this workshop, we will focus on number 2, but you can learn about invoking individual functions in the SAM Local Invoke reference.
In the terminal, run the following command from the root directory of the sam-app folder:
cd ~/environment/sam-app
sam local start-api --port 8080
Test your endpoint
Once your local server is running, we can send HTTP requests to test it.
Without killing the running process, open a new terminal.
Test your endpoint by running a CURL command that triggers an HTTP GET request.
curl http://localhost:8080/hello
MAKE A CODE CHANGE
Now let's make a simple code change. Press CTRL-C to terminate the running process.
Open the file sam-app/HelloWorldFunction/src/main/java/helloworld/App.java change the response message to return hello beautiful world instead of hello world. Your Lambda handler should look like this after the change:
Open the file sam-app/HelloWorldFunction/src/test/java/helloworld/AppTest.java and update the expected value for the response to match the new message. The unit test should look like this after the update:
To see the new changes we must rebuild our SAM project.
sam build
Example Output
Then rerun our local deployment.
sam local start-api --port 8080
Now rerun the CURL command to see the changes reflected in your endpoint.
Congratulations, your project is working successfully when tested locally.
Step 5: Deploy to AWS.
Build the Application.
To build a SAM project, we are going to use the sam build command. This command iterates through the functions in your application, looking for the manifest file (such as requirements.txt or package.json) that contains the dependencies, and automatically creates deployment artifacts.
From the root of the sam-app folder, run the following command in the terminal:
cd ~/environment/sam-app
sam build
Build completed
When the build finishes successfully, you should see a new directory created in the root of the project named .aws-sam. It is a hidden folder, so if you want to see it in the IDE, make sure you enable Show hidden files in Cloud9 to see it.
Explore the build folder
Take a moment to explore the content of the build folder. Notice that the unit tests are automatically excluded from it.
DEPLOY THE APP
The sam deploys command deploys your application by launching a CloudFormation stack. This command has a guided interactive mode, which you enable by specifying the –guided parameter. It is recommended to deploy with guided mode for the first time as it will capture the configuration for future deployments.
Run the following command in the same directory level where the template.yaml is located:
sam deploy --guided
It will ask for certain input parameters, like so:
Confirm deployment
At some point, SAM will ask for deployment confirmation. This is possible because it first creates a CloudFormation ChangeSet, and then it asks for confirmation to execute it. This is kind of like a dry run deployment and is a best practice when doing deployments via CloudFormation. Type y to confirm.
Deployment completed
This command might take a few minutes to finish because it is creating the resources (Lambda function, API Gateway and IAM roles) on the AWS account. When it completes successfully, you should see an output similar to the following:
To understand what just happened we would have to take a look at the guided deployment of the SAM application.
The guided deployment does a few things for you. Let’s take a quick look at what happened under the hood during the guided deployment to understand this process better.
1) Your codebase gets packaged in a zip file. 2) SAM creates an S3 bucket in your account if it doesn’t already exist. 3) Zip file is uploaded to the S3 bucket. 4) SAM creates the packaged template that references the location of the zip file on S3. 5) This template is also uploaded to the S3 bucket. 6) SAM starts the deployment via CloudFormation ChangeSets.
The first time you do a guided deployment, a new file samconfig.toml is created in the root of your project with your specified deployment parameters, this is so that the next time you execute sam deploy, it uses the same parameters without having you enter them again.
VERIFY DEPLOYMENT
Now that your Serverless application is deployed to an AWS account, let’s check if it is running as expected.
Open the CloudFormation console
Navigate to the AWS CloudFormation console, make sure you are in the same region where you have been working on so far. You should see the new stack sam-app in the CREATE_COMPLETE status.
Test the app
Click on the sam-app stack and then go to the Outputs tab. In this tab, you will see the API Gateway URL, the Lambda function ARN and the IAM Role ARN for the function. To test our application, copy the API Gateway endpoint URL and paste it into a new browser window.
When you navigate to this URL in the browser, API Gateway is invoking the Hello World Lambda function. If everything works, you will see Lambda response:
Congratulations!
You have successfully deployed a Serverless application! Let’s automate deployments now!
Step 6: BUILD THE PIPELINE
In this step we will learn how to create a CI/CD pipeline for automatic deployment of the serverless application.
CREATE A GIT REPOSITORY
Any CI/CD pipeline starts with a code repository. In this workshop we use AWS CodeCommit for ease of integration, but you could use other source code integrations, like GitHub for example.
Run the following command from your terminal to create a new CodeCommit repository:
aws codecommit create-repository --repository-name sam-app
You should see the following output. Copy the value of cloneUrlHttp, you will need it later.
CONFIGURE CREDENTIALS
One of the cool things about CodeCommit is the support for IAM authentication. And if you are running this workshop from a Cloud9 workspace, you can leverage the fact that your terminal is already pre-authenticated with valid AWS credentials.
Configure the git client with username and email, so your commits have an author defined.
git config --global user.name "Replace with your name"
git config --global user.email "replace_with_your_email@example.com"
PUSH THE CODE
Ignore the build artifacts
There is no need to track the .aws-sam directory or the packaged.yaml under version control as they are re-generated on every build. We will paste the following into a .gitgnore file.
Create the file
cd ~/environment/sam-app
touch .gitignore
In Cloud9, remember to enable hidden files:
Open the .gitignore file and paste the two lines below.
.aws-sam/
packaged.yaml
Your .gitignore file should look like this.
Remember to save the file.
From the root directory of your sam-app project, run the following commands:
cd ~/environment/sam-app
git init
git add .
git commit -m "Initial commit"
Example Output:
Push the code
Add your CodeCommit repository URL as a remote on your local git project.
git remote add origin codecommit://sam-app
Now, push the code:
git push -u origin master
Verify in CodeCommit
Navigate to the AWS CodeCommit console, find your sam-app repository and click on it to view its contents. Make sure your code is there. You should see a screen like the following:
Introducing the AWS CDK
In this workshop, we are going to use the AWS Cloud Development Kit (also known as CDK), as the pipeline vending mechanism. The AWS CDK is a software development framework for defining cloud infrastructure in code and provisioning it through AWS CloudFormation.
That’s right! You can describe your infrastructure by writing code in TypeScript, C#, Python or Java. Your code is then synthesized into CloudFormation and using the CDK CLI you can deploy it to an AWS account.
SETUP A CDK PROJECT
Install the latest CDK
If you are using Cloud9, the CDK is already pre-installed but it will likely be a few versions old. Run the following commands from the Cloud9 terminal to remove your current version and install the latest one:
npm uninstall -g aws-cdk
npm install -g aws-cdk --force
Initialize project
Now, let’s create a folder within our sam-app directory where the pipeline code will reside.
cd ~/environment/sam-app
mkdir pipeline
cd pipeline
Initialize a new CDK project within the pipeline folder by running the following command:
cdk init --language java
After a few seconds, our new CDK project should look like this:
Project structure
At this point, your project should have the structure below (only the most relevant files and folders are shown). Within the CDK project, the main file you will be interacting with is the PipelineStack.java. Don’t worry about the rest of the files for now.
Modify stack name
Open the src/main/java/com/myorg/PipelineApp.java file, which is your entry point to the CDK project, and change the name of the stack to sam-app-cicd.
Save the file.
Create a PIPELINE using CODE
Open the file src/main/java/com/myorg/PipelineStack.java in your Cloud9 workspace. It is empty at the moment, but here is where you will be adding code to build your CI/CD pipeline.
Build the CDK project
Even though we haven’t wrote any code yet, let’s get familiar with how to build and deploy a CDK project, as you will be doing it multiple times in this workshop and you should get comfortable with the process. Start by building the project with the following command:
cd ~/environment/sam-app/pipeline
mvn package
Deploy a CDK project
After the build has finished, go ahead and deploy the pipeline project by using the CDK CLI:
cdk bootstrap
cdk deploy
The output should look like the following:
A new CloudFormation stack was created in your account, but because your CDK project is empty, the only resource that was created was an AWS::CDK::Metadata. If you check your CloudFormation Console, you will see the new stack and the metadata resource.
ARTIFACTS BUCKET
Every Code Pipeline needs an artifacts bucket, also known as Artifact Store. CodePipeline will use this bucket to pass artifacts to the downstream jobs and its also where SAM will upload the artifacts during the build process.
Let’s get started and write the code for creating this bucket:
Make sure you are editing the PipelineStack.java file.
Alternatively, you can copy the following code and replace it with the existing code.
package com.myorg;
import software.constructs.Construct;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;
import software.amazon.awscdk.services.s3.Bucket;
public class PipelineStack extends Stack {
public PipelineStack(final Construct scope, final String id) {
this(scope, id, null);
}
public PipelineStack(final Construct scope, final String id, final StackProps props) {
super(scope, id, props);
Bucket artifactsBucket = new Bucket(this, "ArtifactsBucket");
}
}
Your PipelineStack.java file should look like this:
Setting up the SOURCE STAGE
The Source Stage is the first step of any CI/CD pipeline and it represents your source code. This stage is in charge of triggering the pipeline based on new code changes (i.e. git push or pull requests). In this workshop, we will be using AWS CodeCommit as the source provider, but CodePipeline also supports S3, GitHub and Amazon ECR as source providers.
Append the following code snippet after your bucket definition in the PipelineStack.java file:
IRepository codeRepo = Repository.fromRepositoryName(this, "AppRepository", "sam-app");
Pipeline pipeline = new Pipeline(this, "Pipeline", PipelineProps.builder()
.artifactBucket(artifactsBucket).build());
Artifact sourceOutput = new Artifact("sourceOutput");
CodeCommitSourceAction codeCommitSource = new CodeCommitSourceAction(CodeCommitSourceActionProps.builder()
.actionName("CodeCommit_Source")
.repository(codeRepo)
.output(sourceOutput)
.build());
pipeline.addStage(StageOptions.builder()
.stageName("Source")
.actions(Collections.singletonList(codeCommitSource))
.build());
You will also need the following imports. We’ll add all the imports we will need now for the workshop.
import software.amazon.awscdk.services.codebuild.*;
import software.amazon.awscdk.services.codecommit.*;
import software.amazon.awscdk.services.codepipeline.*;
import software.amazon.awscdk.services.codepipeline.actions.*;
import java.util.*;
import static software.amazon.awscdk.services.codebuild.LinuxBuildImage.AMAZON_LINUX_2;
To view the complete code see the PipelineStack_Part1.java file from the downloaded resources.
Since we already have the CodeCommit repository, we don’t need to create a new one, we just need to import it using the repository name.
Also notice how we define an object sourceOutput as a pipeline artifact; This is necessary for any files that you want CodePipeline to pass to downstream stages. In this case, we want our source code to be passed to the Build stage.
Setting up the BUILD STAGE
The Build Stage is where your Serverless application gets built and packaged by SAM. We are going to use AWS CodeBuild as the Build provider for our pipeline. It is worth mentioning that CodePipeline also supports other providers like Jenkins, TeamCity or CloudBees.
Add the build stage
Let’s go ahead and add a Build stage to you PipelineStack.java:
// Declare build output as artifacts
Artifact buildOutput = new Artifact("buildOutput");
// Declare a new CodeBuild project
PipelineProject buildProject = new PipelineProject(this, "Build", PipelineProjectProps.builder()
.environment(BuildEnvironment.builder()
.buildImage(AMAZON_LINUX_2).build())
.environmentVariables(Collections.singletonMap("PACKAGE_BUCKET", BuildEnvironmentVariable.builder()
.value(artifactsBucket.getBucketName())
.build()))
.build());
// Add the build stage to our pipeline
CodeBuildAction buildAction = new CodeBuildAction(CodeBuildActionProps.builder()
.actionName("Build")
.project(buildProject)
.input(sourceOutput)
.outputs(Collections.singletonList(buildOutput))
.build());
pipeline.addStage(StageOptions.builder()
.stageName("Build")
.actions(Collections.singletonList(buildAction))
.build());
To view the complete code see the PipelineStack_Part2.java file from the downloaded resources.
Deploy the pipeline
From your terminal, run the following commands to deploy the pipeline:
mvn package
cdk deploy
Verify pipeline creation
Navigate to the AWS CodePipeline Console and click on your newly created pipeline!
The Build step should have failed. Don’t worry! this is expected because we haven’t specified what commands to run during the build yet, so AWS CodeBuild doesn’t know how to build our Serverless application.
Create the BUILDSPEC FILE
A Buildspec File is a series of commands in YAML format that CodeBuild executes to build your application. This file is placed in the root folder of a SAM application and CodeBuild will automatically find it and run it during build time.
In your Cloud9 editor, create a new file named buildspec.yml in the root (top level) of the sam-app directory by right clicking on the sam-app folder and selecting New file.
Then, paste the following content into the file:
# ~/environment/sam-app/buildspec.yml
version: 0.2
phases:
install:
runtime-versions:
java: corretto8
commands:
# Install packages or any pre-reqs in this phase.
# Upgrading SAM CLI to latest version
- pip3 install --upgrade aws-sam-cli
- sam --version
build:
commands:
# Use Build phase to build your artifacts (compile, etc.)
- cd HelloWorldFunction
- mvn package
- cd ..
- sam build
post_build:
commands:
# Use Post-Build for notifications, git tags, upload artifacts to S3
- sam package --s3-bucket $PACKAGE_BUCKET --output-template-file packaged.yaml
artifacts:
discard-paths: yes
files:
# List of local artifacts that will be passed down the pipeline
- packaged.yaml
Save the file. It should look like the following screenshot.
Take a moment to understand the structure of the file and feel free to read the Buildsec Reference here: https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html.
Push code changes
Commit your changes and push them to the repository.
cd ~/environment/sam-app
git add .
git commit -m "Added buildspec.yml"
git push
Verify build succeeds
Navigate to your CodePipeline again, and wait for it to trigger automatically. This time the build will succeed:
Creating the DEPLOY STAGE
The Deploy Stage is where your SAM application and all its resources are created in an AWS account. The most common way to do this is by using CloudFormation ChangeSets to deploy. This means that this stage will have 2 actions: the CreateChangeSet and the ExecuteChangeSet.
Add the Deploy stage to your PipelineStack.java:
// Deploy stage
CloudFormationCreateReplaceChangeSetAction createChangeSet = new CloudFormationCreateReplaceChangeSetAction(CloudFormationCreateReplaceChangeSetActionProps.builder()
.actionName("CreateChangeSet")
.templatePath(buildOutput.atPath("packaged.yaml"))
.stackName("sam-app")
.adminPermissions(true)
.changeSetName("sam-app-dev-changeset")
.runOrder(1)
.build());
CloudFormationExecuteChangeSetAction executeChangeSet = new CloudFormationExecuteChangeSetAction(CloudFormationExecuteChangeSetActionProps.builder()
.actionName("Deploy")
.stackName("sam-app")
.changeSetName("sam-app-dev-changeset")
.runOrder(2)
.build());
pipeline.addStage(StageOptions.builder()
.stageName("Dev")
.actions(Arrays.asList(createChangeSet, executeChangeSet))
.build());
To view the complete code see the PipelineStack_Part3.java file from the downloaded resources.
Deploy the pipeline
On your terminal, run the following commands from within the pipeline directory:
cd ~/environment/sam-app/pipeline
mvn package
cdk deploy
The CLI might ask you to confirm the changes before deploying, this is because we are giving Admin permissions to the IAM role that deploys our application. This is generally not a bad practice since this role can only be assumed by CloudFormation and not by a human, however, if your organization has a stricter security posture you may want to consider creating a custom IAM deployment role with a fine grain policy.
Trigger a release
Navigate to your pipeline and you will see the Deploy stage has been added, however, it is currently greyed out because it hasn’t been triggered. Let’s just trigger a new run of the pipeline manually by clicking the Release Change button.
VERIFY PIPELINE
Let your pipeline run every stage. After it finishes it will look all green like the following screenshot:
Congratulations! You have created a CI/CD pipeline for a Serverless application!
Step 8: CANARY DEPLOYMENTS
A Canary Deployment is a technique that reduces the risk of deploying a new version of an application by slowly rolling out the changes to a small subset of users before rolling it out to the entire customer base.
UPDATE SAM TEMPLATE
Open the SAM template (sam-app/template.yaml) in your project and add the following lines to the HelloWorldFunction properties section.
AutoPublishAlias: live
DeploymentPreference:
Type: Canary10Percent5Minutes
It should look like this: PLEASE CHECK THE CORRECT INDENTATION, IT IS VERY IMPORTANT IN YAML FORMAT.
Deployment Preference Types
For this workshop, we are using the Canary10Percent5Minutes strategy, which means that traffic is shifted in two increments. In the first increment, only 10% of the traffic is shifted to the new Lambda version, and after 5 minutes, the remaining 90% is shifted. There are other deployment strategies you can choose in CodeDeploy:
Canary10Percent30Minutes
Canary10Percent5Minutes
Canary10Percent10Minutes
Canary10Percent15Minutes
Linear10PercentEvery10Minutes
Linear10PercentEvery1Minute
Linear10PercentEvery2Minutes
Linear10PercentEvery3Minutes
AllAtOnce
The Linear strategy means that traffic is shifted in equal increments with an equal number of time interval between each increment.
Validate the SAM template
Run the following command on your terminal:
cd ~/environment/sam-app
sam validate
If the template is correct, you will see template.yaml is a valid SAM Template. If you see an error, then you likely have an indentation issue on the YAML file. Double check and make sure it matches the screenshot shown above.
Push the changes
In the terminal, run the following commands from the root directory of your sam-app project.
git add .
git commit -m "Canary deployments with SAM"
git push
MONITOR CANARY HEALTH
Canary deployments are considerably more successful if the code is being monitored during the deployment. You can configure CodeDeploy to automatically roll back the deployment if a specified CloudWatch metric has breached the alarm threshold. Common metrics to monitor are Lambda Invocation errors or Invocation Duration (latency), for example.
Define a CloudWatch Alarm
Add the following alarm definition to the template.yaml file in the Resources section after the HelloWorldFunction definition.
CanaryErrorsAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Lambda function canary errors
ComparisonOperator: GreaterThanThreshold
EvaluationPeriods: 2
MetricName: Errors
Namespace: AWS/Lambda
Period: 60
Statistic: Sum
Threshold: 0
Dimensions:
- Name: Resource
Value: !Sub "${HelloWorldFunction}:live"
- Name: FunctionName
Value: !Ref HelloWorldFunction
- Name: ExecutedVersion
Value: !GetAtt HelloWorldFunction.Version.Version
And then add the following lines to the DeploymentPreference section of the HelloWorldFunction definition
Alarms:
- !Ref CanaryErrorsAlarm
To view the complete code see the template.yaml file from the downloaded resources.
Validate the SAM template
Run the following command on your terminal:
cd ~/environment/sam-app
sam validate
If the template is correct, you will see template.yaml is a valid SAM Template. If you see an error, then you likely have an indentation issue on the YAML file. Double check and make sure it matches the screenshot shown above.
Push the changes
In the terminal, run the following commands from the root directory of your sam-app project.
git add .
git commit -m "Added CloudWatch alarm to monitor the canary"
git push
VERIFY IN CODEDEPLOY
Wait for your pipeline to get to the deployment stage (ExecuteChangeSet) and when you see it In Progress. Navigate to the CodeDeploy console to watch the deployment progress.
Navigate to the AWS CodeDeploy console and after a couple of minutes, you should see a new deployment in progress. Click on the Deployment to see the details.
The deployment status shows that 10% of the traffic has been shifted to the new version (aka The Canary). CodeDeploy will hold the remaining percentage until the specified time interval has ellapsed, in this case, we specified the interval to be 5 minutes.
Shortly after the 5 minutes, the remaining traffic should be shifted to the new version:
ROLLBACKS
Monitoring the health of your canary allows CodeDeploy to make a decision to whether a rollback is needed or not. If any of the CloudWatch Alarms specified gets to ALARM status, CodeDeploy rollsback the deployment automatically.
Introduce an error on purpose
Lets break the Lambda function on purpose so that the CanaryErrorsAlarm gets triggered during deployment.
Update the lambda code in /samapp/HelloWorldFunction/src/main/java/helloworld/App.java to throw an error on every invocation, like this:
package helloworld;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
/**
* Handler for requests to Lambda function.
*/
public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) {
throw new Error("This will cause a deployment rollback");
// Map<String, String> headers = new HashMap<>();
// headers.put("Content-Type", "application/json");
// headers.put("X-Custom-Header", "application/json");
// APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent()
// .withHeaders(headers);
// try {
// final String pageContents = this.getPageContents("https://checkip.amazonaws.com");
// String output = String.format("{ \"message\": \"hello beautiful world\", \"location\": \"%s\" }", pageContents);
// return response
// .withStatusCode(200)
// .withBody(output);
// } catch (IOException e) {
// return response
// .withBody("{}")
// .withStatusCode(500);
// }
}
private String getPageContents(String address) throws IOException{
URL url = new URL(address);
try(BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) {
return br.lines().collect(Collectors.joining(System.lineSeparator()));
}
}
}
Make sure to update the unit test, otherwise the build will fail. Comment out every line in the /sam-app/HelloWorldFunction/src/test/java/helloworld/AppTest.java file:
package helloworld;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class AppTest {
@Test
public void successfulResponse() {
// App app = new App();
// APIGatewayProxyResponseEvent result = app.handleRequest(null, null);
// assertEquals(result.getStatusCode().intValue(), 200);
// assertEquals(result.getHeaders().get("Content-Type"), "application/json");
// String content = result.getBody();
// assertNotNull(content);
// assertTrue(content.contains("\"message\""));
// assertTrue(content.contains("\"hello beautiful world\""));
// assertTrue(content.contains("\"location\""));
}
}
Push the changes
In the terminal, run the following commands from the root directory of your sam-app project.
git add .
git commit -m "Breaking the lambda function on purpose"
git push
WAIT FOR DEPLOYMENT TO START
Again, wait for your Pipeline to reach the deployment phase (ExecuteChangeSet). It should turn blue when it begins:
INVOKE THE CANARY
While the deployment is running, you need to generate traffic to the new Lambda function to make it fail and trigger the CloudWatch Alarm. In a real production environment, your users will likely generate organic traffic to the canary function, so you may not need to do this.
In your terminal, run the following command to invoke the Lambda function:
aws lambda invoke --function-name \
$(aws lambda list-functions | jq -r -c '.Functions[] | select( .FunctionName | contains("sam-app-HelloWorldFunction")).FunctionName'):live \
--payload '{}' \
response.json
Note: If you get an error that jq command is not installed, you can install it by running sudo yum install -y jq.
There will be a new file response.json created. It contains the response of the lambda invocation. If you open it, you may see the the response of the old Lambda version, or you may see the new one that causes an error.
Remember: During deployment, only 10% of the traffic will be routed to the new version. So, keep on invoking your lambda many times. 1 out of 10 invocations should trigger the new broken lambda, which is what you want to cause a rollback.
Here is a command that invokes your function 15 times in a loop. Feel free to run it in your terminal.
counter=1
while [ $counter -le 15 ]
do
aws lambda invoke --function-name \
$(aws lambda list-functions | jq -r -c '.Functions[] | select( .FunctionName | contains("sam-app-HelloWorldFunction")).FunctionName'):live \
--payload '{}' \
response.json
sleep 1
((counter++))
done
WATCH CODEDEPLOY ROLLBACK
Navigate to the AWS CodeDeploy Console and go into the deployment In-Progress to view its details.
After a few minutes, CodeDeploy will detect that the CanaryErrorsAlarm has triggered and it will start rolling back the deployment. The screen will look something like this:
Congratulations, You have successfully set you a CI/CD pipeline with Canary Deployment and Fault Tolerance.
Step 9: Clean Up.
DELETE S3 BUCKETS
There are 2 buckets that got created as part of this workshop, one was created by SAM CLI and the other one was created by CodePipeline. To delete these buckets, we should empty them first and then proceed to delete them.
First Bucket
Empty the bucket by going to the S3 console. And then Delete it.
Second Bucket
Empty the bucket by going to the S3 console. And then Delete it.
DELETE CloudFormation STACKS
Now that the buckets are empty, we can delete the Cloudformation stacks:
Delete the CF stacks in following sequence and please wait for each to complete before going to next one.
Delete sam-app
aws cloudformation delete-stack --stack-name sam-app
Delete sam-app-cicd
aws cloudformation delete-stack --stack-name sam-app-cicd
Delete aws-sam-cli-managed-default
aws cloudformation delete-stack --stack-name aws-sam-cli-managed-default
You can also delete the CF Stacks by navigation to the Cloud Formation management console and deleting the stacks.
Finally Delete the cloud 9 environment.
Thank you for doing this tutorial!
We hope you learned something.
Was this document helpful? How can we make this document better? Please provide your insights. You can download PDF version for reference.
We provide the best AWS training from Pune, India.
For aws certification contact us now.
Comments