AWS Lambda Canary Releases Tested with Artillery

A technical guide on setting up canary deployments for AWS Lambda using AWS CDK v2 in Python and integrating the setup with AWS CodePipeline.

2 months ago   •   2 min read

By Maik Wiesmüller
Photo by Joshua J. Cotten / Unsplash

Understanding Canary Deployments in AWS Lambda

Canary deployments are a technique used to reduce the risk associated with deploying new software versions. Instead of replacing the old version of the function with the new one all at once, a small percentage of the traffic is gradually shifted to the new version. This approach allows developers to monitor the new version's performance and behavior under real traffic conditions with minimal impact on users.

AWS Lambda supports canary deployments through AWS CodeDeploy, which automates the process and provides several configuration options to control the deployment.

Setting Up a Canary Deployment with AWS CDK

Setting Up a Canary Deployment - Steps

  1. Create Your Lambda Functions: Start by having two versions of your Lambda function - the original and the updated one.
  2. Configure AWS CodeDeploy: Use AWS CodeDeploy to manage the deployment process. You need to define a deployment group and specify the percentage of traffic to shift in each interval.
  3. Define Alarms: Set up CloudWatch alarms to monitor the health of your deployment. AWS CodeDeploy can roll back the deployment if any of these alarms are triggered.

Define the Stack

Our primary focus is to create a CDK stack encapsulating an AWS Lambda function and its CodeDeploy configuration for canary deployments.

from aws_cdk import (
    aws_lambda as lambda_,
    aws_codedeploy as codedeploy,
    aws_cloudwatch as cloudwatch,
    core as cdk
)

class ServerlessCanaryStack(cdk.Stack):
    def __init__(self, scope: cdk.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # Define Lambda function
        lambda_function = lambda_.Function(
            self, 'MyFunction',
            runtime=lambda_.Runtime.PYTHON_3_8,
            handler='index.handler',
            code=lambda_.Code.from_asset('path/to/your/lambda/code')
        )

        # Create Lambda alias
        alias = lambda_.Alias(
            self, 'LambdaAlias',
            alias_name='Prod',
            version=lambda_function.current_version
        )

        # Define CloudWatch alarms for monitoring
        failure_alarm = cloudwatch.Alarm(
            self, 'FailureAlarm',
            metric=lambda_function.metric_errors(),
            threshold=1,
            evaluation_periods=1
        )

        # Setup CodeDeploy for canary deployments
        deploy_config = codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTES
        codedeploy.LambdaDeploymentGroup(
            self, 'DeploymentGroup',
            alias=alias,
            deployment_config=deploy,
            alarms=[failure_alarm]
        )

Integrate with AWS CodePipeline

Next, integrate this stack with AWS CodePipeline. Assuming an existing pipeline, we add a stage to deploy the stack.

from aws_cdk import (
    aws_codepipeline as codepipeline,
    aws_codepipeline_actions as cpactions,
    core as cdk
)

class MyPipelineStack(cdk.Stack):
    def __init__(self, scope: cdk.Construct, serverless_stack: ServerlessCanaryStack, **kwargs) -> None:
        super().__init__(scope, 'MyPipelineStack', **kwargs)

        # Reference to existing pipeline
        pipeline = codepipeline.Pipeline.from_pipeline_arn(self, ...)

        # Define previous stages (source, build, etc.)...

        # Deploy serverless stack stage
        pipeline.add_stage(
            stage_name='DeployServerless',
            actions=[
                cpactions.CloudFormationCreateUpdateStackAction(
                    action_name='DeployStack',
                    template_path=...,
                    stack_name=serverless_stack.stack_name,
                    admin_permissions=True
                )
            ]
        )

Testing with Artillery

After setting up the canary deployment, test it with Artillery, a powerful load testing toolkit.

Installing Artillery

Install Artillery via npm:

npm install -g artillery

Creating Test Scenarios

Develop a test script in YAML to simulate traffic and assess the new version under controlled conditions.

config:
  target: 'https://your-lambda-function-url'
  phases:
    - duration: 60
      arrivalRate: 10
scenarios:
  - flow:
      - get:
          url: "/"

Executing the Test

Run the test and analyze the results:

artillery run test_script.yml

Best Practices

  • Monitor Your Deployment: Keep an eye on AWS CloudWatch and the Artillery reports to understand how your new version is performing.
  • Gradual Rollout: Start with a small percentage of traffic and gradually increase it to ensure stability.
  • Fail Fast: Set up appropriate alarms to detect issues early and roll back if necessary.
  • Iterate and Improve: Use the insights gained from each deployment to refine your process.

Conclusion

Canary deployments in AWS Lambda, complemented by Artillery testing, provide a robust strategy for releasing updates reliably and with minimal user impact. By following the steps outlined above and adhering to best practices, developers can ensure smoother, more reliable deployments, ultimately leading to higher user satisfaction and more robust applications.

Spread the word

Keep reading