referência cruzada da instância EC2 em um modelo do CloudFormation

1

Eu estou em apuros aqui. Eu tenho um aplicativo específico (que não posso modificar) que preciso implantar automaticamente em três instâncias do AWS EC2 usando o CloudFormation, e preciso que todos os três deles estejam cientes um do outro à medida que inicializam ou, pelo menos, antes de qualquer tráfego bate neles.

Em suma, o que precisa acontecer é que à medida que as máquinas são ativadas, eles precisam each executar um comando local (um script de shell do Windows) que inclui os nomes de host ou IPs de todos 3 máquinas. Imagine algo como:

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

O que torna isso mais difícil é que eu não posso usar IPs ou nomes estáticos, eu preciso que isso seja completamente dinâmico para cada pilha. Eu também não posso usar ferramentas nativas não-AWS como Chef ou Terraform ou qualquer outro terceiro, por razões muito irrelevantes para especificar aqui. Tudo deve ser feito por meio de serviços nativos da AWS.

Eu tentei fazer algo assim:

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

Para passar apenas os nomes dns, nem sequer chegou ao ponto de descobrir como realmente executar um script / comando - mas isso já falha devido a uma referência circular.

O que eu entendo é que o CloudFormation analisa essas referências como dependências - para referenciar "server1" eu preciso que ele seja criado, mas se todas as 3 máquinas precisarem de todas as 3 referências, eu bati um muro.

Não tenho experiência suficiente com a AWS (na verdade, nem um pouco) para descobrir um caminho alternativo para isso, mas tive algumas ideias teóricas que você pode confirmar ou sugerir uma de sua preferência:

  1. Peça que cada máquina se registre em algum local externo - como um bucket S3, AWS Config, SQS e assim por diante. E quando o CF terminar, execute um lambda que use esses dados para de alguma forma inicie um script de shell em todas as 3 máquinas.
  2. Talvez o CF tenha uma maneira de executar um script de shell quando todas as instâncias são criadas (usando um Wait?) e seus nomes DNS estão disponíveis?
  3. Desenvolvendo algum tipo de serviço de agente para ser executado em todas as máquinas, tenha um que obtenha referências para os outros dois usando UserData e, em seguida, envie essa informação para os outros dois para acionar o referido script
  4. Talvez executar cf-init de um processo de nó iniciado com userdata script para obter essas informações e transmiti-las aos scripts subseqüentes que precisam ser executados (não tenho certeza se é assim que o cf-init funciona e se acontece depois de tudo 3 máquinas têm nomes de DNS alocados)

Eu quero evitar uma solução excessivamente complicada ou complicada, já que tenho tanto conhecimento limitado quanto tempo limitado (essa não é a receita para o sucesso hoje em dia?)

Espero ter esclarecido o problema. Obrigado antecipadamente!

    
por motig88 09.07.2017 / 17:57

1 resposta

0

Use o nome da pilha do CloudFormation como um subdomínio para os nomes de domínio de 3 servidores. Dessa forma, você sabe quais serão os nomes DNS e poderá injetá-los no arquivo de configuração.

"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" ] }
    ]
  }
}

Isso criará um registro de DNS server1.<stack-name>.<hosted-zone> , por exemplo, server1.test-stack.example.com . Você faz isso nas 3 instâncias.

Em seguida, nos Metadados de cada Instância, você pode criar o arquivo de configuração imediatamente porque sabe quais serão os nomes DNS e não precisa saber quais serão os IPs - o DNS lidará com isso.

"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"
              ]]}
          }
        }
      }
  [...]

Isso criará C:\servers.conf com uma lista de 3 nomes DNS de seus servidores. Novamente, faça isso em cada uma das suas instâncias e pronto:)

Os snippets de modelo acima também devem ajudá-lo a executar os scripts do Cloud Init. Certifique-se de manter as referências de Instâncias corretas, por exemplo, cfn-init.exe -r Instance1 em UserData da Instância1. Atualize para cfn-init.exe -r Instance2 para Instance2, etc. O rótulo por trás de -r deve ser o nome do recurso em que está definido.

Espero que ajude!

    
por 10.07.2017 / 03:29