Как создать переменное количество ресурсов экземпляра EC2 в шаблоне Cloudformation? - PullRequest
23 голосов
/ 09 марта 2011

Как создать переменное число ресурсов экземпляра EC2 в шаблоне Cloudformation в соответствии с параметром шаблона?

API-интерфейс EC2 и инструменты управления позволяют запускать несколько экземпляров одного и того же AMI, но я не могу найти, каксделать это с помощью Cloudformation.

Ответы [ 5 ]

20 голосов
/ 09 января 2017

Ресурс AWS::EC2::Instance не поддерживает параметры MinCount / MaxCount базового API RunInstances, поэтому невозможно создать номер переменнойэкземпляров EC2 путем передачи параметров в единственную копию этого ресурса.

Чтобы создать переменное число ресурсов экземпляра EC2 в шаблоне CloudFormation в соответствии с параметром шаблона и без развертывания группы автоматического масштабирования, существует дваварианты:

1.Условия

Вы можете использовать Conditions, чтобы создать переменное число AWS::EC2::Instance ресурсов в зависимости от параметра.

Это немного многословно (потому что вы должны использоватьFn::Equals), но это работает.

Вот рабочий пример, который позволяет пользователю указать максимум 5 экземпляров:

Launch Stack

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

1a.Шаблонный препроцессор с условиями

В качестве варианта вышеописанного вы можете использовать шаблонный препроцессор, такой как Ruby's 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

Для удобства приведем суть сгенерированного выходного YAML для 10 переменных EC2 .

2.Пользовательский ресурс

Альтернативный подход заключается в реализации пользовательского ресурса , который напрямую вызывает API-интерфейсы RunInstances / TerminateInstances:

Launch Stack

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]
12 голосов
/ 04 мая 2012

Я думаю, что после оригинального плаката было что-то вроде:

"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" }**,
        }
    },

... другими словами, введите количество начальных экземпляров (емкость) от параметра.

5 голосов
/ 09 декабря 2014

Короткий ответ: ты не можешь.Вы не можете получить точно такой же результат (N идентичных экземпляров EC2, не связанных группой автоматического масштабирования).

Запуск нескольких экземпляров из консоли не похож на создание группы автоматического масштабирования с N экземплярами в качестве требуемой емкости.Это просто полезный ярлык, вместо того, чтобы N раз проходить один и тот же процесс создания EC2.Это называется «резервирование» (никакого отношения к зарезервированному экземпляру).Группы автоматического масштабирования - это разные звери (даже если у вас N идентичных экземпляров EC2).

Вы можете:

  • дублировать (юк) ресурс EC2 в шаблоне
  • используйте вложенный шаблон, который сам создаст EC2, и вызовите его N раз из вашего основного стека, передавая его каждый раз с одинаковыми параметрами

Проблема в том, что числоэкземпляров EC2 не будет динамическим, он не может быть параметром.

  • использовать внешний интерфейс для шаблонов CloudFormation, таких как тропосфера, который позволяет писать описание EC2 внутри функции и вызывать функцию Nраз (мой выбор сейчас).В конце концов, у вас есть шаблон CloudFormation, который выполняет эту работу, но вы написали код создания EC2 только один раз.Это не реальный параметр CloudFormation, но в конце дня вы получите свой динамический номер EC2.
3 голосов
/ 17 января 2012

Между тем существует множество примеров шаблонов AWS CloudFormation , и некоторые из них включают запуск нескольких экземпляров, хотя обычно демонстрируют другие функции параллельно;например, AutoScalingKeepAtNSample.template создает образец с балансировкой нагрузки, автоматически масштабируемый пример веб-сайта и настроен на запуск 2 экземпляров EC2 для этой цели согласно отрывку этого шаблона:

"WebServerGroup": {

    "Type": "AWS::AutoScaling::AutoScalingGroup",
    "Properties": {
        "AvailabilityZones": {
            "Fn::GetAZs": ""
        },
        "LaunchConfigurationName": {
            "Ref": "LaunchConfig"
        },
        "MinSize": "2",
        "MaxSize": "2",
        "LoadBalancerNames": [
            {
                "Ref": "ElasticLoadBalancer"
            }
        ]
    }

},

Также доступны более сложные / полные примеры, например, шаблон Drupal для Высокодоступного веб-сервера с экземпляром базы данных Amazon RDS Multi-AZ и использованием S3 для хранения содержимого файла , который в настоящее время используетсянастроен так, чтобы 1-5 экземпляров веб-сервера могли взаимодействовать с Multi-AZ MySQL Amazon RDS экземпляром базы данных и работать за Elastic Load Balancer , который управляет сетьюэкземпляры сервера через Автоматическое масштабирование .

2 голосов
/ 23 мая 2013

Используйте функцию 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".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...