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

Перекрестная ссылка на экземпляр EC2 в шаблоне CloudFormation

Я здесь немного не в себе. У меня есть конкретное приложение (которое я не могу изменить), которое мне нужно автоматически развернуть на 3 экземплярах AWS EC2 с помощью CloudFormation, и мне нужно, чтобы все 3 из них знали друг о друге при загрузке или, по крайней мере, до появления трафика. бьет их.

Короче говоря, должно произойти то, что по мере того, как машины раскрутятся, они должны каждый запустите локальную команду (сценарий оболочки Windows), которая включает имена хостов или IP-адреса все 3 машины. Представьте себе что-то вроде:

c:\>node startReplication.js server1.aws.com,server2.aws.com,server3.aws.com

Что делает это сложнее, так это то, что я не могу использовать статические IP-адреса или имена, мне нужно, чтобы это было полностью динамическим для каждого стека. Я также не могу использовать какие-либо инструменты, не относящиеся к AWS, такие как Chef или Terraform. или любой другой третьей стороной по причинам, не имеющим отношения к здесь указанию. Все должно выполняться через собственные сервисы AWS.

Я пробовал сделать что-то вроде этого:

"UserData" : {
        "Fn::Base64" : {
            "Fn::Join" : [ ",", [
                { "Fn::GetAtt" : [ "server1", "PublicDnsName" ] },
                { "Fn::GetAtt" : [ "server2", "PublicDnsName" ] },
                { "Fn::GetAtt" : [ "server3", "PublicDnsName" ] } ]
        }
    }

Чтобы передать только имена DNS, я даже не дошел до того, чтобы выяснить, как на самом деле запустить сценарий / команду, но это уже не удается из-за циклической ссылки.

Я понимаю, что CloudFormation рассматривает эти ссылки как зависимости - поэтому для ссылки на «server1» мне нужно, чтобы он был создан, но если всем трем машинам нужны все 3 ссылки, я упал на кирпичную стену.

У меня недостаточно опыта работы с AWS (на самом деле, совсем нет), чтобы найти альтернативный путь для этого, но у меня были некоторые теоретические идеи, которые вы могли бы подтвердить или предложить одну из своих:

  1. Пусть каждая машина зарегистрируется в каком-либо внешнем месте - например, в корзине S3, AWS Config, SQS и т. Д. И когда CF будет завершен, запустите лямбда, которая использует эти данные для как-то запустить сценарий оболочки на всех трех машинах.
  2. Может быть, у CF есть способ запустить сценарий оболочки, когда все экземпляры созданы (с помощью Wait?) И их имена DNS доступны?
  3. Разрабатывая какую-то службу агента для работы на всех машинах, создайте одну, которая будет получать ссылки на два других, используя UserData а затем попросите его отправить эту информацию двум другим, чтобы запустить указанный скрипт
  4. Может быть, бег cf-init из процесса узла, запущенного с userdata script, чтобы получить эту информацию и передать ее последующим скриптам, которые необходимо запустить (не уверен, так ли работает cf-init, и если это произойдет после того, как всем 3 машинам выделены имена DNS)

Я хочу избежать слишком сложного или запутанного решения, поскольку у меня и ограниченные знания, и ограниченное время (разве это не рецепт успеха в наши дни?)

Надеюсь, я достаточно прояснил проблему. Заранее спасибо!

Используйте имя стека CloudFormation в качестве поддомена для трех доменных имен серверов. Таким образом, вы знаете, какими будут имена DNS, и можете вставить их в файл конфигурации.

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

Это создаст запись DNS server1.<stack-name>.<hosted-zone>, например server1.test-stack.example.com. Вы делаете это для всех трех экземпляров.

Затем в метаданных каждого экземпляра вы можете сразу создать файл конфигурации, потому что вы знаете, какими будут имена DNS, и вам не нужно знать, какие будут IP-адреса - DNS обработает это.

"Instance1": {
  "Type": "AWS::EC2::Instance",
  "Properties": {
    [...]
    "UserData": {
      "Fn::Base64": { "Fn::Join": ["", [
        "<script>\n",
        "cfn-init.exe -v -s ", { "Ref" : "AWS::StackId" }, " -r Instance1", " --region ", { "Ref" : "AWS::Region" }, "\n",
        "</script>"
      ] ] }
    }
  },
  "Metadata": {
    "AWS::CloudFormation::Init": {
      "config": {
        "files": {
          "c:\\servers.conf": {
            "content": { "Fn::Join": ["", [
              "server1.", { "Ref": "AWS::StackName" }, ".", { "Ref": "HostedZone" }, "\n",
              "server2.", { "Ref": "AWS::StackName" }, ".", { "Ref": "HostedZone" }, "\n",
              "server3.", { "Ref": "AWS::StackName" }, ".", { "Ref": "HostedZone" }, "\n"
              ]]}
          }
        }
      }
  [...]

Это создаст C:\servers.conf со списком из 3-х DNS-имен ваших серверов. Опять же, сделайте это на каждом из ваших экземпляров, и все готово :)

Приведенные выше фрагменты шаблона также должны помочь вам в запуске сценариев Cloud Init. Убедитесь, что вы указали правильные ссылки на экземпляры, т.е. cfn-init.exe -r Instance1 в UserData экземпляра Instance1. Обновите это до cfn-init.exe -r Instance2 для Instance2 и т. д. Метка позади -r должно быть именем ресурса, в котором он определен.

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