簡體   English   中英

AWS Cloudformation:如何在創建EC2時重用放在user-data參數中的bash腳本?

[英]AWS Cloudformation: How to reuse bash script placed in user-data parameter when creating EC2?

在Cloudformation中,我有兩個堆棧(一個嵌套)。

嵌套堆棧“ec2-setup”:

{
  "AWSTemplateFormatVersion" : "2010-09-09",

  "Parameters" : {
    // (...) some parameters here

    "userData" : {
      "Description" : "user data to be passed to instance",
      "Type" : "String",
      "Default": ""
    }

  },

  "Resources" : {

    "EC2Instance" : {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "UserData" : { "Ref" : "userData" },
        // (...) some other properties here
       }
    }

  },
  // (...)
}

現在在我的主模板中,我想引用上面給出的嵌套模板,並使用userData參數傳遞一個bash腳本。 另外, 我不想內聯用戶數據腳本的內容,因為我想為少數ec2實例重用它(所以每次我在主模板中聲明ec2實例時我都不想復制腳本)。

我試圖通過將腳本的內容設置為參數的默認值來實現此目的:

{
  "AWSTemplateFormatVersion": "2010-09-09",

  "Parameters" : {
    "myUserData": {
      "Type": "String",
      "Default" : { "Fn::Base64" : { "Fn::Join" : ["", [
        "#!/bin/bash \n",
        "yum update -y \n",

        "# Install the files and packages from the metadata\n",
        "echo 'tralala' > /tmp/hahaha"
      ]]}}
    }
  },
(...)

    "myEc2": {
      "Type": "AWS::CloudFormation::Stack",
      "Properties": {
        "TemplateURL": "s3://path/to/ec2-setup.json",
        "TimeoutInMinutes": "10",
        "Parameters": {
          // (...)
          "userData" : { "Ref" : "myUserData" }
        }

但是在嘗試啟動堆棧時出現以下錯誤:

“模板驗證錯誤:模板格式錯誤:每個默認成員必須是一個字符串。”

該錯誤似乎是由於聲明{Fn :: Base64(...)}是一個對象 - 而不是一個字符串(盡管它導致返回base64編碼的字符串)。

一切正常,如果我在調用嵌套模板時將我的腳本直接粘貼到參數部分(作為內聯腳本)(而不是將字符串設置為參數):

"myEc2": {
  "Type": "AWS::CloudFormation::Stack",
  "Properties": {
    "TemplateURL": "s3://path/to/ec2-setup.json",
    "TimeoutInMinutes": "10",
    "Parameters": {
      // (...)
      "userData" : { "Fn::Base64" : { "Fn::Join" : ["", [
        "#!/bin/bash \n",
        "yum update -y \n",

        "# Install the files and packages from the metadata\n",
        "echo 'tralala' > /tmp/hahaha"
        ]]}}
    }

但是我希望將userData腳本的內容保存在參數/變量中以便能夠重用它。

有沒有機會重復使用這樣的bash腳本而不需要每次都復制/粘貼它?

以下是有關如何在用戶數據中為通過CloudFormation定義的多個EC2實例重用bash腳本的一些選項:

1.將默認參數設置為字符串

您的原始嘗試解決方案應該有效,只需稍微調整一下:您必須將默認參數聲明為字符串,如下所示(使用YAML而不是JSON可以更容易/更容易地聲明多行字符串內聯):

  AWSTemplateFormatVersion: "2010-09-09"
  Parameters:
    myUserData:
      Type: String
      Default: |
        #!/bin/bash
        yum update -y
        # Install the files and packages from the metadata
        echo 'tralala' > /tmp/hahaha
(...)
  Resources:
    myEc2:
      Type: AWS::CloudFormation::Stack
      Properties
        TemplateURL: "s3://path/to/ec2-setup.yml"
        TimeoutInMinutes: 10
        Parameters:
          # (...)
          userData: !Ref myUserData

然后,在您的嵌套堆棧中,應用任何必需的內部函數Fn::Base64 ,以及Fn::Sub ,如果您需要在用戶數據腳本中應用任何RefFn::GetAtt函數,這非常有用) EC2實例的資源屬性:

  AWSTemplateFormatVersion: "2010-09-09"
  Parameters:
    # (...) some parameters here
    userData:
      Description: user data to be passed to instance
      Type: String
      Default: ""    
  Resources:
    EC2Instance:
      Type: AWS::EC2::Instance
      Properties:
        UserData:
          "Fn::Base64":
            "Fn::Sub": !Ref userData
        # (...) some other properties here
  # (...)

2.將腳本上傳到S3

您可以將單個Bash腳本上載到S3存儲桶,然后通過在模板中的每個EC2實例中添加最小用戶數據腳本來調用腳本:

  AWSTemplateFormatVersion: "2010-09-09"
  Parameters:
    # (...) some parameters here
    ScriptBucket:
      Description: S3 bucket containing user-data script
      Type: String
    ScriptKey:
      Description: S3 object key containing user-data script
      Type: String
  Resources:
    EC2Instance:
      Type: AWS::EC2::Instance
      Properties:
        UserData:
          "Fn::Base64":
            "Fn::Sub": |
              #!/bin/bash
              aws s3 cp s3://${ScriptBucket}/${ScriptKey} - | bash -s
        # (...) some other properties here
  # (...)

3.使用預處理器從單一來源內聯腳本

最后,您可以使用troposphere或您自己的模板預處理器工具從更緊湊/富有表現力的源文件中“生成”詳細的CloudFormation可執行模板。 這種方法將允許您消除源文件中的重復 - 雖然模板將包含“重復的”用戶數據腳本,但這只會出現在生成的模板中,因此不應該造成問題。

您必須在模板外部查看以向多個模板提供相同的用戶數據。 這里常見的方法是進一步抽象模板,或“模板模板”。 使用相同的方法創建兩個模板,並將它們保持為DRY。

我是cloudformation的忠實粉絲,並使用它來創建我的大部分資源,特別是對於生產限制的用途。 但就像它一樣強大,它並不是非常關鍵。 除了創建模板之外,您還必須調用coudformation API來創建堆棧,並提供堆棧名稱和參數。 因此,圍繞使用雲信息的自動化是完整解決方案的必要部分。 這種自動化可以是簡單的(例如bash腳本)或復雜的。 我已經開始使用ansible的cloudformation模塊來自動化“模板”,無論是使用Jinja為模板創建模板,還是只為同一個可重用模板提供不同的參數集,或者在創建堆棧之前進行發現; 任何輔助操作都是必要的。 有些人為此目的非常喜歡對流層 - 如果你是一個pythonic思想家,你可能會覺得它很合適。 一旦您擁有處理堆棧創建的任何類型的自動化,您就會發現很容易添加步驟以使模板本身更具動態性,或者從可重用組件中組裝多個堆棧。

在工作中我們使用了相當多的雲形態,並且現在傾向於采用組合方法,在這種方法中我們定義我們使用的模板的共享組件,然后從組件中組合實際模板。

另一種選擇是合並兩個堆棧,使用條件來控制在從模板創建的任何特定堆棧中包含已定義的資源。 這在簡單的情況下工作正常,但是從長遠來看,所有這些條件的組合復雜性往往使這成為一個困難的解決方案,除非差異非常簡單。

實際上我發現了一個比已經提到的更多的解決方案 這個解決方案一方面有點“hackish”,但另一方面我發現它對“bash腳本”用例(以及其他參數)非常有用。

我們的想法是創建一個額外的堆棧 - “ 參數堆棧 ” - 它將輸出值。 由於堆棧的輸出不限於String(因為它是默認值),我們可以將整個base64編碼的腳本定義為堆棧的單個輸出。

缺點是每個堆棧都需要定義至少一個資源,因此我們的參數堆棧還需要定義至少一個資源。 此問題的解決方案是在已定義現有資源的另一個模板中定義參數,或者創建一個永遠不會創建的“假資源”,因為條件永遠不會令人滿意。

在這里,我提出了假資源的解決方案。 首先,我們創建新的paramaters-stack.json,如下所示:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Outputs/returns parameter values",


  "Conditions" : {
    "alwaysFalseCondition" : {"Fn::Equals" : ["aaaaaaaaaa", "bbbbbbbbbb"]}
  },

  "Resources": {
    "FakeResource" : {
      "Type" : "AWS::EC2::EIPAssociation",
      "Condition" : "alwaysFalseCondition",
      "Properties" : {
        "AllocationId" :  { "Ref": "AWS::NoValue" },
        "NetworkInterfaceId" : { "Ref": "AWS::NoValue" }
      }
    }
  },

  "Outputs": {
    "ec2InitScript": {
      "Value":
      { "Fn::Base64" : { "Fn::Join" : ["", [
        "#!/bin/bash \n",
        "yum update -y \n",

        "# Install the files and packages from the metadata\n",
        "echo 'tralala' > /tmp/hahaha"
      ]]}}

    }
  }
}

現在在主模板中我們首先聲明我們的參數堆棧 ,然后我們引用該參數堆棧的輸出:

{
  "AWSTemplateFormatVersion": "2010-09-09",

   "Resources": { 

    "myParameters": {
      "Type": "AWS::CloudFormation::Stack",
      "Properties": {
        "TemplateURL": "s3://path/to/paramaters-stack.json",
        "TimeoutInMinutes": "10"
      }
    },

    "myEc2": {
      "Type": "AWS::CloudFormation::Stack",
      "Properties": {
        "TemplateURL": "s3://path/to/ec2-setup.json",
        "TimeoutInMinutes": "10",
        "Parameters": {
          // (...)
          "userData" : {"Fn::GetAtt": [ "myParameters", "Outputs.ec2InitScript" ]}
        }
     }
  }
}

請注意,可以在一個堆棧文件中創建多達60個輸出,因此可以使用此技術為每個堆棧文件定義60個變量/參數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM