演练:使用 Lambda 支持的自定义资源查找 AMI ID
本演练展示了如何使用 Lambda 和自定义资源在 CloudFormation 模板中动态查找亚马逊机器映像(AMI)ID。这有助于简化在 CloudFormation 模板中更新 AMI ID 的方式。
在创建用于启动 Amazon EC2 实例的 CloudFormation 模板时,必须指定一个 AMI ID,类似于将要安装在实例上的操作系统和软件的模板。AMI ID 是否正确取决于您在其中启动的实例类型和 Amazon Web Services 区域。ID 可能会定期更改,比如当使用软件更新来更新 AMI 时。
通常会在 Mappings
部分中指定 AMI ID,该部分将 AMI ID 映射到特定的实例类型和区域。这意味着,要更新 ID,您需要在每个模板中手动更改这些 ID。但是,您可以使用自定义资源和 Lambda 创建函数,用来获取您正在使用的实例类型和区域的最新 AMI 的 ID。这样,就不必在堆栈模板中手动维护 AMI ID、实例类型和区域之间的映射。
此演练演示如何创建自定义资源并将某个 Lambda 函数与它关联以查找 AMI ID。此演练假定您熟悉 Lambda 函数和自定义资源。有关自定义资源及其工作原理的介绍,请参阅使用自定义资源创建自定义预置逻辑。有关 Lambda 的信息,请参阅 Amazon Lambda 开发人员指南。
注意
CloudFormation 是一种免费服务;但是,您需要为您在堆栈中使用的 Amazon 资源(如 Lambda 函数和 EC2 实例)付费,费用按每种资源的现价收取。有关 Amazon 定价的详细信息,请参阅 http://aws.amazon.com
作为创建自定义资源和 Lambda 函数的替代方案,您还可以使用模板中的 Amazon Systems Manager 参数来检索存储在 Systems Manager 参数中的最新 AMI ID 值。这可以使您的模板提高可重用性和更易于维护。有关更多信息,请参阅 使用 CloudFormation 提供的参数类型引用现有的资源和 Systems Manager 参数。
概述
以下步骤概述了此实施过程。
-
将包含 Lambda 函数代码的示例包保存到 Amazon S3 存储桶中,该存储桶应位于您要创建 EC2 实例的同一 Amazon Web Services 区域。
-
使用示例模板创建堆栈,其中具有一个自定义资源、一个 Lambda 函数、一个 EC2 实例以及一个 Lambda 用来调用 Amazon EC2 的 IAM 角色。
-
该堆栈会将该 Lambda 函数与该自定义资源关联起来。创建此堆栈时,CloudFormation 会调用该函数并向其发送请求类型、输入数据和预签名 Amazon S3 URL 等信息。
-
该 Lambda 函数使用输入数据来查找最新的 AMI ID,并将该 AMI ID 作为响应发送到该预签名 URL。
-
CloudFormation 在预签名 URL 位置收到响应,并继续创建堆栈。在创建实例时,CloudFormation 会使用 Lambda 函数提供的 AMI ID 来创建具有最新 AMI 的 EC2 实例。
模板演练
要查看整个示例模板,请参阅:
以下代码段介绍示例模板的相关部分,以帮助您了解如何将 Lambda 函数与自定义资源关联以及如何使用该函数的响应。
AWS::Lambda::Function 资源 AMIInfoFunction
AWS::Lambda::Function
资源指定了函数的源代码、处理程序名称、运行时环境和执行角色的 Amazon 资源名称(ARN)。
-
Code
属性指定上传此示例包的 Amazon S3 位置(存储桶名称和文件名)。示例模板使用输入参数 ("Ref": "S3Bucket"
和"Ref": "S3Key"
) 设置存储桶和文件名,以便在创建堆栈时指定此名称。同样,处理程序名称 (对应于.zip
包中源文件 (JavaScript 文件) 的名称) 也使用了一个输入参数 ("Ref": "ModuleName"
)。源文件为 JavaScript 代码,因此运行时指定为nodejs18.x
。 -
对于此演练中使用的代码,函数的执行时间超出了
3
秒的默认值,因此将超时设置为30
秒。如果您指定的超时时间不够长,Lambda 可能导致超时,使函数无法完成,从而造成堆栈创建失败。 -
Role
属性使用Fn::GetAtt
函数来获取模板中其他地方声明的LambdaExecutionRole
执行角色的 ARN。
JSON
"AMIInfoFunction": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": { "Ref": "S3Bucket" }, "S3Key": { "Ref": "S3Key" } }, "Handler": { "Fn::Join" : [ "", [{ "Ref": "ModuleName" },".handler"] ] }, "Runtime": "nodejs18.x", "Timeout": "30", "Role": { "Fn::GetAtt" : ["LambdaExecutionRole", "Arn"] } } }
YAML
AMIInfoFunction: Type: AWS::Lambda::Function Properties: Code: S3Bucket: !Ref S3Bucket S3Key: !Ref S3Key Handler: !Sub "${ModuleName}.handler" Runtime: nodejs18.x Timeout: 30 Role: !GetAtt LambdaExecutionRole.Arn
AWS::IAM::Role 资源 LambdaExecutionRole
执行角色授予 Lambda 函数向 Amazon 发送日志和调用 EC2 DescribeImages
API 的权限。
JSON
"LambdaExecutionRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": ["lambda.amazonaws.com"]}, "Action": ["sts:AssumeRole"] }] }, "Path": "/", "Policies": [{ "PolicyName": "root", "PolicyDocument": { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents"], "Resource": "arn:aws:logs:*:*:*" }, { "Effect": "Allow", "Action": ["ec2:DescribeImages"], "Resource": "*" }] } }] } }
YAML
LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" Policies: - PolicyName: root PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: arn:aws:logs:*:*:* - Effect: Allow Action: - ec2:DescribeImages Resource: "*"
Custom::AMIInfo 资源 AMIInfo
对于 Linux 和 Windows 模板,自定义资源都会调用与其关联的 Lambda 函数。要将函数与自定义资源关联,可以使用 Fn::GetAtt
内置函数为 ServiceToken
属性指定函数的 ARN。CloudFormation 将自定义资源声明中包含的其他属性(如 Region
和 Architecture
)作为输入发送给 Lambda 函数。Lambda 函数为这些输入属性确定正确的名称和值。
JSON
"AMIInfo": { "Type": "Custom::AMIInfo", "Properties": { "ServiceToken": { "Fn::GetAtt" : ["AMIInfoFunction", "Arn"] }, "Region": { "Ref": "AWS::Region" }, "Architecture": { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } } }
YAML
AMIInfo: Type: Custom::AMIInfo Properties: ServiceToken: !GetAtt AMIInfoFunction.Arn Region: !Ref "AWS::Region" Architecture: Fn::FindInMap: - AWSInstanceType2Arch - !Ref InstanceType - Arch
对于 Windows,自定义资源提供 Windows 版本给 Lambda 函数而不是实例的架构。
JSON
"AMIInfo": { "Type": "Custom::AMIInfo", "Properties": { "ServiceToken": { "Fn::GetAtt": ["AMIInfoFunction", "Arn"] }, "Region": { "Ref": "AWS::Region" }, "OSName": { "Ref": "WindowsVersion" } } }
YAML
AMIInfo: Type: Custom::AMIInfo Properties: ServiceToken: !GetAtt AMIInfoFunction.Arn Region: !Ref "AWS::Region" OSName: !Ref "WindowsVersion"
CloudFormation 调用 Lambda 函数时,该函数调用 EC2 Amazon Web Services 区域 API,使用 DescribeImages
和实例架构或操作系统名称来筛选映像列表。然后,该函数按日期为映像列表排序并返回最新 AMI 的 ID。
在返回最新 AMI 的 ID 时,该函数在响应对象的 Data
属性中向预签名 URL 发送此 ID。数据将被构造为名称/值对,如以下示例所示:
"Data": { "Id": "ami-02354e95b3example" }
AWS::EC2::Instance 资源 SampleInstance
以下代码段说明如何从 Lambda 函数获取数据。它使用 Fn::GetAtt
内部函数,提供自定义资源的名称以及您要获取的值的属性名。在此演练中,自定义资源名称是 AMIInfo
,属性名称是 Id
。
JSON
"SampleInstance": { "Type": "AWS::EC2::Instance", "Properties": { "InstanceType" : { "Ref": "InstanceType" }, "ImageId": { "Fn::GetAtt": [ "AMIInfo", "Id" ] } } }
YAML
SampleInstance: Type: AWS::EC2::Instance Properties: InstanceType: !Ref InstanceType ImageId: !GetAtt AMIInfo.Id
先决条件
您必须首先拥有 Amazon S3 存储桶,然后才能完成创建堆栈的步骤。使用 Lambda 函数创建堆栈时,必须指定包含函数源代码的 Amazon S3 存储桶的位置。存储桶必须位于创建堆栈的同一 Amazon Web Services 区域。有关创建桶的更多信息,请参阅《Amazon Simple Storage Service 用户指南》中的创建桶。
您需要拥有使用 Lambda、Amazon EC2 和 CloudFormation 等所有相关服务的 IAM 权限。
第 1 步:将 Lambda 示例包保存到 Amazon S3
在这一步中,您需要将 Lambda 示例包(.zip
文件)上传到 Amazon S3 存储桶。
该包中含有该 Lambda 函数的源代码和需要的库。就本演练而言,此函数不需要额外的库。
在 Amazon S3 中保存示例包
-
从 Amazon S3 下载示例包。保存文件时,使用示例的文件名
amilookup.zip
或amilookup-win.zip
。 -
在 https://console.aws.amazon.com/s3/home
处打开 Amazon S3 控制台。 -
在屏幕顶部的导航栏中,选择您在其中创建了 Amazon S3 存储桶的 Amazon Web Services 区域。
-
在存储桶列表中,请选择存储桶的名称。记下存储桶名称,因为在创建堆栈时会用到该名称。
-
选择上传。
-
在上传下的文件和文件夹中,将示例包上传到该存储桶。有关更多信息,请参阅《Amazon Simple Storage Service 开发人员指南》中的上传对象。
步骤 2:启动堆栈
在这一步中,您将从示例模板启动一个堆栈。该堆栈包含一个 Lambda 函数、一个 IAM 角色(执行角色)、一个将会调用该函数的自定义资源以及一个将使用该函数的结果的 EC2 实例。
在创建堆栈期间,自定义资源将调用 Lambda 函数并等到该函数向预签名 Amazon S3 URL 发送响应。在响应中,该函数返回与 EC2 实例类型和您要在其中创建实例的 Amazon Web Services 区域 对应的最新 AMI ID。函数的响应数据存储为自定义资源的属性,用于指定 EC2 实例的 AMI ID。
要创建 堆栈,请执行以下操作:
-
通过以下网址打开 CloudFormation 控制台:https://console.aws.amazon.com/cloudformation/
。 -
选择Create Stack(创建堆栈)。
-
在 Template (模板) 部分中,选择 Specify an Amazon S3 template URL (指定 S3 模板 URL),然后复制以下 URL 并粘贴到文本框中:
- Linux 模板
-
https://s3.amazonaws.com/cloudformation-examples/lambda/LambdaAMILookupSample.template
- Windows 模板
-
https://s3.amazonaws.com/cloudformation-examples/lambda/LambdaAMILookupSample-win.template
-
选择下一步。
-
在 Stack name (堆栈名称) 名称字段中,键入
SampleEC2Instance
。 -
在 Parameters 部分,指定您创建的 Amazon S3 存储桶的名称,然后选择 Next。
其他参数的默认值为在示例
.zip
包中使用的相同名称。 -
在本演练中,您无需添加标记或指定高级设置,因此请选择 Next。
-
确保堆栈名称和模板 URL 正确,然后选择 Create。
CloudFormation 可能需要几分钟的时间创建堆栈。要监控进度,可查看堆栈事件。有关更多信息,请参阅 从 CloudFormation 控制台查看堆栈信息。
如果堆栈创建成功,堆栈中的所有资源 (如 Lambda 函数、自定义资源和 EC2 实例) 也会一同创建。您成功使用 Lambda 函数和自定义资源指定了 EC2 实例的 AMI ID。在此模板中,您无需创建和维护 AMI ID 映射。
要查看创建 EC2 实例时使用的是哪个 AMI ID CloudFormation,请查看堆栈输出。
如果 Lambda 函数返回错误,则在 CloudWatch Logs 控制台
步骤 3:清理资源
删除堆栈以清理您创建的所有堆栈资源,从而避免为不必要的资源付费。
删除堆栈
-
从 CloudFormation 控制台中,选择 SampleEC2Instance 堆栈。
-
选择 Actions,然后选择 Delete Stack。
-
在确认消息中,选择 Yes, Delete。
您创建的所有资源都会被删除。
您现已了解如何通过 CloudFormation 创建和使用 Lambda 函数,您可以使用本演练中的示例模板和代码生成其他堆栈和函数。