Cloudformation里面,整个架构都是在template里面定义的,然后通过这个template生成对应的Stack

AWS官方提供了一个参考手册,https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-whatis-howdoesitwork.html

非常的详细和有用,初看有些枯燥,但是细细品味很有意思,下面是豆子的学习笔记。

在template里面,他一共有9个section可以定义,但是只有resource这个模块是必须存在的,其他8个模块都是可选项。这9个模块分别是Format Version,MetaData,Parameters, Mappings, Conditons, Transform,Resources 和 Outputs。

基本的格式如下所示 JSON 版

{
  "AWSTemplateFormatVersion" : "version date",

  "Description" : "JSON string",

  "Metadata" : {
    template metadata
  },

  "Parameters" : {
    set of parameters
  },

  "Mappings" : {
    set of mappings
  },

  "Conditions" : {
    set of conditions
  },

  "Transform" : {
    set of transforms
  },

  "Resources" : {
    set of resources
  },

  "Outputs" : {
    set of outputs
  }
}

下面来分别看看每个模块能做啥。

Format Version ( 可选)

他定义了template可以实现的功能。目前最新版本的就是一个 2010-09-09

例如:

"AWSTemplateFormatVersion" : "2010-09-09" ** Description ( 可选 )** 这个也很容易理解,就是添加对模板的说明的。对于YAML来说,可以在template里面随时添加注释,但是对于JSON而言,这个是唯一一个能添加注释说明的地方。

例如:

"Description" : "Here are some details about the template."

Metadata ( 可选 )

可以在里面添加任意的对象,来对template进行额外的说明。

"Metadata" : {
  "Instances" : {"Description" : "Information about the instances"},
  "Databases" : {"Description" : "Information about the databases"}
}

Parameters (可选) 这个比起前三个section来说,要复杂的多,这个地方是用于提供给用户可以自己自定义参数的地方。

具体可以定义哪些参数,参考链接 https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html

一个基本要求是

  1. 一个模板里面最多60个参数
  2. 每个参数需要名字
  3. 每个参数需要定义类型
  4. 每个参数在执行模板的时候需要传入一个值。我们可以定义这个值的范围,缺省值等等
  5. 可以通过内部函数Ref来调用我们的参数,这个调用一般是在Resource或者Output 这两个section里面使用。

例如:

"Parameters" : {
  "DBPort" : {
    "Default" : "3306",
    "Description" : "TCP/IP port for the database",
    "Type" : "Number",
    "MinValue" : "1150",
    "MaxValue" : "65535"
  },
  "DBPwd" : {
    "NoEcho" : "true",
    "Description" : "The database admin account password",
    "Type" : "String",
    "MinLength" : "1",
    "MaxLength" : "41",
    "AllowedPattern" : "^[a-zA-Z0-9]*$"
  }
}

**Mapping ( 可选 ) ** 这个是通过匹配不同的key来获取不同的value。一个常见的使用是场景是通过不同的region,自动选择对应的AMI镜像文件。

例如:

下面是一个完整的cf文件,里面我们只使用了3个section,分别是Format Version,Mappings 和 Resources。 Mappings 定义了个嵌套的二级JSON对象

Mapping的结构这么大,如何获取对应的信息呢?我们可以通过一个内置函数 Fn::FindInMap来获取。Fn::FindInMap 需要指定 Mapping的名字RegionalMap,第一层的Key region的名字, 以及第二层的key 架构的名字,从而获得对应的AMI的id。这个操作是在Resource部分实行的。这里还调用了一个全局变量 Presudo Parameter AWS::Region 来自动获取用户所在的Region的名字

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

  "Mappings" : {
    "RegionMap" : {
      "us-east-1"        : {"HVM64" : "ami-0ff8a91507f77f867", "HVMG2" : "ami-0a584ac55a7631c0c"},
      "us-west-1"        : {"HVM64" : "ami-0bdb828fd58c52235", "HVMG2" : "ami-066ee5fd4a9ef77f1"},
      "eu-west-1"        : {"HVM64" : "ami-047bb4163c506cd98", "HVMG2" : "ami-0a7c483d527806435"},
      "ap-northeast-1"   : {"HVM64" : "ami-06cd52961ce9f0d85", "HVMG2" : "ami-053cdd503598e4a9d"},
      "ap-southeast-1"   : {"HVM64" : "ami-08569b978cc4dfa10", "HVMG2" : "ami-0be9df32ae9f92309"}
    }
  },

  "Resources" : {
    "myEC2Instance" : {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "ImageId" : { "Fn::FindInMap" : [ "RegionMap", { "Ref" : "AWS::Region" }, "HVM64"]},
        "InstanceType" : "m1.small"
      }
    }
  }
}

来实际的运行一下看看结果, 创建Stack之后,看看自动生成的EC2实例。我所在的区域是 North Virginia, 也就是 us-east-1, 他会

看看EC2的AMI ID,这与我们在Mapping里面定义的是一致的

