4

我在开发应用程序中有 20 多个 lambda 函数。还有一个包含大量通用代码的 lambda 层。

一个 Lambda 函数,将它与层的特定版本挂钩,每次更新层时,它都会生成一个新版本。由于它是一个开发应用程序,我几乎每天都有一个新版本的层。这会在每天必须触及的 lambda 函数上造成混乱 - 以升级层版本。

我知道在生产中冻结 lambda 函数的代码很重要,并且必须将 lambda 函数的一个版本挂钩到层的一个版本。

但是,对于开发环境,是否可以防止每次更新图层时生成新的图层版本?或者配置 lambda 函数,让最新的 lambda 版本总是指最新的 layer 版本?

4

3 回答 3

3

不幸的是,目前无法引用最新的版本,并且层版本没有别名的概念。

最好的建议是自动执行此操作,以便每当您创建新的 Lambda 层版本时,它都会更新当前包含此 Lambda 层的所有 Lambda 函数。

要创建此事件触发器,请创建一个使用其事件来侦听事件的 CloudWatch 函数PublishLayerVersion

然后让它触发一个 Lambda,该 Lambda 将为每个 Lambda 触发update-function-layers函数,以将其层替换为新层。

于 2020-07-19T10:34:46.100 回答
3

从@Chris 回答增强,您还可以在堆栈中使用 lambda 支持的自定义资源,并使用此 lambda 使用新层 ARN 更新目标配置。我注意到这一点,以防几天前我发现这个线程时有人有类似的需求。

关于这个解决方案有一些注意事项:

  • 客户资源的 lambda 必须将状态响应发送回触发 CloudFormation (CFN) 端点,否则 CFN 堆栈将挂起直到超时(大约一个小时或更长时间,如果您对此 lambda 有问题,这是一个痛苦的过程,是小心点)
  • 发送响应的简单方法,您可以使用 cfnresponse(pythonic 方式),当您使用 CFN lambda 内联代码时,这个库神奇地可用(CFN 在使用内联代码处理 CFN 时设置此库)并且必须有一行 'import cfnresponse' : D
  • CFN 创建后不会接触到自定义资源,因此当您更新堆栈以更改新层时,lambda 不会触发。使它移动的一个技巧是使用具有自定义属性的自定义资源,然后您将更改此属性,每次执行堆栈时都会更改某些内容,层版本 arn。所以这个自定义资源将被更新,意味着这个资源的 lambda 将在堆栈更新时被触发。
  • 不知道为什么用 AWS::Serverless:Layer 更改了 lambda 层的逻辑名称,所以我不能依赖该层逻辑名称,但我仍然有 !Ref 它的 ARN

这是一个示例代码

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  myshared-libraries layer

Resources:
  LambdaLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
        LayerName: !Sub MyLambdaLayer
        Description: Shared library layer
        ContentUri: my_layer/layerlib.zip
        CompatibleRuntimes: 
          - python3.7
  ConsumerUpdaterLambda:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: consumer-updater
      InlineCode: |
        import os, boto3, json
        import cfnresponse        
        def handler(event, context):
            print('EVENT:[{}]'.format(event))
            if event['RequestType'].upper() == 'UPDATE':
                shared_layer = os.getenv("DB_LAYER")
                lambda_client = boto3.client('lambda')
                consumer_lambda_list = ["target_lamda"]
                for consumer in consumer_lambda_list:
                  try:
                      lambda_name = consumer.split(':')[-1]
                      lambda_client.update_function_configuration(FunctionName=consumer, Layers=[shared_layer])
                      print("Updated Lambda function: '{0}' with new layer: {1}".format(lambda_name, shared_layer))
                  except Exception as e:
                      print("Lambda function: '{0}' has exception: {1}".format(lambda_name, str(e)))
            responseValue = 120
            responseData = {}
            responseData['Data'] = responseValue
            cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
      Handler: index.handler
      Runtime: python3.7
      Role: !GetAtt ConsumerUpdaterRole.Arn
      Environment:
        Variables:
          DB_LAYER: !Ref LambdaLayer

  ConsumerUpdaterRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
          Action: sts:AssumeRole
      ManagedPolicyArns:
      - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
      - PolicyName:
          Fn::Sub: updater-lambda-configuration-policy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - lambda:GetFunction
            - lambda:GetFunctionConfiguration
            - lambda:UpdateFunctionConfiguration
            - lambda:GetLayerVersion
            - logs:DescribeLogGroups
            - logs:CreateLogGroup
            Resource: "*"

  ConsumerUpdaterMacro:
    DependsOn: ConsumerUpdaterLambda
    Type: Custom::ConsumerUpdater
    Properties:
      ServiceToken: !GetAtt ConsumerUpdaterLambda.Arn
      DBLayer: !Ref LambdaLayer

