CloudFormation 作為 AWS 基礎設施即程式碼 (IaC) 的核心服務,能以宣告式方式管理雲端資源。本文將探討如何利用 CloudFormation 的進階功能,例如 Fn::Cidr 函式、條件元素和刪除政策,建構更具彈性且可重複使用的範本。透過 Fn::Cidr,我們可以根據 VPC 的 CIDR 範圍自動生成子網路的 CIDR 範圍,簡化網路設定流程並減少錯誤。條件元素則允許我們根據不同的環境(例如測試或生產環境)調整資源組態,提升範本的通用性。刪除政策則提供更精細的資源生命週期管理,確保關鍵資料在堆積疊刪除時得到妥善保護。此外,文章也將探討安全性考量、效能最佳化以及 CloudFormation 的未來發展趨勢,協助讀者掌握 IaC 最佳實務。

進階範本開發

在網路架構方面,我們的堆積疊將包含以下資源:

  • 1個VPC
  • 3個公有子網路
  • 3個WebTier子網路
  • 3個中介軟體子網路
  • 3個資料函式庫子網路
  • 1個網際網路閘道
  • 1個NAT閘道
  • 1個公有路由表
  • 1個私有路由表
  • 2個IAM角色(供管理員和開發人員使用)

這些資源將具有諸如名稱(Name)和環境(Env)等標籤。

在這種情況下,我們希望引數化資源的屬性,例如CIDR範圍和標籤。因此,我們將具備以下引數:

  • VPC CIDR範圍
  • 公有子網路1 CIDR範圍
  • 公有子網路2 CIDR範圍
  • 公有子網路3 CIDR範圍
  • WebTier子網路1 CIDR範圍
  • WebTier子網路2 CIDR範圍
  • WebTier子網路3 CIDR範圍
  • 中介軟體子網路1 CIDR範圍
  • 中介軟體子網路2 CIDR範圍
  • 中介軟體子網路3 CIDR範圍
  • 資料函式庫子網路1 CIDR範圍
  • 資料函式庫子網路2 CIDR範圍
  • 資料函式庫子網路3 CIDR範圍
  • 環境(Environment)

建立可重複使用的範本

所有這些引數都將具有字串(String)型別。讓我們來撰寫引數(Parameters)部分:

Parameters:
  VpcCidr:
    Type: String
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
  PublicSubnetCidr1:
    Type: String
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
  PublicSubnetCidr2:
    Type: String
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
  PublicSubnetCidr3:
    Type: String
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
  # 依此類別推...
  DatabaseSubnetCidr1:
    Type: String
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
  DatabaseSubnetCidr2:
    Type: String
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
  DatabaseSubnetCidr3:
    Type: String
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
  Environment:
    Type: String
    AllowedValues: ["prod", "test"]

內容解密:

此段落定義了範本的引數部分,用於接收使用者輸入的各種網路組態資訊。Type欄位指定了引數的資料型別,而AllowedPattern欄位則確保輸入的CIDR範圍符合特定的正規表示式格式。AllowedValues欄位限制了Environment引數的可能值。

在我們的資源中,我們將使用Fn::Ref函式將引數值注入資源屬性。同時,我們也會使用另一個內建函式Fn::Sub來連線一個良好的字串名稱給我們的VPC:

Resources:
  Vpc:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: !Ref VpcCidr
      EnableDnsHostnames: True
      EnableDnsSupport: True
      InstanceTenancy: Default
      Tags:
        - Key: 'Name'
          Value: !Sub '${Environment}-vpc'
        - Key: 'Env'
          Value: !Ref Environment

內容解密:

此部分定義了VPC資源的建立。!Ref用於參照引數值或資源的屬性。!Sub函式用於替換字串中的變數,從而動態生成VPC的名稱標籤。

對於其餘的資源,我們將採用類別似的模式。

重點注意事項

Fn::Ref函式也可以用於在堆積疊中的一個資源中參照某些屬性。在這種情況下,我們向Fn::Ref提供資源的邏輯名稱,而不是引數。

在以下範例中,我們使用Fn::Ref來參照VPC ID,並用於建立子網路:

PublicSubnet1:
  Type: 'AWS::EC2::Subnet'
  Properties:
    CidrBlock: !Ref PublicSubnetCidr1
    VpcId: !Ref Vpc

內容解密:

此段程式碼建立了一個公有子網路,並將其CIDR區塊和VPC ID分別設定為PublicSubnetCidr1引數的值和先前建立的VPC的ID。

