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.
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 )
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).
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:
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:
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
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
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.
Mencari hal serupa yang berfungsi dengan fungsi Lambda yang digunakan dari S3.
Kasus penggunaan saya adalah ini:
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:
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
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.
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