AWS CloudFormation关联AWS Lambda 函数自动查找 AMI ID

作者:光环云 田帆

众所周知Amazon Cloudformation为您提供了一种通用语言来描述和预配置您的云环境中的所有基础设施资源。CloudFormation 使您可以跨所有地区和账户使用简单的文本文件以自动化的安全方式为您的应用程序需要的所有资源建模并对其进行预配置。该文件是您的云环境的单一信任源。它会自动化处理和简化预见性地反复创建相关资源组任务,这些资源可以为您的应用程序提供支持。现在,创建和互连应用程序运行所需的全部资源与创建一个 EC2 或 RDS 实例一样简单。

AWS CloudFormation 引入了以下两个概念:模板 (一种 JSON 或 YAML 格式的基于文本的文件,用于描述运行应用程序所需部署的所有 AWS 资源) 和堆栈 (AWS 资源集合,在 AWS CloudFormation 实例化模板时作为一个单元进行创建和管理)。

我们可以自己编写启动堆栈的模板,里面还有很多参数都需要详细的配置才能确保资源可以启动而且正确。通常在利用cloudformation启动堆栈时我们需要把Ec2的AMI ID写进模板。但是,不同的Region中和不同的实例类型的AMI ID是不同的,如果您的模板需要适用于多个Region就需要把不同的AMI ID做成映射(mapping),然后在模板的资源模块ec2的属性中去调用这个映射。下面以JSON格式示例:

您需要查找所需要的AMI在不同Region中的实际ID,例如:ami-8ff710e2。请记住下图中Mappings的名字(AWSRegionArch2AMI)。
AWS CloudFormation关联AWS Lambda 函数自动查找 AMI ID然后我们可以在Resource中Ec2的Properties来引用Mapping中的AMI ID。
AWS CloudFormation关联AWS Lambda 函数自动查找 AMI ID但是这种模式来设计模板不会一劳永逸。因为AMI ID 是否正确取决于您在其中启动堆栈的实例类型和区域。ID 可能会定期更改,比如当通过软件更新方式更新 AMI 时。如果你将 AMI ID 映射到特定实例类型和区域。要更新此 ID,您需要在您的每个模板中手动更改它们。这样会大大增加您的运维成本和时间。

那么下面的文章会指导您利用自定义资源和 AWS Lambda,创建自定义资源并将 Lambda 函数与CloudFormation关联以查找 AMI ID,您可以创建一个函数,用于获取您正在使用的区域和实例类型的最新 AMI 的 ID,从而使您不必维护映射。

模板使用自定义资源类型调用输入值并将它发送到 Lambda 函数。使用此模板时,CloudFormation 会调用此函数并向它发送信息,如请求类型、输入数据和预签名 Amazon Simple Storage Service (Amazon S3) URL。该函数使用这些信息来查找 AMI ID,然后向预签名 URL 发送响应。

在 AWS CloudFormation 获取预签名 URL 位置的响应后,它将继续创建堆栈。AWS CloudFormation 在创建实例时,会使用 Lambda 函数的响应指定实例的 AMI ID。当然我们需要有相关的IAM权限才能完成此次操作,在这里不再赘述权限的设置。

首先您需要在S3中创建一个桶来存放Lambda要执行的函数,并记下储存桶的名字。CloudFormation在创建堆栈的时候来桶中获取Lambda将执行的代码。请把下面的JS代码打包成名字为amilookup.zip的压缩包。然后上传到所创建的S3桶中。
AWS CloudFormation关联AWS Lambda 函数自动查找 AMI IDAWS CloudFormation关联AWS Lambda 函数自动查找 AMI IDAWS CloudFormation关联AWS Lambda 函数自动查找 AMI IDAWS CloudFormation关联AWS Lambda 函数自动查找 AMI ID
在创建堆栈期间,自定义资源将调用 Lambda 函数并等到该函数向预签名 Amazon S3 URL 发送响应。在响应中,该函数返回与 EC2 实例类型和您要在其中创建实例的区域对应的最新 AMI 的 ID。函数的响应数据存储为自定义资源的属性,用于指定 EC2 实例的 AMI ID。

