我正在尝试为 AWS 中的 Web 服务设置构建管道。计划是让服务在 Auto Scale 组中运行,并使用 Jenkins 构建一次性 EC2 实例,运行测试,并在成功后对实例进行映像并将其传递给现有的 Auto Scale 组。
为了创建所有必要的资源,我创建了两个 CloudFormation 模板,一个用于构建 Autoscale 组和周围资源,另一个用于构建一次性可测试实例。
不过,我注意到了一个问题:每个区域和每个 EC2 实例类型都有不同的基础镜像。这意味着,Jenkins 需要知道要构建的区域和实例类型。
理想的解决方案是一种选择目标 CloudFormation 堆栈并从那里提取信息(区域、实例类型等)的方法。这意味着如果我们对 Auto Scale Group 进行任何更改,它们将自动反映在 Jenkins 构建中。如果我们创建了第二个组,我们可以复制 Jenkins 作业并更改一个参数以将其指向新的堆栈。但这似乎不是一个选择……
这些是我能想到的潜在解决方案,但我并不特别喜欢其中任何一个:
在两个模板中对这些信息进行硬编码
这违背了 CF 模板的想法,因为我理想情况下希望保持这些东西的灵活性。将信息发送到 API,然后让 Jenkins 查找
这是相当多的额外工作,因为我必须构建和维护一台机器来存储几个变量。更不用说,我不确定我是否可以从脚本将信息输出到 API,因此必须将其包装在另一个脚本中,以便从 AWS CLI 工具中获取信息。对最终图像进行分类
我可以允许所有参数具有灵活性,然后根据这些参数对最终图像进行分类。然后我可以确保自动缩放组仅加载具有正确参数的图像。这可以防止使用不正确的基础图像,但可能会导致使用错误的信息进行构建(如果我们更改一个,我们必须记住更改另一个)。
感觉我想做的事情不应该那么难,但我却想不出最好的方法来实现它。
答案1
CloudFormation 包含一个伪参数AWS::Region
,可用于查询其运行所在的区域。请参阅http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/pseudo-parameter-reference.html
您可以使用映射来获取 AMI 映像列表,并根据区域选择 AMI。请参阅http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-findinmap.html
通常,AMI 的选择不受实例类型的影响。但是,我不熟悉您的用例,所以这可能很重要。
至于您的工作流程,我不会“更改”您的 Autoscaling 组。应该采取另一种方式:更改您的 CloudFormation 模板,然后更新您的堆栈以更新您的 Autoscaling 组。
因此你会:
- 从您的 CFN 模板构建您的单一实例,确认它正常工作并构建您的新 AMI。
- 获取新的 AMI 映像 ID 并更新您的 Autoscale 组的 CFN 模板。
- 通过更新堆栈来更新 Autoscale 组
直接对自动扩展组进行更改的危险在于,这些更改不会反映在堆栈的模板中。如果您必须重建堆栈或制作副本,则会丢失这些更改。理想情况下,从 CloudFormation 堆栈创建的资源应被视为只读,并且只能通过更改 CFN 模板进行更新。
答案2
比上面列出的选项稍微好一点的选项(实际上也是可能的)是使用 AWS CLI 工具获取有关当前堆栈的信息。这可以包装在脚本中以获取返回的 JSON 并找到所需的参数/输出。
通过将其移动到外部脚本而不是将其放在 CloudFormation 模板中,它仍然会稍微混淆它,所以我将保留这个问题,希望有人有更好的答案。
这是一个极其简单的例子:
<?php
$result = `aws cloudformation describe-stacks --stack-name=GROUPSTACKNAME`;
$stack = json_decode($result);
$stack = $stack->Stacks[0];
if(!$stack) {
throw new Exception("Stack no found");
}
$parameters = [];
foreach($stack->Parameters as $param) {
$parameters[$param->ParameterValue] = $param->ParameterKey;
}
echo http_build_query($parameters);
答案3
将需要传输的信息定义为第一个堆栈的输出。使用伪参数该地区等
创建第二个堆栈时,在第一个堆栈上调用 describe-stack 以获取输出,然后将值作为参数传递给第二个堆栈。