CloudFormation 模板内的 EC2 实例交叉引用

CloudFormation 模板内的 EC2 实例交叉引用

我有点为难。我有一个特定的应用程序(我无法修改它),我需要使用 CloudFormation 自动将其部署到 3 个 AWS EC2 实例上,并且我需要这 3 个实例在启动时或至少在任何流量到达它们之前彼此了解。

简而言之,需要发生的是,当机器启动时,它们需要每个运行包含主机名或 IP 的本地命令(Windows Shell 脚本)全部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 台机器都需要所有 3 个引用,那么我就会遇到困难。

我对 AWS 的经验还不够多(实际上根本没有),无法找到替代方法,但我有一些理论想法,您也许可以证实,或者提出您自己的想法:

  1. 让每台机器将自己注册到某个外部位置 - 例如 S3 bucket、AWS Config、SQS 等等。当 CF 完成后,运行使用该数据的 lambda 来不知何故在所有 3 台机器上启动 shell 脚本。
  2. 也许 CF 有一种方法可以在所有实例都创建完毕(使用等待?)并且它们的 DNS 名称可用时运行 shell 脚本?
  3. 开发某种代理服务在所有机器上运行,让其中一台机器获取对另外两台机器的引用UserData,然后让其将该信息发送给另外两台机器以触发上述脚本
  4. 也许cf-init从使用脚本启动的节点进程运行userdata以获取此信息并将其传递给需要运行的后续脚本(不确定 cf-init 是否如此工作以及是否在所有 3 台机器都分配了 DNS 名称后发生这种情况)

我想避免过于复杂或令人费解的解决方案,因为我的知识和时间有限(这不是如今成功的秘诀吗?)

希望我已经把这个问题解释清楚了。提前谢谢!

答案1

使用 CloudFormation 堆栈名称作为 3 个服务器域名的子域。这样您就知道 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" ] }
    ]
  }
}

server1.<stack-name>.<hosted-zone>例如,这将创建一个 DNS 记录server1.test-stack.example.com。您可以对所有 3 个实例执行此操作。

然后,您可以在每个实例的元数据中立即创建配置文件,因为您知道 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在 Instance1 的 UserData 中。将其更新为cfn-init.exe -r Instance2Instance2,等等。后面的标签-r必须是定义它的资源名称。

希望有帮助!

相关内容