Ответ 1
Ресурс AWS::EC2::Instance
не поддерживает параметры MinCount
/MaxCount
базового API-интерфейса RunInstances
, поэтому невозможно создать переменное число экземпляров EC2 путем передачи параметров в одну копию этого ресурса.
Чтобы создать переменное количество ресурсов экземпляра EC2 в шаблоне CloudFormation в соответствии с параметром шаблона и без развертывания группы автоматического масштабирования, есть два варианта:
1. Условия
Вы можете использовать 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: !Or
- !Not [!Equals [2, !Ref InstanceCount]]
- !Equals [3, !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
1a. Шаблонный препроцессор с условиями
В качестве варианта вышеизложенного вы можете использовать препроцессор шаблона, такой как 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%>: !Or
<% (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
Для удобства рассмотрим сгенерированный выходной YAML для 10 переменных экземпляров EC2.
2. Пользовательский ресурс
Альтернативный подход заключается в реализации пользовательского ресурса, который RunInstances
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]