如何在 Amazon CloudFormation 模板中使两个实例都知道 IP 地址

如何在 Amazon CloudFormation 模板中使两个实例都知道 IP 地址

我在 Amazon CloudFormation 模板中有多个实例,并尝试通过 UserData 将它们连接在一起,即互相告知对方机器的 IP 地址。

该模板看起来像这样:

"Instance1" : {
  "Type" : "AWS::EC2::Instance",
    ...
    "UserData" : { 
        "Fn::Base64" : { 
        "Fn::Join" : [ "\n", [ 
            { "Fn::Join" : [ "=", [ "Instance2", { "Fn::GetAtt" : [ "Instance2" , "PrivateIp"] } ] ] }
            ] ]
             } }
  }
},
"Instance2" : {
  "Type" : "AWS::EC2::Instance",
    ...
    "UserData" : { 
        "Fn::Base64" : { 
        "Fn::Join" : [ "\n", [ 
            { "Fn::Join" : [ "=", [ "Instance1", { "Fn::GetAtt" : [ "Instance1" , "PrivateIp"] } ] ] }
            ] ]
             } }
  }
},

Amazon CloudFormation 拒绝处理此 CloudFormation 并报告它无法处理两个实例之间的循环依赖关系。

有没有办法解决这个问题,而不必自己构建任何东西。例如,我希望两个实例上的 UserData 都反映另一台机器的 IP 地址,而无需事后手动更改 UserData。

答案1

我实际上找到了一种通过使用 ElasticIPs 单独使用 Cloud Formation 模板来实现此目的的方法。

我创建了一个 ElasticIP(在这种情况下您不能直接将其分配给实例!)

"ServerEIP" : {
 "Type" : "AWS::EC2::EIP",
 "Properties" : {
 }
},

然后我在 UserData 中引用该 IP

"Client" : {
   ...
        "UserData" : { 
            { "Fn::Join" : [ "=", [ "Server", { "Ref" : "ServerEIP" } ] ] }

服务器可以直接引用客户端

"Server" : {
  "Type" : "AWS::EC2::Instance",
    ...
"UserData" : { 
        ...
            { "Fn::Join" : [ "=", [ "Client", { "Fn::GetAtt" : [ "Client" , "PrivateIp"] } ] ] },

稍后我将 Elastic 与实际服务器关联起来,以使 Cloud Formation 正确处理依赖关系:

"ServerIPAssoc" : {
     "Type" : "AWS::EC2::EIPAssociation",
     "Properties" : {
         "InstanceId" : { "Ref" : "Server" },
         "EIP" : { "Ref" : "ServerEIP" }
     }
 },

完成!我现在有两个知道另一个节点的 IP 地址的实例。

唯一的缺点是,现在的流量是通过公共 IP 地址进行的,因此会产生流量成本并且可能不太安全。

更新:我现在遇到了所描述的问题这里,不确定我是否可以在这里解决这个问题。

答案2

您可以实现您的高级目标,但不能满足您列出的限制(即,在两个实例中都包含用户数据中的原始 IP 地址)。原因很简单:

  • 必须在实例启动之前指定用户数据。

  • 实例启动后才知道 IP 地址。

CloudFormation 可以启动一个实例并将其 IP 地址提供给第二个实例,但不能同时提供两者(循环依赖)。

您可以使用多种方法和技术来解决这种双向沟通问题。从高层次来看:

  • 您可以将 A 的 IP 地址传递给 B,然后让 B 联系 A 并让其知道它的 IP 地址是什么(注意安全)。

  • 您可以将每个实例的 IP 地址存储在外部存储(例如,Route53、SimpleDB)中,然后每个实例在启动时查询该外部存储以找到其伙伴。

推荐

这是一种可与 CloudFormation 和可靠的 AWS 服务一起使用的简单方法:

  1. 在 Route53 中设置一个域(托管区域)。这可能与您当前使用的任何公共域完全不同,但是如果您已经在使用 Route53,则可以将此功能插入同一域中。

  2. 在您的 CloudFormation 模板中,为每个实例生成一个唯一的名称,可能基于当前的 CloudFormation 堆栈名称(例如,“MYSTACK-server-a.example.com”和“MYSTACK-server-b.example.com”)

  3. 在 CloudFormation 模板中,将实例名称传递到每个服务器各自的用户数据中。

  4. 向您的 CloudFormation 模板添加指令,将这些新的 DNS 名称(记录集)注入 Route53,将它们映射到实例的 IP 地址。

CloudFormation 将启动实例,并传入用户数据。当为实例分配 IP 地址后,CloudFormation 会将它们映射到 Route53 DNS 中的主机名。然后,您的实例可以使用主机名相互查找。

如果您的实例需要在启动时找到其合作伙伴,则它们将需要持续轮询 DNS,直到合作伙伴进入运行状态并被分配 IP 地址。请谨慎使用缓存 DNS“未命中”的软件。

相关内容