Outputs:
  SharedLayer:
    Value: !Ref LambdaLayer
    Export:
      Name: MySharedLayer

另一种选择是使用堆栈通知 ARN,它将所有堆栈事件发送到定义的 SNS,您将在其中使用它来触发更新 lambda。在您的 lambda 中,您将使用 AWS::Lambda::Layer 资源过滤 SNS 消息正文(这是一个可读的 json 喜欢的格式字符串),然后获取层 ARN 的 PhysicalResourceId。如何将 SNS 主题加入您的堆栈,请使用 CLI sam/cloudformation deploy --notification-arns 选项。不幸的是,CodePipeline 不支持此配置选项,因此您只能与 CLI 一起使用

用于 lambda 的示例代码,用于使用资源数据提取/过滤 SNS 消息正文

import os, boto3, json

def handler(event, context):
    print('EVENT:[{}]'.format(event))
    resource_data = extract_subscription_msg(event['Records'][0]['Sns']['Message'])
    layer_arn = ''
    if len(resource_data) > 0:
        if resource_data['ResourceStatus'] == 'CREATE_COMPLETE' and resource_data['ResourceType'] == 'AWS::Lambda::LayerVersion':
            layer_arn = resource_data['PhysicalResourceId']
    if layer_arn != '':
        lambda_client = boto3.client('lambda')
        consumer_lambda_list = ["target_lambda"]
        for consumer in consumer_lambda_list:
            lambda_name = consumer.split(':')[-1]
            try:
                lambda_client.update_function_configuration(FunctionName=consumer, Layers=[layer_arn])
                print("Update Lambda: '{0}' to layer: {1}".format(lambda_name, layer_arn))
            except Exception as e:
                print("Lambda function: '{0}' has exception: {1}".format(lambda_name, str(e)))
    return

def extract_subscription_msg(msg_body):
    result = {}
    if msg_body != '':
        attributes = msg_body.split('\n')
        for attr in attributes:
            if attr != '':
                items = attr.split('=')
                if items[0] in ['PhysicalResourceId', 'ResourceStatus', 'ResourceType']:
                    result[items[0]] = items[1].replace('\'', '')
    return result
于 2020-10-21T23:53:25.020 回答
1

可以根据https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/lambda_layer_version使用附加数据语句得出层的最新版本号

因此,在您的定义模块中,您将拥有原始图层资源定义

resource "aws_lambda_layer_version" "layer_mylib" {
  filename   = "layer_mylib.zip"
  layer_name = "layer_mylib"

  compatible_runtimes = ["python3.6", "python3.7", "python3.8"]
}

然后要获取最新版本的 ARN,请使用

data "aws_lambda_layer_version" "mylatest" {
  layer_name = aws_lambda_layer_version.layer_mylib.layer_name
}

然后data.aws_lambda_layer_version.mylatest.arn

将给出包含最新版本号的参考,可以通过放置来检查

output {
  value = data.aws_lambda_layer_version.mylatest.arn 
}

在你的common.tf

于 2020-12-18T19:43:54.257 回答