amazon-web-services - east - aws tags
¿Cómo crear un número variable de recursos de instancia de EC2 en la plantilla de Cloudformation? (5)
Creo que lo que buscaba el cartel original es algo así como:
"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" }**,
}
},
... en otras palabras, inserte el número de instancias iniciales (la capacidad) de un parámetro.
¿Cómo crear un número variable de recursos de instancia de EC2 en la plantilla de Cloudformation, de acuerdo con un parámetro de plantilla?
La API de EC2 y las herramientas de administración permiten iniciar varias instancias de la misma AMI, pero no puedo encontrar la forma de hacerlo utilizando Cloudformation.
La respuesta corta es: no puedes. No puede obtener exactamente el mismo resultado (N instancias EC2 idénticas, no vinculadas por un grupo de escalado automático).
Lanzar varias instancias por igual desde la consola no es como crear un grupo de escalado automático con N instancias como capacidad deseada. Es solo un atajo útil que tienes, en lugar de tener que pasar N veces por el mismo proceso de creación de EC2. Se llama "una reserva" (sin relación con la instancia reservada). Los grupos de escalado automático son una bestia diferente (aunque termine con N instancias EC2 idénticas).
Tu también puedes:
- duplicar (yuk) el recurso EC2 en la plantilla
- use una plantilla anidada, que hará la creación de EC2 por sí misma, y la llamará N veces desde su pila maestra, alimentándola cada vez con los mismos parámetros
El problema es que el número de instancias de EC2 no será dinámico, no puede ser un parámetro.
- use una interfaz para las plantillas de CloudFormation, como la troposfera, que le permite escribir la descripción de EC2 dentro de una función, y llamar a la función N veces (mi elección ahora). Al final, tienes una plantilla de CloudFormation que hace el trabajo, pero has escrito el código de creación EC2 solo una vez. No es un parámetro de CloudFormation real , pero al final del día, obtienes tu número dinámico de EC2.
Mientras tanto, hay muchas Plantillas de ejemplo de AWS CloudFormation disponibles, y varias incluyen el lanzamiento de varias instancias, aunque generalmente muestran otras características en paralelo; por ejemplo, AutoScalingKeepAtNSample.template crea un sitio web de muestra con AutoScalingKeepAtNSample.template carga y AutoScalingKeepAtNSample.template automática y está configurado para iniciar 2 instancias de EC2 para este propósito según este extracto de plantilla:
"WebServerGroup": {
"Type": "AWS::AutoScaling::AutoScalingGroup",
"Properties": {
"AvailabilityZones": {
"Fn::GetAZs": ""
},
"LaunchConfigurationName": {
"Ref": "LaunchConfig"
},
"MinSize": "2",
"MaxSize": "2",
"LoadBalancerNames": [
{
"Ref": "ElasticLoadBalancer"
}
]
}
},
También hay más muestras avanzadas / completas disponibles, por ejemplo, la plantilla Drupal para un servidor web de alta disponibilidad con una instancia de base de datos Multi-AZ Amazon RDS y el uso de S3 para almacenar el contenido del archivo , que actualmente está configurado para permitir hablar de 1 a 5 instancias de servidor web a una instancia de base de datos de Amazon RDS MySQL Multi-AZ y ejecutándose detrás de un Elastic Load Balancer , que organiza las instancias del servidor web a través de Auto Scaling .
Utilice la función Ref
.
http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html
Las variables definidas por el usuario se definen en la sección "Parameters"
del archivo de configuración. En la sección "Resources"
del archivo de configuración puede completar valores utilizando referencias a estos parámetros.
{
"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" },
...
},
},
...
},
...
}
En el ejemplo anterior, { "Ref": ... }
se usa para llenar valores en la plantilla. En este caso, proporcionamos enteros como valores para "MinSize"
y "MaxSize"
.
AWS::EC2::Instance
Resource no admite los parámetros MinCount
/ MaxCount
de la API RunInstances
subyacente, por lo que no es posible crear un número variable de instancias de EC2 pasando Parámetros a una sola copia de este Recurso.
Para crear un número variable de recursos de instancia de EC2 en la plantilla de CloudFormation de acuerdo con un Parámetro de plantilla, y sin implementar un Grupo de Auto Escalado, existen dos opciones:
1. Condiciones
Puede usar las Conditions
para crear un número variable de Recursos de AWS::EC2::Instance
en función del Parámetro.
Es un poco detallado (porque tienes que usar Fn::Equals
), pero funciona.
Este es un ejemplo práctico que permite al usuario especificar hasta un máximo de 5 instancias:
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 [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
1a. Preprocesador de plantillas con condiciones
Como una variación de lo anterior, puede usar un preprocesador de plantillas como el Erb de Ruby para generar la plantilla anterior basada en un máximo específico, haciendo que su código fuente sea más compacto y eliminando la duplicación:
<%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-%>
Para procesar la fuente anterior en una plantilla compatible con CloudFormation, ejecute:
ruby -rerb -e "puts ERB.new(ARGF.read, nil, ''-'').result" < template.yml > template-out.yml
Para su comodidad, aquí hay una idea general del YAML de salida generado para 10 instancias de EC2 variables .
2. Recurso personalizado
Un enfoque alternativo es implementar un recurso personalizado que llame directamente a las 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]