CloudFormation 模板中的 EC2 實例交叉引用
我在這裡有點麻煩。我有一個特定的應用程序(一個我無法修改的應用程序),我需要使用 CloudFormation 在 3 個 AWS EC2 實例上自動部署它,並且我需要它們中的所有 3 個在它們啟動時相互了解,或者至少在任何流量之前打他們。
簡而言之,需要發生的是,隨著機器啟動,它們需要各自執行一個本地命令(一個 Windows shell 腳本),其中包括所有3 台機器的主機名或 IP。想像一下:
c:\>node startReplication.js server1.aws.com,server2.aws.com,server3.aws.com
讓這更難的是我不能使用靜態 IP 或名稱,我需要它對每個堆棧都是完全動態的。 我也不能使用任何非 AWS 原生工具,例如 Chef 或 Terraform或任何其他第 3 方,原因與此處無關,無法在此處指定。一切都必須通過原生 AWS 服務完成。
我試過做這樣的事情:
"UserData" : { "Fn::Base64" : { "Fn::Join" : [ ",", [ { "Fn::GetAtt" : [ "server1", "PublicDnsName" ] }, { "Fn::GetAtt" : [ "server2", "PublicDnsName" ] }, { "Fn::GetAtt" : [ "server3", "PublicDnsName" ] } ] } }
只傳遞 dns 名稱,甚至還沒有達到弄清楚如何實際執行腳本/命令的地步——但是由於循環引用,這已經失敗了。
我的理解是,CloudFormation 將這些引用視為依賴項 - 所以要引用“server1”,我需要創建它,但如果所有 3 台機器都需要所有 3 個引用,我就碰壁了。
我對 AWS 的經驗不足(實際上,一點也不),無法為此找到替代路徑,但我有一些理論想法,您可能可以確認或提出自己的建議:
- 讓每台機器將自己註冊到某個外部位置 - 例如 S3 儲存桶、AWS Config、SQS 等。當 CF 完成後,執行一個 lambda,它使用該數據以某種方式在所有 3 台機器上啟動一個 shell 腳本。
- 當所有實例都被創建(使用等待?)並且它們的 DNS 名稱可用時,也許 CF 有一種執行 shell 腳本的方法?
- 開發某種代理服務以在所有機器上執行,讓一個代理服務獲取對其他兩台使用的引用
UserData
,然後讓它將該資訊發送給另外兩台以觸發所述腳本- 也許
cf-init
從使用腳本啟動的節點程序執行userdata
以獲取此資訊並將其傳遞給需要執行的後續腳本(不確定這是否是 cf-init 的工作方式以及是否在所有 3 台機器都分配了 dns 名稱之後發生)我想避免過於復雜或令人費解的解決方案,因為我的知識和時間都有限(這不是當今成功的秘訣嗎?)
我希望我已經把這個問題說得夠清楚了。提前致謝!
使用 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 Instance2
為 Instance2 等。後面的標籤-r
必須是定義它的資源名稱。希望有幫助!