請注意,我們沒有在範本檔案中指定引數的預設值。在這種情況下,我們需要在create-stack命令中提供它們,否則驗證將不會透過。

有兩種方法可以向CloudFormation提供引數:作為JSON檔案或作為位置引數。

對於具有大量引數的大型範本,建議使用JSON檔案:

[
  {
    "ParameterKey": "Environment",
    "ParameterValue": "Testing"
  },
  {
    "ParameterKey": "VpcCidr",
    "ParameterValue": "10.0.0.0/16"
  },
  {
    "ParameterKey": "PublicSubnetCidr1",
    "ParameterValue": "10.0.1.0/24"
  },
  {
    "ParameterKey": "PublicSubnetCidr2",
    "ParameterValue": "10.0.2.0/24"
  },
  # 依此類別推...
]

在建立範本時,我們在引數中指定引數檔案:

$ aws cloudformation create-stack \
  --stack-name core \
  --template-body file://core.yaml \
  --parameters file://testing.json

如果我們有預設值並希望進行一些更改,我們可以逐一指定引數作為位置引數:

$ aws cloudformation create-stack \
  --stack-name core \
  --template-body file://core.yaml \
  --parameters \
  ParameterKey="Environment",ParameterValue="Testing" \
  ParameterKey="VpcCidr",ParameterValue="10.0.0.0/16"

這適用於引數較少的情況,但對於大量的引數集來說太過複雜。

重點注意事項

引數以列表形式提供,元素之間以空格分隔。請注意列表型別的引數,您將需要使用雙反斜線:

ParameterKey=List,ParameterValue=Element1\\,Element2

讓我們對引數進行一些更改。由於我們有多個CIDR範圍對應每個層級的子網路,讓我們使用列表引數而不是字串:

Parameters:
  VpcCidr:
    Type: String
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
  PublicSubnetCidrs:
    Type: List<String>
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
  # 依此類別推...
  MiddlewareSubnetCidrs:
    Type: List<String>
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
  DatabaseSubnetCidrs:
    Type: List<String>
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
  Environment:
    Type: String
    AllowedValues: ["prod", "test"]

內容解密:

此變更將多個子網路的CIDR範圍合併成一個列表引數,使得管理更加方便。

由於我們使用的是列表,我們不能再使用Fn::Ref(雖然技術上可以,但會傳遞整個列表)。相反,我們將使用Fn::Select

PublicSubnet1:
  Type: 'AWS::EC2::Subnet'
  Properties:
    CidrBlock: !Select [0, PublicSubnetCidrs]
    VpcId: !Ref Vpc

PublicSubnet2:
  Type: 'AWS::EC2::Subnet'
  Properties:
    CidrBlock: !Select [1, PublicSubnetCidrs]
    VpcId: !Ref Vpc

PublicSubnet3:
  Type: 'AWS::EC2::Subnet'
  Properties:
    CidrBlock: !Select [2, PublicSubnetCidrs]
    VpcId: !Ref Vpc

內容解密:

Fn::Select函式用於從列表中選擇特定的元素。在此例中,我們用它來選擇PublicSubnetCidrs列表中的特定CIDR範圍,並將其分配給不同的子網路。

現在,我們需要稍微更改引數檔案:

{
  "ParameterKey": "PublicSubnetCidrs",
  "ParameterValue": [
    "10.0.1.0/24",
    "10.0.2.0/24",
    "10.0.3.0/24"
  ]
}

內容解密:

此JSON範例展示瞭如何為列表引數PublicSubnetCidrs提供多個CIDR範圍值。

這樣看起來好多了,對吧?

隨著雲端運算技術的不斷進步,範本開發的最佳實踐也在不斷演變。未來,我們可以期待看到更多關於範本模組化、自動化和安全性的創新。持續關注AWS CloudFormation和其他IaC工具的新功能和最佳實踐,將有助於保持您的基礎設施管理技能與時俱進。

進階範本開發

在建立可重複使用的 CloudFormation 範本時,我們經常需要處理網路設定的細節。特別是在設定多個子網路(Subnet)時,手動指定 CIDR 範圍可能既繁瑣又容易出錯。幸運的是,AWS CloudFormation 提供了內建函式 Fn::Cidr,讓我們能夠更輕鬆地管理子網路的 CIDR 範圍。

使用 Fn::Cidr 自動生成 CIDR 範圍

Fn::Cidr 函式的主要功能是將一個較大的 CIDR 範圍分割成多個較小的 CIDR 範圍。這在設計網路架構時非常有用,特別是當你需要建立多個子網路,而這些子網路都來自同一個大的 IP 範圍。

