如何根据模板参数在 Cloudformation 模板中创建可变数量的 EC2 实例资源?
EC2 API 和管理工具允许启动同一个 AMI 的多个实例,但我找不到如何使用 Cloudformation 执行此操作。
如何根据模板参数在 Cloudformation 模板中创建可变数量的 EC2 实例资源?
EC2 API 和管理工具允许启动同一个 AMI 的多个实例,但我找不到如何使用 Cloudformation 执行此操作。
该AWS::EC2::Instance
资源不支持底层API 的MinCount
/参数,因此无法通过将参数传递给该资源的单个副本来创建可变数量的 EC2 实例。MaxCount
RunInstances
要根据模板参数在 CloudFormation 模板中创建可变数量的 EC2 实例资源,并且不部署 Auto Scaling Group,有两种选择:
您可以根据参数Conditions
创建可变数量的资源。AWS::EC2::Instance
这有点冗长(因为您必须使用Fn::Equals
),但它确实有效。
这是一个允许用户指定最多5 个实例的工作示例:
Description: Create a variable number of EC2 instance resources.
Parameters:
InstanceCount:
Description: Number of EC2 instances (must be between 1 and 5).
Type: Number
Default: 1
MinValue: 1
MaxValue: 5
ConstraintDescription: Must be a number between 1 and 5.
ImageId:
Description: Image ID to launch EC2 instances.
Type: AWS::EC2::Image::Id
# amzn-ami-hvm-2016.09.1.20161221-x86_64-gp2
Default: ami-9be6f38c
InstanceType:
Description: Instance type to launch EC2 instances.
Type: String
Default: m3.medium
AllowedValues: [ m3.medium, m3.large, m3.xlarge, m3.2xlarge ]
Conditions:
Launch1: !Equals [1, 1]
Launch2: !Not [!Equals [1, !Ref InstanceCount]]
Launch3: !And
- !Not [!Equals [1, !Ref InstanceCount]]
- !Not [!Equals [2, !Ref InstanceCount]]
Launch4: !Or
- !Equals [4, !Ref InstanceCount]
- !Equals [5, !Ref InstanceCount]
Launch5: !Equals [5, !Ref InstanceCount]
Resources:
Instance1:
Condition: Launch1
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
Instance2:
Condition: Launch2
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
Instance3:
Condition: Launch3
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
Instance4:
Condition: Launch4
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
Instance5:
Condition: Launch5
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
作为上述的变体,您可以使用 Ruby 的Erb之类的模板预处理器根据指定的最大值生成上述模板,从而使您的源代码更加紧凑并消除重复:
<%max = 10-%>
Description: Create a variable number of EC2 instance resources.
Parameters:
InstanceCount:
Description: Number of EC2 instances (must be between 1 and <%=max%>).
Type: Number
Default: 1
MinValue: 1
MaxValue: <%=max%>
ConstraintDescription: Must be a number between 1 and <%=max%>.
ImageId:
Description: Image ID to launch EC2 instances.
Type: AWS::EC2::Image::Id
# amzn-ami-hvm-2016.09.1.20161221-x86_64-gp2
Default: ami-9be6f38c
InstanceType:
Description: Instance type to launch EC2 instances.
Type: String
Default: m3.medium
AllowedValues: [ m3.medium, m3.large, m3.xlarge, m3.2xlarge ]
Conditions:
Launch1: !Equals [1, 1]
Launch2: !Not [!Equals [1, !Ref InstanceCount]]
<%(3..max-1).each do |x|
low = (max-1)/(x-1) <= 1-%>
Launch<%=x%>: !<%=low ? 'Or' : 'And'%>
<% (1..max).each do |i|
if low && i >= x-%>
- !Equals [<%=i%>, !Ref InstanceCount]
<% elsif !low && i < x-%>
- !Not [!Equals [<%=i%>, !Ref InstanceCount]]
<% end
end
end-%>
Launch<%=max%>: !Equals [<%=max%>, !Ref InstanceCount]
Resources:
<%(1..max).each do |x|-%>
Instance<%=x%>:
Condition: Launch<%=x%>
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
<%end-%>
要将上述源处理为与 CloudFormation 兼容的模板,请运行:
ruby -rerb -e "puts ERB.new(ARGF.read, nil, '-').result" < template.yml > template-out.yml
为方便起见,这里是为10 个可变 EC2 实例生成的输出 YAML 的要点。
另一种方法是实现直接调用/ API的自定义资源:RunInstances
TerminateInstances
Description: Create a variable number of EC2 instance resources.
Parameters:
InstanceCount:
Description: Number of EC2 instances (must be between 1 and 10).
Type: Number
Default: 1
MinValue: 1
MaxValue: 10
ConstraintDescription: Must be a number between 1 and 10.
ImageId:
Description: Image ID to launch EC2 instances.
Type: AWS::EC2::Image::Id
# amzn-ami-hvm-2016.09.1.20161221-x86_64-gp2
Default: ami-9be6f38c
InstanceType:
Description: Instance type to launch EC2 instances.
Type: String
Default: m3.medium
AllowedValues: [ m3.medium, m3.large, m3.xlarge, m3.2xlarge ]
Resources:
EC2Instances:
Type: Custom::EC2Instances
Properties:
ServiceToken: !GetAtt EC2InstancesFunction.Arn
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
MinCount: !Ref InstanceCount
MaxCount: !Ref InstanceCount
EC2InstancesFunction:
Type: AWS::Lambda::Function
Properties:
Handler: index.handler
Role: !GetAtt LambdaExecutionRole.Arn
Code:
ZipFile: !Sub |
var response = require('cfn-response');
var AWS = require('aws-sdk');
exports.handler = function(event, context) {
var physicalId = event.PhysicalResourceId || 'none';
function success(data) {
return response.send(event, context, response.SUCCESS, data, physicalId);
}
function failed(e) {
return response.send(event, context, response.FAILED, e, physicalId);
}
var ec2 = new AWS.EC2();
var instances;
if (event.RequestType == 'Create') {
var launchParams = event.ResourceProperties;
delete launchParams.ServiceToken;
ec2.runInstances(launchParams).promise().then((data)=> {
instances = data.Instances.map((data)=> data.InstanceId);
physicalId = instances.join(':');
return ec2.waitFor('instanceRunning', {InstanceIds: instances}).promise();
}).then((data)=> success({Instances: instances})
).catch((e)=> failed(e));
} else if (event.RequestType == 'Delete') {
if (physicalId == 'none') {return success({});}
var deleteParams = {InstanceIds: physicalId.split(':')};
ec2.terminateInstances(deleteParams).promise().then((data)=>
ec2.waitFor('instanceTerminated', deleteParams).promise()
).then((data)=>success({})
).catch((e)=>failed(e));
} else {
return failed({Error: "In-place updates not supported."});
}
};
Runtime: nodejs4.3
Timeout: 300
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: EC2Policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'ec2:RunInstances'
- 'ec2:DescribeInstances'
- 'ec2:DescribeInstanceStatus'
- 'ec2:TerminateInstances'
Resource: ['*']
Outputs:
Instances:
Value: !Join [',', !GetAtt EC2Instances.Instances]
我认为最初的海报是这样的:
"Parameters" : {
"InstanceCount" : {
"Description" : "Number of instances to start",
"Type" : "String"
},
...
"MyAutoScalingGroup" : {
"Type" : "AWS::AutoScaling::AutoScalingGroup",
"Properties" : {
"AvailabilityZones" : {"Fn::GetAZs" : ""},
"LaunchConfigurationName" : { "Ref" : "MyLaunchConfiguration" },
"MinSize" : "1",
"MaxSize" : "2",
"DesiredCapacity" : **{ "Ref" : "InstanceCount" }**,
}
},
...换句话说,从参数中插入初始实例的数量(容量)。
简短的回答是:你不能。您无法获得完全相同的结果(N 个相同的 EC2 实例,未由 Auto Scaling 组绑定)。
从控制台启动多个实例并不像创建一个具有 N 个实例作为所需容量的 Auto Scaling 组。这只是您拥有的一个有用的快捷方式,而不必通过相同的 EC2 创建过程进行 N 次。它被称为“预留”(与预留实例无关)。Auto Scaling 组是不同的野兽(即使您最终拥有 N 个相同的 EC2 实例)。
您可以:
问题是,EC2 实例的数量不会是动态的,它不能是参数。
同时,有许多可用的AWS CloudFormation 示例模板,其中一些包括启动多个实例,尽管通常同时演示其他功能;例如,AutoScalingKeepAtNSample.template创建了一个负载平衡的 Auto Scaled 示例网站,并配置为根据此模板摘录为此目的启动 2 个 EC2 实例:
"WebServerGroup": {
"Type": "AWS::AutoScaling::AutoScalingGroup",
"Properties": {
"AvailabilityZones": {
"Fn::GetAZs": ""
},
"LaunchConfigurationName": {
"Ref": "LaunchConfig"
},
"MinSize": "2",
"MaxSize": "2",
"LoadBalancerNames": [
{
"Ref": "ElasticLoadBalancer"
}
]
}
},
还有更高级/完整的示例可用,例如用于具有多可用区 Amazon RDS 数据库实例并使用 S3 存储文件内容的高可用性 Web 服务器的 Drupal 模板,当前配置为允许 1-5 个 Web 服务器实例通话到多可用区MySQL Amazon RDS数据库实例并在Elastic Load Balancer后面运行,后者通过Auto Scaling编排 Web 服务器实例。
使用该Ref
功能。
http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html
用户定义的变量在"Parameters"
配置文件的部分中定义。在"Resources"
配置文件的部分中,您可以使用对这些参数的引用来填写值。
{
"AWSTemplateFormatVersion": "2010-09-09",
...
"Parameters": {
"MinNumInstances": {
"Type": "Number",
"Description": "Minimum number of instances to run.",
"Default": "1",
"ConstraintDescription": "Must be an integer less than MaxNumInstances."
},
"MaxNumInstances": {
"Type": "Number",
"Description": "Maximum number of instances to run.",
"Default": "5",
"ConstraintDescription": "Must be an integer greater than MinNumInstances."
},
"DesiredNumInstances": {
"Type": "Number",
"Description": "Number of instances that need to be running before creation is marked as complete in CloudFormation management console.",
"Default": "1",
"ConstraintDescription": "Must be an integer in the range specified by MinNumInstances..MaxNumInstances."
}
},
"Resources": {
"MyAutoScalingGroup": {
"Type": "AWS::AutoScaling::AutoScalingGroup",
"Properties": {
...
"MinSize": { "Ref": "MinNumInstances" },
"MaxSize": { "Ref": "MaxNumInstances" },
"DesiredCapacity": { "Ref": "DesiredNumInstances" },
...
},
},
...
},
...
}
在上面的示例中,{ "Ref": ... }
用于将值填充到模板中。在这种情况下,我们提供整数作为"MinSize"
和的值"MaxSize"
。