Назад | Перейти на главную страницу

Управление зависимостями с помощью CloudFormation и CFN-SIGNAL

Это может показаться немного запутанным, но я постараюсь упростить.

у меня есть CloudFormation шаблон настройки 3 одинаковых EC2 машины, и используя cfn-init в UserData script он извлекает некоторый код автоматизации из S3, и запускает его, чтобы настроить эти машины в конфигурации высокой доступности, очень специфичной для конкретного продукта, которая здесь не актуальна.

Это выглядит примерно так:

"commands" : {
          "0-Tester" : {
            "command" : "echo \"I am OK.\" > \"d:\\test.txt\"",
            "waitAfterCompletion": 5
          },
          "1-Pullcode" : {
            "command" : "aws s3 cp s3://some-bucket/code.zip d:/code.zip > d:/s3sync.log",
            "waitAfterCompletion": 5
          },
          "2-UnpackCode" : {
            "command" : "powershell Expand-Archive -Path d:\\code.zip -DestinationPath d:\\dev",
            "waitAfterCompletion": 5
          },
          "3-ResetLicensing" : {
            "command" : "\"C:/Program Files/something/iisnodeModule/node.exe\" d:/dev/aws-automation/service.js --service Licensing.Service --action restart > d:/oxy_restart.log",
            "waitAfterCompletion": 5
          },
          "4-RunAutomation" : {
            "command" : "\"C:/Program Files/something/iisnodeModule/node.exe\" d:/dev/aws-automation/automate.js --config c:/servers.conf --all > d:/automation.log",
            "waitAfterCompletion": 5
          }
        }

Чтобы эта автоматизация произошла, каждая машина должна знать IP-адреса или DNS-имена всех трех машин (например, подумайте о создании набора реплик mongodb), и для этого у меня есть шаблон, создающий 3 записи DNS в Route53 для этих EC2 (в частной размещенной зоне) и создания этих 3 предсказуемых DNS-имен в файле на каждом экземпляре EC2.

Происходит это так:

"config": {
        "files": {
          "c:\\servers.conf": {
            "content": {
              "role": "app",
              "servers": [
                {"Fn::Join": [".",["build1",{"Ref": "AWS::StackName"},{"Ref": "HostedZone"}]]},
                {"Fn::Join": [".",["app1",{"Ref": "AWS::StackName"},{"Ref": "HostedZone"}]]},
                {"Fn::Join": [".",["app2",{"Ref": "AWS::StackName"},{"Ref": "HostedZone"}]]}
              ],
              "replSetName": { "Ref": "ReplicaSetName" },
              "ecFolder": { "Ref": "ElasticubeFolder" }
            }
          }
        },

И конечно

"DNSRecordBuild1": {
  "Type": "AWS::Route53::RecordSet",
  "Properties": {
    "HostedZoneName": {"Ref": "HostedZone"},
    "Name": {
      "Fn::Join": [".",["build1",{"Ref": "AWS::StackName"},{"Ref": "HostedZone"}]]
    },
    "Type": "A",
    "TTL": "900",
    "ResourceRecords": [{"Fn::GetAtt": ["build1","PrivateIp"]}]
  }
}

Чтобы создать записи DNS.

Итак, на этом этапе стек создается идеально - CFN раскручивает 3 экземпляра, отображает конфигурацию json на каждом из них с именами DNS, которые в конечном итоге будут существовать, извлекает код из S3, и запускает скрипты. Сейчас R53 записи, очевидно, зависят от этих экземпляров, но они создаются, как только экземпляр готов, что перед скрипты начинают работать - таким образом, к тому времени, когда скрипты ссылаются на указанные имена DNS, они уже существуют в R53. Эта установка работает нормально.

Теперь я хотел добавить ELB в стек, и я хотел, чтобы он был создан только когда 3 машины полностью настроены и готовы к трафику. Итак, я добавил DependsOn атрибут к ELB ресурс, который работал нормально, а затем добавил cfn-signal и CreationPolicy для каждого экземпляра, чтобы убедиться, что он помечен как выполненный только после завершения сценариев автоматизации:

"cfn-signal.exe -e %ERRORLEVEL% --stack ", { "Ref" : "AWS::StackName" }, " --resource build1 --region ", { "Ref" : "AWS::Region" }

и

"CreationPolicy" : {
    "ResourceSignal" : {
      "Timeout" : "PT10M"
    }
  }

Но сразу же это нарушает весь процесс - потому что теперь R53 записи не будут созданы, пока машина не отправит signal - и машина не отправит его, потому что пытается запустить сценарии, зависящие от этих DNS-имен, и поэтому терпит неудачу.

А пока я просто удалил signal и policy позволить ELB запуститься, как только экземпляры будут подключены к сети, но это не идеально. Итак, вопрос: как я могу отложить создание ELB до того, как скрипты будут выполнены, без задержки ресурсов R53 или создания любых таких циклов зависимости, как указано выше?

На данный момент мои мысли такие:

  1. Добавление wait ресурс для шаблона, сигнализируя ему, и имея ELB ресурс DependsOn этот wait - не уверен, что это вообще возможно или какие побочные эффекты это может иметь
  2. Не создавая ELB через CFN вообще, а скорее создается через AWS CLI на одной из машин, когда он сможет проверить, что все остальные машины готовы. Мне действительно не нравится этот подход, так как он потребует большого количества дополнительного кода в моих сценариях автоматизации и затруднит управление этим ресурсом (то есть, при удалении стека потребуется удалить ELB вручную)
  3. Попросите каждый экземпляр «сигнализировать» с помощью альтернативного метода, такого как размещение файла или флага где-то (например, S3), а затем наличие lambda отреагируйте на это и создайте ELB - но это имеет тот же недостаток, что и # 2 ...

Как насчет того, чтобы атаковать его с совершенно другой точки зрения и использовать AWS EC2 System Manager (SSM) для настройки экземпляров после создания всех ресурсов (например, 3x EC2, DNS и ELB)?

SSM будет зависеть от создания всего вышеперечисленного, а затем от входа в каждый экземпляр и запуска приложения. Это должно решить проблемы с зависимостями.

Проверять, выписываться AWS :: SSM :: Association и AWS :: SSM :: Документ для поддержки CloudFormation EC2 Systems Manager.

Надеюсь, это поможет :)