假設我們的 VPC CIDR 範圍定義在引數檔案中,如下所示:

[
  {
    "ParameterKey": "VpcCidr",
    "ParameterValue": "10.0.0.0/16"
  }
]

我們需要將這個大的 CIDR 範圍(/16)分割成多個較小的 /24 範圍。為此,我們可以使用 Fn::Cidr 函式,並提供以下引數:

  • ipBlock: 我們的 VPC CIDR 範圍。
  • count: 需要生成的 CIDR 範圍數量。
  • cidrBits: 指定生成的 CIDR 範圍的遮罩大小。對於 /24 範圍,我們需要指定 8

程式碼範例:使用 Fn::Cidr 生成 CIDR 範圍

PublicSubnet1:
  Type: 'AWS::EC2::Subnet'
  Properties:
    CidrBlock: !Select [0, !Cidr [!Ref VpcCidr, 9, 8]]
    VpcId: !Ref Vpc

PublicSubnet2:
  Type: 'AWS::EC2::Subnet'
  Properties:
    CidrBlock: !Select [1, !Cidr [!Ref VpcCidr, 9, 8]]
    VpcId: !Ref Vpc

PublicSubnet3:
  Type: 'AWS::EC2::Subnet'
  Properties:
    CidrBlock: !Select [2, !Cidr [!Ref VpcCidr, 9, 8]]
    VpcId: !Ref Vpc

內容解密:

在上述程式碼中,我們使用了 Fn::Cidr 函式來生成多個 /24 CIDR 範圍。!Cidr [!Ref VpcCidr, 9, 8] 表示根據 VpcCidr 引數生成 9 個 /24 的 CIDR 範圍。然後,我們使用 !Select 函式從生成的列表中選擇特定的 CIDR 範圍,並將其分配給不同的子網路。

使用條件元素

在某些情況下,我們希望根據不同的環境(例如測試環境和生產環境)建立不同的資源組態。CloudFormation 的條件元素(Conditions)允許我們根據引數值來決定是否建立某個資源,或是為資源指定不同的屬性。

程式碼範例:使用條件元素

Parameters:
  Env:
    Type: String
    Default: test
    AllowedValues: ["test", "prod"]
  ImageId:
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'

Conditions:
  IsProd: !Equals [!Ref Env, "prod"]

Resources:
  WebLt:
    Type: 'AWS::EC2::LaunchTemplate'
    Properties:
      LaunchTemplateName: "web"
      LaunchTemplateData:
        ImageId: !Ref ImageId
        InstanceType: !If [IsProd, "m5.large", "t3.micro"]

內容解密:

在這個範例中,我們定義了一個名為 Env 的引數,用於區分測試環境和生產環境。根據 Env 的值,IsProd 條件會被評估為 truefalse。然後,在 WebLt 資源中,我們使用 !If 函式根據 IsProd 條件的值來選擇 EC2 例項的型別。如果 Envprod,則使用 m5.large;否則,使用 t3.micro

刪除政策

在管理 CloudFormation 堆積疊時,我們經常需要確保關鍵資源不會被意外刪除。刪除政策(Deletion Policies)提供了一種機制,讓我們能夠控制資源在堆積疊刪除時的行為。

程式碼範例:使用刪除政策

Resources:
  VeryImportantDb:
    Type: 'AWS::RDS::DBInstance'
    DeletionPolicy: Snapshot
    Properties:
      # RDS 例項的屬性設定

內容解密:

在這個範例中,我們為 VeryImportantDb 資源指定了 DeletionPolicy: Snapshot。這意味著當我們刪除包含這個資源的 CloudFormation 堆積疊時,CloudFormation 會先為 RDS 例項建立一個快照,然後再刪除該例項。這樣可以確保資料的安全。

隨著雲端運算技術的不斷進步,CloudFormation 也在不斷演進。未來,我們可以期待更多的功能和改進,以進一步簡化基礎設施的管理和佈署。作為基礎設施即程式碼(IaC)的重要工具,CloudFormation 將繼續在雲端運算領域發揮關鍵作用。

安全性考量

在使用 CloudFormation 時,安全性是一個非常重要的考量。我們需要確保範本的安全性,避免敏感資訊洩露。此外,合理使用刪除政策和條件元素,也可以提高資源的安全性。

效能最佳化

在設計 CloudFormation 範本時,效能最佳化是一個重要的方面。合理使用內建函式,如 Fn::Cidr,可以提高範本的靈活性。同時,精簡範本結構,避免不必要的資源定義,也可以提高佈署效率。