zl程序教程

您现在的位置是:首页 >  其他

当前栏目

aws cdk 配置 lambda 函数的金丝雀发布

2023-04-18 16:47:06 时间

之前的文章介绍了使用sam框架完成lambda函数的金丝雀发布,这里使用cdk创建lambda函数项目实现此功能

Building CI/CD pipelines for lambda canary deployments using AWS CDK

项目的结构如下图所示

image-20230110233157161

lambda堆栈示例

应用程序和环境配置

#!/usr/bin/env python3
import os
import aws_cdk as cdk

from aws_cdk_lambda.aws_cdk_lambda_stack import AwsCdkPythonStack

app = cdk.App()

# 获取cdk.out中context中的qa键值
environment_type = "qa"
environment_context = app.node.try_get_context(environment_type)
region = environment_context["region"]
account = app.node.try_get_context("account")
stack_name = f'{app.node.try_get_context("prefix")}-{environment_type}'

print(stack_name)

AwsCdkPythonStack(app, "AwsCdkPythonStack",
    env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')),
    )

app.synth()

修改cdk.out,添加以下参数

context:{
	"qa": {
          "region": "cn-north-1",
          "lambda": {
            "name": "cdk-workshop-function-qa",
            "alias": "live",
            "stage": "qa"
          },
          "tags": {
            "App":"cdk-workshop",
            "Environment": "QA",
            "IaC": "CDK"
          }
     }
}

创建lambda堆栈

from datetime import datetime
from aws_cdk import Stack, RemovalPolicy
from constructs import Construct
from aws_cdk.aws_lambda import Function, Runtime, Code, Alias, VersionOptions
from aws_cdk.aws_apigateway import LambdaRestApi, StageOptions
from aws_cdk.aws_cloudwatch import Alarm, ComparisonOperator
from aws_cdk.aws_codedeploy import LambdaDeploymentGroup, LambdaDeploymentConfig

class CdkWorkshopStack(Stack):

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

        environment_type = self.node.try_get_context("environmentType")
        context = self.node.try_get_context(environment_type)
        self.alias_name = context["lambda"]["alias"]
        self.stage_name = context["lambda"]["stage"]
        current_date =  datetime.today().strftime('%d-%m-%Y')

        my_lambda = Function(
            scope = self,
            id = "MyFunction",
            function_name= context["lambda"]["name"],
            handler = "handler.lambda_handler",
            runtime = Runtime.PYTHON_3_9,
            code = Code.from_asset("lambda"),
            current_version_options = VersionOptions(
                description = f'Version deployed on {current_date}',
                removal_policy = RemovalPolicy.RETAIN
            )
        )
		
        # 每次cdk部署命令运行时自动创建一个新版本,修改版本的删除策略为保留
        new_version = my_lambda.current_version
        new_version.apply_removal_policy(RemovalPolicy.RETAIN)

        alias = Alias(
            scope = self,
            id = "FunctionAlias",
            alias_name = self.alias_name,
            version = new_version
        )
        
          # LambdaRestApi(
        #     scope = self,
        #     id = "RestAPI",
        #     handler = alias,
        #     deploy_options = StageOptions(stage_name=self.stage_name)
        # )

        failure_alarm = Alarm(
            scope = self,
            id = "FunctionFailureAlarm",
            metric = alias.metric_errors(),
            threshold = 1,
            evaluation_periods = 1,
            alarm_description = "The latest deployment errors > 0",
            alarm_name = f'{self.stack_name}-canary-alarm',
            comparison_operator = ComparisonOperator.GREATER_THAN_THRESHOLD,
        )

        LambdaDeploymentGroup(
            scope = self,
            id = "CanaryDeployment",
            alias = alias,
            deployment_config = LambdaDeploymentConfig.CANARY_10_PERCENT_5_MINUTES,
            alarms = [failure_alarm]
        )

添加应用代码

$ mkdir lambda && touch lambda/handler.py
$ cat handler.py
import json
def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from CDK!')
    }

生成的cfn模板会创建一系列相关资源,示意图如下。主要创建的资源

  • lambda,包括lambda执行角色,alias和version
  • apigateway,通过deployment创建resource,在resource上创建method,还有apigateway调用resource,apigateway调用lambda的权限。对于lambda的部署可以省略此部分
  • codedeploy,包括application和部署组他
  • 当发生lambda函数更新时,cloudformation通过updatepolicy

在这里插入图片描述

对应堆栈资源如下

在这里插入图片描述

更新lambda函数重新部署cdk,查看堆栈事件,堆栈更新策略开始执行

在这里插入图片描述

部署触发

在这里插入图片描述

金丝雀部署开始转移流量

在这里插入图片描述

通过将alias指向不同version完成流量切换

在这里插入图片描述

pipeline实现部署

最后还有一个创建pipeline堆栈的示例

https://catalog.us-east-1.prod.workshops.aws/workshops/5195ab7c-5ded-4ee2-a1c5-775300717f42/en-US/cicd/cdk-pipelines/pipeline

有一个shellstep比较好奇是什么,创建出来看看

class PipelineStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        repository = Repository.from_repository_arn(
            self,
            "CodeCommitRepo",
            'arn:aws-cn:codecommit:cn-north-1:xxxxxxx:temptest'
        )

        self.source_stage = CodePipelineSource.code_commit(repository,"master")
        pipeline = CodePipeline(self, "Pipeline",
            pipeline_name = "cdk-pipeline",
            synth = ShellStep(
                "Synth",
                input = self.source_stage,
                install_commands=[
                    "npm install -g aws-cdk",
                    "pip3 install -r requirements.txt",
                    "ACCOUNT=$(aws sts get-caller-identity | jq -r .Account)"
                    ],
                commands=[
                    "cdk synth -c account=$ACCOUNT -c environmentType=$ENV_TYPE"
                    ],
            )
        )

shellstep实际上是个build阶段,command写到了buildspec.yaml

在这里插入图片描述