it-swarm.asia

Bagaimana cara membuat versi baru dari fungsi Lambda menggunakan CloudFormation?

Saya mencoba membuat versi baru dari fungsi Lambda menggunakan CloudFormation.

Saya ingin memiliki beberapa versi dari fungsi Lambda yang sama sehingga saya dapat (a) menunjuk alias pada versi yang berbeda - seperti DEV dan PROD - dan (b) dapat memutar kembali ke versi sebelumnya.

Ini adalah definisi dari versi Lambda saya:

LambdaVersion:
  Type: AWS::Lambda::Version
  Properties:
    FunctionName:
      Ref: LambdaFunction

Versi dibuat ketika menjalankan "aws cloudformation create-stack" tetapi perintah "aws cloudformation update-stack" berikutnya tidak melakukan apa-apa. Tidak ada versi Lambda baru yang dibuat.

Saya mencoba untuk mendapatkan versi baru dari fungsi Lambda yang dibuat setelah saya mengunggah file Zip baru ke S3 dan kemudian menjalankan "update-stack". Bisakah saya melakukannya dengan CloudFormation? Apakah AWS :: Lambda :: Versi benar-benar rusak (seperti yang disebutkan di sini https://github.com/hashicorp/terraform/issues/6067#issuecomment-211708071 ) atau saya hanya tidak mendapatkan sesuatu?

Perbarui 1/11/17 Balasan resmi dari dukungan Amazon: "... untuk setiap versi baru yang akan diterbitkan, Anda perlu mendefinisikan tambahan (sic) AWS: : Lambda :: Versi sumber daya ... "

Tim AWS CloudFormation/Lambda, jika Anda membaca ini - ini tidak dapat diterima. Memperbaikinya.

23
boris

AWS :: Lambda :: Versi tidak berguna. Anda harus menambahkan sumber daya baru untuk setiap versi Lambda. Jika Anda ingin menerbitkan versi baru untuk setiap pembaruan Cloudformation, Anda harus meretas sistem.

Saya memecahkan masalah ini dengan membuat sumber daya khusus yang didukung Lambda yang dipicu untuk setiap penyebaran. Di dalam Lambda ini, saya membuat versi baru untuk fungsi Lambda yang diberikan dalam parameter.

Untuk sumber Lambda, Anda dapat memeriksa http://serverless-Arch-eu-west-1.s3.amazonaws.com/serverless.Zip

Berikut adalah contoh Cloudformation menggunakan fungsi Lambda Deployment ini (Anda mungkin perlu beberapa modifikasi):

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters": {
    "DeploymentTime": {
      "Type": "String",
      "Description": "It is a timestamp value which shows the deployment time. Used to rotate sources."
    }
  },
  "Resources": {
    "LambdaFunctionToBeVersioned": {
      "Type": "HERE_DEFINE_YOUR_LAMBDA"
    },
    "DeploymentLambdaRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "lambda.amazonaws.com"
                ]
              },
              "Action": [
                "sts:AssumeRole"
              ]
            }
          ]
        },
        "Path": "/",
        "ManagedPolicyArns": [
          "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
        ],
        "Policies": [
          {
            "PolicyName": "LambdaExecutionPolicy",
            "PolicyDocument": {
              "Version": "2012-10-17",
              "Statement": [
                {
                  "Effect": "Allow",
                  "Action": [
                    "lambda:PublishVersion"
                  ],
                  "Resource": [
                    "*"
                  ]
                }
              ]
            }
          }
        ]
      }
    },
    "DeploymentLambda": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Role": {
          "Fn::GetAtt": [
            "DeploymentLambdaRole",
            "Arn"
          ]
        },
        "Handler": "serverless.handler",
        "Runtime": "nodejs4.3",
        "Code": {
          "S3Bucket": {
            "Fn::Sub": "serverless-Arch-${AWS::Region}"
          },
          "S3Key": "serverless.Zip"
        }
      }
    },
    "LambdaVersion": {
      "Type": "Custom::LambdaVersion",
      "Properties": {
        "ServiceToken": {
          "Fn::GetAtt": [
            "DeploymentLambda",
            "Arn"
          ]
        },
        "FunctionName": {
          "Ref": "LambdaFunctionToBeVersioned"
        },
        "DeploymentTime": {
          "Ref": "DeploymentTime"
        }
      }
    }
  }
}