Condition ( 可选 ) 接下来,看看Condition 这个模块。这个模块的作用类似于if..else 语句,如果某种情况为真,那么进行操作,否则进行另外的操作。

他的典型使用方式如下:

Parameter setction: 定义你打算比较的输入参数

Condition section:利用内部函数进行判断,内部函数包括Fn::And , Fn:: Equals, Fn::If, Fn::Not, Fn::Not 五个操作,基本语法:


"Conditions" : {

  "Logical ID" : {Intrinsic function}
}

Resource and Outputs section: 这两个模块里面进行关联condition,凡是关联为真的rescource才会被创建。

下面看一个实例进行说明。这两个例子是如果用户创建的是test环境,那么就给创建一个实例,如果是prod的环境,那就在实例的基础上,再添加一个volume

首先还是Mapping 模块,这个上面的例子已经实验过来。 接下来是Parameters 模块,他提供了两个支持的值供用户选择 接下来是Condition 模块,按照我们说的语法,给了一个名字叫CreateProdResource,后面是一个嵌套的内置函数,首先获取EnvType 参数的值,然后和prod这个关键词进行比较,如果一样,就返还真,否则返回假 接下来在resources 模块,创建了3个resources,第一个通过Mappnig来创建EC2,上一个例子已经测试过;第二个resource是创建一个挂载点,里面有一个通过conditon这个关键字来获取对应的条件,第三个类似,通过condition来获取前面定义的条件,另外通过内置函数Fn::GetAtt获取AZ的值 最后是Outputs模块,同样指定了condition 为真,才返回结果


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

  "Mappings" : {
    "RegionMap" : {
      "us-east-1"      : { "AMI" : "ami-0ff8a91507f77f867", "TestAz" : "us-east-1a" },
      "us-west-1"      : { "AMI" : "ami-0bdb828fd58c52235", "TestAz" : "us-west-1a" },
      "us-west-2"      : { "AMI" : "ami-a0cfeed8", "TestAz" : "us-west-2a" },
      "eu-west-1"      : { "AMI" : "ami-047bb4163c506cd98", "TestAz" : "eu-west-1a" },
      "sa-east-1"      : { "AMI" : "ami-07b14488da8ea02a0", "TestAz" : "sa-east-1a" },
      "ap-southeast-1" : { "AMI" : "ami-08569b978cc4dfa10", "TestAz" : "ap-southeast-1a" },
      "ap-southeast-2" : { "AMI" : "ami-09b42976632b27e9b", "TestAz" : "ap-southeast-2a" },
      "ap-northeast-1" : { "AMI" : "ami-06cd52961ce9f0d85", "TestAz" : "ap-northeast-1a" }
    }
  },
    
  "Parameters" : {
    "EnvType" : {
      "Description" : "Environment type.",
      "Default" : "test",
      "Type" : "String",
      "AllowedValues" : ["prod", "test"],
      "ConstraintDescription" : "must specify prod or test."
    }
  },
  
  "Conditions" : {
    "CreateProdResources" : {"Fn::Equals" : [{"Ref" : "EnvType"}, "prod"]}
  },
  
  "Resources" : {
    "EC2Instance" : {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "ImageId" : { "Fn::FindInMap" : [ "RegionMap", { "Ref" : "AWS::Region" }, "AMI" ]}
      }
    },
    
    "MountPoint" : {
      "Type" : "AWS::EC2::VolumeAttachment",
      "Condition" : "CreateProdResources",
      "Properties" : {
        "InstanceId" : { "Ref" : "EC2Instance" },
        "VolumeId"  : { "Ref" : "NewVolume" },
        "Device" : "/dev/sdh"
      }
    },

    "NewVolume" : {
      "Type" : "AWS::EC2::Volume",
      "Condition" : "CreateProdResources",
      "Properties" : {
        "Size" : "100",
        "AvailabilityZone" : { "Fn::GetAtt" : [ "EC2Instance", "AvailabilityZone" ]}
      }
    }
  },
  
  "Outputs" : {
    "VolumeId" : {
      "Value" : { "Ref" : "NewVolume" }, 
      "Condition" : "CreateProdResources"
    }
  }  
}

来实际跑一下

这里可以允许用户选择prod还是test, 我选择了prod

看看他的stack创建resource的顺序,首先创建了EC2和New Volume,然后创建了Mountpoint

看看创建好的volume

以及对应的Outputs

**Tranform ( 可选) **这个主要是调用Lambda,后面有具体例子讲解

**Resources (必选) ** 这里面我们创建对应的资源。每种AWS 服务都有自己的属性值需要定义,具体使用可以参考这个链接 https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html

Outputs ( 可选) 这里的输出结果可以在其他stack里面调用,也可以在console里面显示结果。

语法格式如下

"Outputs" : {
  "Logical ID" : {
    "Description" : "Information about the value",
    "Value" : "Value to return",
    "Export" : {
      "Name" : "Value to export"
    }
  }
}

至此,9个基本的section简单的过了一遍,下一篇来看看Template里面的intrinsic function(内置函数)