我们来分段详细剖析一下CloudFormation的模板是如何实现的。

Lambda函数的在模板中创建需要声明 AWS::Lambda::Function 资源。需要包含函数的源代码、处理程序名称、运行时环境和执行角色 ARN。
AWS CloudFormation关联AWS Lambda 函数自动查找 AMI IDProperties中的Code用来指定此代码压缩包在S3的位置,S3Bucket为桶名,S3Key为文件名。用Ref来调用。同时调用模板另一端定义好的Module Name模块来形成Lambda的Handler.如下:
AWS CloudFormation关联AWS Lambda 函数自动查找 AMI ID
此代码为JavaScript编写所以Lambda的运行环境在模板中nodejs8.10

此函数的执行时间超出了 3 秒的默认值,因此将超时设置为 30 秒。如果您指定的超时时间不够长,Lambda 可能导致超时,使函数无法完成,从而造成堆栈创建失败。

Lambda的执行角色通过Fn::GetAtt属性来指定,并且能够让Lambda向AWS发送日志和调用EC2 DescribeImages API来获取AMI ID。下面模板展示如何声明相关的Role。
AWS CloudFormation关联AWS Lambda 函数自动查找 AMI ID
获取AMI ID需要CloudFormation与Lambda函数进行关联,我们可以用Fn::GetAtt 来获取刚刚创建Lambda函数的ARN作为ServiceToken,并且调用我们已经声明好的EC2的架构和Region。如下:
AWS CloudFormation关联AWS Lambda 函数自动查找 AMI ID
当 AWS CloudFormation 调用 Lambda 函数时,该函数调用 EC2 DescribeImages API,使用区域和实例架构或操作系统名称来筛选映像的列表。然后,该函数按日期为映像列表排序并返回最新 AMI 的 ID。

那么在要启动的Ec2中是如何来获取AMI ID的呢?通过Fn::GetAtt 内部函数,提供自定义资源的名称以及您要获取的值的属性名。此模板自定义资源名称是 AMIInfo(如上已经声明),属性名称是 Id。
AWS CloudFormation关联AWS Lambda 函数自动查找 AMI ID
到此您应该对于此次的CloudFormation模板的工作原理有了一定的了解。接下来我们开始启动堆栈,可以在console中的Events来监控堆栈执行的过程。并可以从CloudWatch的log中来查看s3生成预签名url和自定义CloudFormation资源(Type: Custom::AMIInfo)的内容和格式。

首先进入CloudFormation的console选择创建一个新的堆栈,然后如下,选择在本地的模板。
AWS CloudFormation关联AWS Lambda 函数自动查找 AMI ID
下一步之后,如下图指定参数,堆栈名称自定义。有的参数我们在模板中已经指定了,您只需要输入实例类型和桶名称即可。
AWS CloudFormation关联AWS Lambda 函数自动查找 AMI ID
我们可以通过Events页签来查看资源的启动顺序,如果有问题会在这里报错,查看响应的错误之后改动模板,然后在重启尝试。可以注意一下图中几个箭头,以从下到上的顺序来创建所需要的资源。
AWS CloudFormation关联AWS Lambda 函数自动查找 AMI ID
现在我们整个堆栈和资源都已经创建完毕,您可以通过输出页签查看获取到的AMI ID,并且审查已启动EC2使用的AMI ID是否与此吻合。
AWS CloudFormation关联AWS Lambda 函数自动查找 AMI IDAWS CloudFormation关联AWS Lambda 函数自动查找 AMI ID

到此虽然的实例已经创建,但是为了更深入的了解CloudFormation,我们还需要进入CloudWatch面板,查看在创建Lambda函数和与CloudFormation交互的过程中所产生的log,从中我们可以看到CloudFormation自定义资源的信息。下图为S3生成响应的预共享签名URL
AWS CloudFormation关联AWS Lambda 函数自动查找 AMI ID
下图为自定义资源请求的状态以及成功获取到的AMI ID。
AWS CloudFormation关联AWS Lambda 函数自动查找 AMI ID
感谢您的阅读。


微信搜索“光环云社群”公众号,了解AWS最新资讯

微信搜索“光环云极客”小程序,加入我们一起推广AWS