(Penafian: Kode ini adalah bagian dari buku saya, untuk informasi lebih lanjut tentang Lambda & API Gateway Anda dapat memeriksa: https://www.Amazon.com/Building-Serverless-Architectures-Cagatay-Gurturk/dp/1787129195 )

15

Saya memiliki kasus penggunaan yang sama (perlu menggunakan CloudFormation untuk mengelola fungsi lambda untuk digunakan @Edge di CloudFront, yang selalu diperlukan versi fungsi lambda, bukan $LATEST) dan pencarian saya mendarat di pertanyaan ini terlebih dahulu, tetapi setelah sedikit lebih menggali Saya senang menemukan sekarang ada dukungan asli untuk versi lambda otomatis dengan fitur AutoPublishAlias baru dari AWS Serverless Application Model (pada dasarnya satu set tambahan opsional konstruksi tingkat tinggi untuk templat CloudFormation Anda).

Diumumkan di sini: https://github.com/awslabs/serverless-application-model/issues/41#issuecomment-347723981

Untuk detailnya lihat:

Pada dasarnya Anda memasukkan AutoPublishAlias dalam definisi AWS::Serverless::Function Anda:

MyFunction:
  Type: "AWS::Serverless::Function"
  Properties:
    # ...
    AutoPublishAlias: MyAlias

Dan kemudian di tempat lain dalam templat CloudFormation Anda dapat merujuk versi terbaru yang diterbitkan sebagai !Ref MyFunction.Version (sintaksis yaml).

14
Rob Peters

AWS::Lambda::Version resource hanya mewakili satu versi fungsi Lambda yang diterbitkan - itu tidak akan secara otomatis menerbitkan versi baru pada setiap pembaruan kode Anda. Untuk mencapai ini, Anda memiliki dua opsi:

1. Sumber daya khusus

Anda dapat menerapkan Sumber Daya Kustom Anda sendiri yang memanggil PublishVersion pada setiap pembaruan.

Untuk pendekatan ini, Anda masih perlu mengubah setidaknya satu Parameter setiap kali Anda memperbarui tumpukan Anda, untuk memicu pembaruan pada Sumber Daya Kustom yang akan memicu tindakan PublishVersion. (Namun, Anda tidak harus benar-benar memperbarui templat.)

Inilah contoh lengkap dan berfungsi:

 Launch Stack

Description: Publish a new version of a Lambda function whenever the code is updated.
Parameters:
  Nonce:
    Description: Change this string when code is updated.
    Type: String
    Default: "Test"
Resources:
  MyCustomResource:
    Type: Custom::Resource
    Properties:
      ServiceToken: !GetAtt MyFunction.Arn
      Nonce: !Ref Nonce
  MyFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          var response = require('cfn-response');
          exports.handler = function(event, context) {
            return response.send(event, context, response.SUCCESS, {Result: '${Nonce}'});
          };
      Runtime: nodejs4.3
  LambdaDeploy:
    Type: Custom::LambdaVersion
    Properties:
      ServiceToken: !GetAtt LambdaDeployFunction.Arn
      FunctionName: !Ref MyFunction
      Nonce: !Ref Nonce
  LambdaDeployFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: "index.handler"
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          var AWS = require('aws-sdk');
          var response = require('cfn-response');
          exports.handler = (event, context) => {
            console.log("Request received:\n", JSON.stringify(event));
            if (event.RequestType == 'Delete') {
              return response.send(event, context, response.SUCCESS);
            }
            var lambda = new AWS.Lambda();
            lambda.publishVersion({FunctionName: event.ResourceProperties.FunctionName}).promise().then((data) => {
              return response.send(event, context, response.SUCCESS, {Version: data.Version}, data.FunctionArn);
            }).catch((e) => {
              return response.send(event, context, response.FAILED, e);
            });
          };
      Runtime: nodejs4.3
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal: {Service: [lambda.amazonaws.com]}
          Action: ['sts:AssumeRole']
      Path: /
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
      - PolicyName: PublishVersion
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action: ['lambda:PublishVersion']
            Resource: '*'
Outputs:
  LambdaVersion:
    Value: !GetAtt LambdaDeploy.Version
  CustomResourceResult:
    Value: !GetAtt MyCustomResource.Result

2. Preprosesor template

Anda dapat menggunakan preprosesor template seperti embedded Ruby (atau hanya memperbarui template Anda secara manual pada setiap penyebaran) untuk mempublikasikan Versi baru pada setiap pembaruan kode Anda dengan mengubah AWS::Lambda::Version sumber daya ID Logical setiap kali kode Anda diperbarui.

Contoh:

# template.yml
Description: Publish a new version of a Lambda function whenever the code is updated.
<%nonce = Rand 10000%>
Resources:
  LambdaVersion<%=nonce%>:
    Type: AWS::Lambda::Version
    Properties:
      FunctionName: !Ref MyFunction
  MyCustomResource:
    Type: Custom::Resource
    Properties:
      ServiceToken: !GetAtt MyFunction.Arn
      Nonce: <%=nonce%>
  MyFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          var response = require('cfn-response');
          exports.handler = function(event, context) {
            return response.send(event, context, response.SUCCESS, {Result: '<%=nonce%>'});
          };
      Runtime: nodejs4.3
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal: {Service: [lambda.amazonaws.com]}
          Action: ['sts:AssumeRole']
      Path: /
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Outputs:
  LambdaVersion:
    Value: !GetAtt LambdaVersion<%=nonce%>.Version
  CustomResourceResult:
    Value: !GetAtt MyCustomResource.Result

Untuk membuat/memperbarui tumpukan saat melewati template.yml melalui preprosesor template erb, jalankan:

aws cloudformation [create|update]-stack \
  --stack-name [stack_name] \
  --template-body file://<(Ruby -rerb -e "puts ERB.new(ARGF.read).result" < template.yml) \
  --capabilities CAPABILITY_IAM
7
wjordan

Jawaban diperbarui untuk Februari 2018

Anda dapat menggunakan AWS SAM (Model Aplikasi Tanpa Server) , dan perintah sam package dan sam deploy untuk memperbarui Lambda. Mereka mirip dengan perintah aws cloudformation package dan aws cloudformation deploy, tetapi juga memungkinkan Anda memperbarui versi Lambda secara otomatis.

SAM dapat mengemas kode Anda (atau mengambil paket Zip yang Anda buat sebaliknya), mengunggahnya ke S3, dan memperbarui Versi Lambda dari $LATEST. (Jika ini yang Anda butuhkan, ini juga dapat dilakukan dengan aws cloudformation, tanpa SAM; contoh kode sama seperti di bawah ini, tetapi hanya menggunakan deklarasi standar CloudFormation). Kemudian, dengan SAM, jika dikonfigurasi sesuai, Anda juga dapat secara otomatis menerbitkan Versi dan memperbarui Alias ​​untuk menunjukkannya. Itu juga bisa, secara opsional, menggunakan AWS CodeDeploy untuk secara bertahap memindahkan lalu lintas dari Versi sebelumnya ke versi baru, dan kembalikan jika terjadi kesalahan. Semua ini dijelaskan di penyebaran Lambda Aman .


Secara teknis, idenya adalah bahwa setiap kali Anda memperbarui tumpukan, Anda memerlukan AWS::Lambda::Function's Code Anda untuk menunjuk ke paket new dalam S3. Ini akan memastikan bahwa ketika Anda memperbarui tumpukan, versi Lambda $ TERBARU akan diperbarui dari paket baru. Kemudian, Anda juga dapat mengotomatiskan penerbitan Versi baru dan mengalihkan Alias ​​ke versi itu. 

Untuk itu, buat templat SAM, yang mirip dengan (superset) templat CloudFormation. Ini mungkin termasuk deklarasi spesifik SAM, seperti yang untuk AWS::Serverless::Function di bawah ini. Arahkan Code ke direktori kode sumber (atau Zip yang sudah dikemas), dan setel properti AutoPublishAlias.

...

MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      ...  # all usual CloudFormation properties are accepted 
      AutoPublishAlias: dev  # will publish a Version and create/update Alias `dev` to point to it
      Code: ./my/lambda/src
...

Menjalankan:

$ sam package --template-file template.yaml --output-template-file packaged.yaml --s3-bucket my-bucket

Paket direktori ini memuat konten sebagai Zip (jika Code sudah bukan Zip), mengunggahnya ke S3 di bawah kunci autogenerated baru, dan menghasilkan templat CloudFormation akhir ke packaged.yaml, yang memberikan Anda referensi Code yang tepat ke dalamnya; seperti ini:

...
MyFunction:
    Properties:
      Code:
        S3Bucket: my-bucket
        S3Key: ddeeaacc44ddee33ddaaee223344
...

Sekarang Anda dapat menggunakan packaged.yaml yang dihasilkan dengan SAM, untuk membuat Versi fungsi:

sam deploy --template-file packaged.yaml --stack-name my-stack [--capabilities ...]

Ini akan memperbarui versi $LATEST Lambda, dan, jika AutoPublishAlias didefinisikan, terbitkan sebagai Versi baru dan perbarui Alias ​​untuk menunjuk ke Versi yang baru diterbitkan.

Lihat contoh dalam SAM GitHub repo untuk kode templat yang lengkap.

6
Timur

Mencari hal serupa yang berfungsi dengan fungsi Lambda yang digunakan dari S3.

Kasus penggunaan saya adalah ini:

  • Anda memiliki template cloudformation yang membuat fungsi Lambda dari lokasi bucket S3
  • Anda perlu memperbarui fungsi ini sehingga Anda membuat perubahan kode secara lokal dan Dorong perubahan ke S3
  • Anda sekarang ingin Mendorong perubahan ini ke Lambda sehingga Anda mencoba memperbarui tumpukan dan cloudformation mengatakan tidak ada perubahan untuk memperbarui sehingga Anda harus menggunakan memperbarui kode secara manual menggunakan konsol AWS Lambda.

Tidak senang dengan ini, saya mencari alternatif dan menemukan pertanyaan ini. Tidak ada jawaban yang tepat untuk saya, jadi saya telah mengambil beberapa ide dan mengadaptasi jawaban di sini dan membuat versi saya sendiri ditulis dengan Python.

Kode ini diadaptasi dari jawaban dari @wjordan sehingga kredit kepadanya untuk ide dan jawaban asli. Perbedaannya adalah:

  • Ini ditulis dengan Python
  • Ini bekerja dengan kode Lambda yang digunakan dari ember S3
  • Itu memperbarui kode dan menerbitkan versi baru

Anda memerlukan parameter nonce. Anda mengubah nilai parameter ini ketika kode perlu diterbitkan ulang ke Lambda. Ini untuk memastikan bahwa cloudformation akan memperbarui sumber daya khusus Anda. Ketika sumber daya kustom diperbarui, itu akan menjalankan kode Python yang pada akhirnya memperbarui kode Lambda Anda.

Semoga ini bisa membantu seseorang.

Description: Publish a new version of a Lambda function whenever the code is updated.
Parameters:
  Nonce:
    Description: Change this string when code is updated.
    Type: String
    Default: "Test"
Resources:
  MyCustomResource:
    Type: Custom::Resource
    Properties:
      ServiceToken: !GetAtt MyFunction.Arn
      Nonce: !Ref Nonce
  MyFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        S3Bucket: BucketContainingYourLambdaFunction
        S3Key: KeyToYourLambdaFunction.Zip
      Runtime: "python3.6"
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal: {Service: [lambda.amazonaws.com]}
          Action: ['sts:AssumeRole']
      Path: /
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
  LambdaDeployCustomResource:
    Type: Custom::LambdaVersion
    Properties:
      ServiceToken: !GetAtt LambdaDeployFunction.Arn
      FunctionName: !Ref MyFunction
      S3Bucket: BucketContainingYourLambdaFunction
      S3Key: KeyToYourLambdaFunction.Zip
      Nonce: !Ref Nonce
  LambdaDeployFunction:
    Type: AWS::Lambda::Function
    DependsOn: LambdaDeployFunctionExecutionRole
    Properties:
      Handler: "index.handler"
      Role: !GetAtt LambdaDeployFunctionExecutionRole.Arn
      Code:
        ZipFile: !Sub |
          import boto3
          import json
          import logging
          import cfnresponse
          import time
          from botocore.exceptions import ClientError

          def handler(event, context):
            logger = logging.getLogger()
            logger.setLevel(logging.INFO)
            logger.info (f"Input parameters from cloud formation: {event}")
            responseData = {}
            if (event["RequestType"] == 'Delete'):
              logger.info("Responding to delete event...")
              cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)

            try:            
              lambdaClient = boto3.client('lambda')
              s3Bucket = event['ResourceProperties']['S3Bucket']
              s3Key = event['ResourceProperties']['S3Key']
              functionName = event['ResourceProperties']['FunctionName']
              logger.info("Updating the function code for Lambda function '{}' to use the code stored in S3 bucket '{}' at key location '{}'".format(functionName, s3Bucket, s3Key))
              logger.info("Sleeping for 5 seconds to allow IAM permisisons to take effect")
              time.sleep(5)             
              response = lambdaClient.update_function_code(
                FunctionName=functionName,
                S3Bucket='{}'.format(s3Bucket),
                S3Key='{}'.format(s3Key),
                Publish=True)
              responseValue = "Function: {}, Version: {}, Last Modified: {}".format(response["FunctionName"],response["Version"],response["LastModified"])
              responseData['Data'] = responseValue
              cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, response["FunctionArn"])
            except ClientError as e:
              errorMessage = e.response['Error']['Message']
              logger.error(errorMessage)
              cfnresponse.send(event, context, cfnresponse.FAILED, responseData)
      Runtime: "python3.6"
      Timeout: "30"
  LambdaDeployFunctionExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal: 
            Service: lambda.amazonaws.com
          Action: 
            - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
      - PolicyName: ReadS3BucketContainingLambdaCode
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action: 
              - s3:GetObject              
            Resource: ArnOfS3BucketContainingLambdaCode/*
      - PolicyName: UpdateCodeAndPublishVersion
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action: 
              - lambda:UpdateFunctionCode
              - lambda:PublishVersion
            Resource: '*'
Outputs:
  LambdaVersion:
    Value: !GetAtt LambdaDeploy.Version
  CustomResourceResult:
    Value: !GetAtt MyCustomResource.Result 
2
CarlR

Sayangnya, ini tidak mungkin dilakukan dengan menggunakan CloudFormation. Anda perlu menambahkan bagian AWS::Lambda::Version baru di templat CloudFormation Anda untuk setiap versi. 

Solusi terdekat adalah membuat temp .erb dan membuatnya menghasilkan templat CloudFormation dengan semua versi.

1
Sinan Gedik
  1. Kami dapat membuat paket penyebaran Lambda; 
  2. Lulus paket Lambda Dengan versi sebagai salah satu parameter Formasi Cloud, mis. ... "LambdaPakcageNameWithVersion"; 
  3. Gunakan "LambdaPakcageNameWithVersion" sebagai kunci kode Lambda s3; 
  4. Paket Lamdba baru akan digunakan ketika menjalankan perintah aws-cli ke Memperbarui tumpukan cloudformation atau menjalankan pipa CI/CD.

  MyLambda:
    Type: AWS::Lambda::Function
    Properties:
      Role: LambdaRole
      Code:
        S3Bucket: LambdaPackageS3Bucket
        S3Key: !Sub "${LambdaPakcageNameWithVersion}"
      FunctionName: LambdaFunctionName
      Handler: lambda_function.lambda_handler
      Runtime: python3.6
      Timeout: 60

0
Jerry