In the previous post (https://jithinjudepaul.com/deployment-strategies-for-lambda-functions/ ), we saw how we could use Lambda functions to perform Blue-Green deployments. In most cases Lambda functions are often layered with an API Gateway layer as well. In this post, we will see how we can deploy Lambda functions in the Blue-Green deployment method when we apply an API Gateway layer as well to the Lambda function.
I am using the same repo that we used for part-1 here as well. So if you want to follow along and practice, kindly clone or fork the repo. Here is the repo link: https://github.com/jithinjudepaule/lambda-blue-green
High Level Architecture
The High level architecture for Deployment is as shown in figure 1-1. We keep the basic principle same i.e create the Lambda Alias with 2 versions and route the traffic accordingly. Then this Alias is mapped to the API Gateway.
Figure 1-1: High level architecture for API Gateway with Lambda deployment
We would follow the following high level steps:
Step 1: Create The API Gateway and add a resource using Terraform
Step 2: Create the API integration that points to the Lambda Alias arn
Step 3: Split the Lambda Alias’ traffic between 2 versions for testing
Step 4: Deploy the API Gateway using terraform
Step 5: Test the traffic splitting between 2 lambda versions
Step 6: Move 100% traffic to the latest version
Step 1: API Gateway Configuration
We create an API Gateway Rest API, define its path and add a method to it.. This is required as all the requests pointing to the API Gateway needs to be mapped to a resource and a method. For simplicity, I have added the ‘ANY’ method here.
resource "aws_api_gateway_rest_api" "api" {
name = "api-blue-green"
}
resource "aws_api_gateway_resource" "proxy" {
rest_api_id = "${aws_api_gateway_rest_api.api.id}"
parent_id = "${aws_api_gateway_rest_api.api.root_resource_id}"
path_part = "{proxy+}"
}
resource "aws_api_gateway_method" "proxy" {
rest_api_id = "${aws_api_gateway_rest_api.api.id}"
resource_id = "${aws_api_gateway_resource.proxy.id}"
http_method = "ANY"
authorization = "NONE"
}
Step 2: API Gateway-Lambda Alias integration
This step involves creating an integration between the API Gateway and the Lambda function Alias. The uri parameter of this integration resource must be mapped to the ARN of the Lambda Alias as shown below:
resource "aws_api_gateway_integration" "lambda" {
rest_api_id = "${aws_api_gateway_rest_api.api.id}"
resource_id = "${aws_api_gateway_method.proxy.resource_id}"
http_method = "${aws_api_gateway_method.proxy.http_method}"
integration_http_method = "POST"
type = "AWS_PROXY"
uri = "${aws_lambda_alias.blue_green_lambda_prod_alias.invoke_arn}"
}
Here we need to perform an additional step of mapping the API Gateway method and API Gateway integration to the root of the Rest API object to mitigate the issue of encountering an empty path at the root of the API. Here as well we need to map the Lambda Alias’ ARN to the uri parameter. This is shown below:
resource "aws_api_gateway_method" "proxy_root" {
rest_api_id = "${aws_api_gateway_rest_api.api.id}"
resource_id = "${aws_api_gateway_rest_api.api.root_resource_id}"
http_method = "ANY"
authorization = "NONE"
}
resource "aws_api_gateway_integration" "lambda_root" {
rest_api_id = "${aws_api_gateway_rest_api.api.id}"
resource_id = "${aws_api_gateway_method.proxy_root.resource_id}"
http_method = "${aws_api_gateway_method.proxy_root.http_method}"
integration_http_method = "POST"
type = "AWS_PROXY"
uri = "${aws_lambda_alias.blue_green_lambda_prod_alias.invoke_arn}"
}
Step 3: Traffic splitting
I have split the traffic to 50% between version 5 and version 6. (How to achieve this was explained in detail in part 1 of this blog series whose link I gave in the beginning)
Step 4: API Gateway deployment using terraform
Now with all the setting and configurations being done, now we need to deploy the API and for that we need to create the deployment resource and apply it.
resource "aws_api_gateway_deployment" "api_deployment" {
depends_on = [
"aws_api_gateway_integration.lambda",
"aws_api_gateway_integration.lambda_root",
]
rest_api_id = "${aws_api_gateway_rest_api.api.id}"
stage_name = "test"
variables = {
deployed_at = "${timestamp()}"
}
}
One caveat here is that the deployment is not applied for the integration level changes. So if you want to make changes in your integration resource and deploy it, you need to introduce changes in your api_deployment resource as well. In the code above, I have introduced a timestamped variable for this purpose, so that the changes are deployed every time.
Step 5: Test the traffic splitting between 2 lambda versions
Once we apply the above Terraform code, we will get the endpoint for the APIGateway from the output variable which we defined.
output "base_url" {
value = "${aws_api_gateway_deployment.api_deployment.invoke_url}"
}
We can test the same in the browser and we will get alternating outputs between version 5 and 6 as shown below:
Step 6: Move 100% traffic to the latest version
Now that we have tested both versions, we can move the entire traffic to the latest version as shown below:
resource "aws_lambda_alias" "blue_green_lambda_prod_alias" {
name = "prod"
description = "the prod alias"
function_name = aws_lambda_function.blue_green_lambda.arn
function_version = "6"
routing_config {
additional_version_weights = {
"5" = 0
}
}
depends_on = [
aws_lambda_function.blue_green_lambda
]
}
Additionally you could remove the ‘routing_config’ part completely, I have included it here only for illustrative purposes. Once you apply the above Terraform code, the entire traffic shifts to the latest version, i.e Version 6.
Conclusion
In part-1 we saw how we could use Blue-Green deployment strategy to deploy Lambda functions and in part-2 we saw how we could achieve the same by Implementing an additional API Gateway layer. In the next part we see how we can use CICD to achieve Lambda deployments.