ECS-05-企业级ECS集群构建
使用资源编排服务进行云上资源创建和管理。相较于使用其他方式管理云上资源,使用资源编排服务时,云上用户可以方便的使用资源模板文件的方式,创建和管理云上资源。这种IaC(基础设计即代码)的资源管理方式,让资源的创建和运维变得标准化,规范化。杜绝了人为创建资源带来的潜在风险。
1. 需求
- 应用采用分布式服务架构设计,大部分服务集群都是LAMP架构。
- 每个服务都放在单独的ECS里面运行,在ECS上层提供统一的CLB访问接口。
- 不同服务集群的负载并不一致,因此需要分配不同的ECS镜像和计算资源。
- 由于需要经常测试,每天可能会重复部署很多次。因此希望部署工作能控制在10分钟之内完成。
运维工作就是非常繁琐和枯燥的,在IDC机房里面做应用上线前的测试时,经常遇到的问题基本有以下几个难点:
- 硬件配置繁琐
- 网络配置繁琐
- 镜像版本复杂
- 部署时间紧迫
上云了,可以使用IaC的方式来创建和管理云资源了。
2. 资源编排服务
IaC(基础设施即代码):可理解为把运维人员复杂的部署工作写成一份配置文件,然后通过配置文件来创建环境。
用脚本来自动创建是一个管理资源方式,可以解决部署速度慢的问题。但是它本身也存在着一些问题:
首先Python,bash这样的编程语言是为了实现逻辑计算设计的,并不是为了管理资源而设计的。因此使用编程语言会让代码非常的冗余。
其次使用编写脚本管理资源的时候,每个人都有自己的编写习惯,这样就使得资源管理脚本不容易阅读,一旦维护人员更换,接手的同事往往会对留下来的脚本无从下手。甚至就算是自己编写的脚本,但是由于搁置太久,连自己都忘了最初的逻辑。因此使用脚本管理资源不能算是基础设施即代码。
一般来说在云上使用IaC的时候,我们会使用配置文件而不是脚本文件来管理资源。在配置文件中我们只需要描述资源的类型、属性、相互关系之类的信息就可以了。为了能让IaC的配置文件转化成IT资源,就需要用到云平台的能力了。在阿里云上有一个专门的产品叫资源编排服务ROS(Resource Orchestration Service),就是负责完成这个工作的。
ROS控制台:https://rosnext.console.aliyun.com/overview
资源栈 > 创建资源栈 > 使用Teeraform > 选择已有模板 > 输入模板 > Terraform > main.tf
resource "alicloud_vpc" "ros_vpc" {
vpc_name = "ROS_VPC"
cidr_block = "192.168.0.0/16"
}
resource "alicloud_vswitch" "ros_vsw" {
vswitch_name = "ROS_VSWITCH"
zone_id = "cn-hangzhou-b"
vpc_id = alicloud_vpc.ros_vpc.id
cidr_block = "192.168.1.0/24"
}
专有网络控制台:https://vpc.console.aliyun.com/vpc/cn-hangzhou/vpcs
已经自动创建了一个名为ROS_VPC的专有网络,在专有网络中,还包含了一个名为ROS_VSWITCH的交换机。
ROS的基本概念:
➢ 在ROS中资源栈是基本的资源管理单元
➢ 资源栈中又包含了模板文件和云上资源
➢ 模板文件可以使用ROS模板或Terraform模板
➢ ROS模板文件又可以使用JSON格式或者YAML格式
➢ Terraform模板采用特有的TF格式
➢ 运行模板文件可以创建相应的云上资源

一般来说,在使用的ROS的时候,先创建资源栈,再在资源栈中创建模板文件,最后通过模板文件生成对应的云上资源。
Terraform文件格式:
在ROS中模板文件有两种形式,一种是阿里云自有的ROS模板,可以使用JSON格式或YAML格式书写。另一种是比较通用的Terraform模板,这种形式的解析引擎开源,同时也被多个主流云平台支持,同时其文件格式也比较便于阅读,因此比较推荐使用Terraform模板。
➢ Terraform格式的配置采用文件和文件夹的方式管理,配置文件的后缀名是.tf
➢ Terraform的默认配置文件入口是main.tf
➢ Terraform配置文件中又包含了:资源,变量,输出等元素
Terraform中元素的常见描述格式为:
元素种类 "元素类型" "元素名"{
属性名1 = 属性值1
属性名2 = 属性值2
...
}
e.g
resource "alicloud_vpc" "ros_vpc" {
vpc_name = "ROS_VPC"
cidr_block = "192.168.0.0/16"
}
以上描述了一个VPC。其中 resource 表示这一段描述了一个资源,“alicloud_vpc”表示这一段描述了一个VPC,“ros_vpc”是这个资源在Terraform中的名字。vpc_name 和 cidr_block 是元素的属性。
在描述元素的时候,有的元素的属性会依赖到另一个元素或者另一个元素的属性。当我们需要从另一个元素中引用属性的时候,我们可以使用 元素类型.元素名.属性名 的格式。如下,配置交换机的时候需要一个交换机id的属性vpc_id。这个属性就需要从VPC中进行获取。可以使用alicloud_vpc.ros_vpc.id的方式来引用。
resource "alicloud_vswitch" "ros_vsw" {
vswitch_name = "ROS_VSWITCH"
zone_id = "cn-hangzhou-b"
vpc_id = alicloud_vpc.ros_vpc.id
cidr_block = "192.168.1.0/24"
}
查询Terraform支持的元素和属性:
Terraform的官方网站:https://registry.terraform.io/providers/aliyun/alicloud/latest/docs

3. Variable
创建资源栈 > 使用Terraform > 选择已有模板 > 输入模板 > Terraform > main.tf
variable "vpc_cidr" {
type = string
default = "192.168.0.0/16"
}
resource "alicloud_vpc" "ros_vpc" {
vpc_name = "ROS_VPC"
cidr_block = var.vpc_cidr
}
Terraform模板还可以在页面输入参数
Terraform的variable参数功能除了可以显示标准的输入框之外,还支持配置输入框。
在资源栈控制台中,使用Terraform创建资源栈
variable "vpc_cidr" {
type = string
default = "192.168.0.0/16"
description = <<EOT
{
"Label": {
"en": "VPC cidr",
"zh-cn": "VPC 网段"
},
"AllowedValues": [
"10.0.0.0/16",
"172.16.0.0/16",
"192.168.0.0/16"
]
}
EOT
}
resource "alicloud_vpc" "ros_vpc" {
vpc_name = "ROS_VPC"
cidr_block = var.vpc_cidr
}
文件编辑完成后,点击下一步按钮,会看到输入框的标题变成了VPC网段,同时VPC网段只能选择三个网络地址。
上一步,回到模板编辑界面,修改main.tf文件为如下内容
variable "vpc_name" {
type = string
default = "VPC名称"
description = <<EOT
{
"Label": {
"en": "VPC Name",
"zh-cn": "VPC名称"
},
"MinLength": 2,
"MaxLength": 8
}
EOT
}
variable "vpc_cidr" {
type = string
default = "192.168.0.0/16"
description = <<EOT
{
"Label": {
"en": "VPC cidr",
"zh-cn": "VPC CIDR"
},
"AllowedValues": [
"10.0.0.0/16",
"172.16.0.0/16",
"192.168.0.0/16"
]
}
EOT
}
resource "alicloud_vpc" "ros_vpc" {
vpc_name = var.vpc_name
cidr_block = var.vpc_cidr
}
编辑结束后,按下一步按钮之后,会看到配置模板参数阶段,出现了两个控件,在VPC名称输入框中输入超过8个字符,会发现出现错误提示。
我们将VPC名称修改为符合要求的长度。然后点击创建。等待一段时间后发现,资源将按照指定的参数成功创建。
variable显示系统界面:
有了输入参数界面,模板的使用就灵活多了,能够适应多种不同的部署场合了。在部署网络环境的时候,经常会遇到可用区的选择,每个地域的可用区都不同。遇到这种情况可以通过variable来调用系统的界面进行输入。
回到资源栈列表,找到刚才创建的资源栈条目,更新。
进入更新状态后,会跳到配置模板参数页面,此时我们需要修改模板的文件内容,因此我们先点击上一步按钮。
在选择模板页面,我们选择替换当前模板和选择已有模板两个选项。
在模板录入方式中,选择输入模板选项。会看到页面回到了文件编辑界面。
我们打开main.tf文件,在文件中追加如下内容
variable "vswitch_name" {
type = string
default = "交换机名称"
description = <<EOT
{
"Label": {
"en": "VSwitch Name",
"zh-cn": "交换机名称"
},
"MinLength": 2,
"MaxLength": 8
}
EOT
}
variable "zone_id" {
type = string
description = <<EOT
{
"Label": {
"en": "Availability Zone",
"zh-cn": "交换机可用区"
},
"AssociationProperty": "ALIYUN::ECS::Instance::ZoneId"
}
EOT
}
resource "alicloud_vswitch" "ros_vsw" {
vswitch_name = var.vswitch_name
zone_id = var.zone_id
vpc_id = alicloud_vpc.ros_vpc.id
cidr_block = var.vpc_cidr
}
编辑完毕后,下一步。在配置模板参数页面,会看到可用区的选择控件。
我们选择一个可用区,然后再尝试修改一下VPC的名称和VPC网段。选择完毕后,我们点击确认修改按钮。等待一段时候之后发现资源栈更新成功了。
进入VPC控制台,会发现经过更新之后,VPC的名称和网段已经更新成功,同时在VPC中也也创建了新的交换机。
variable总结:
➢ 添加variable段落可以为模板提供用户输入参数的界面
➢ variable段落的格式为variable 参数名称{属性名1=属性值1...}
➢ variable段落中可以定义type和default等属性
➢ resource引用variable时可以使用var.参数名称的格式
➢ variable的description的属性可以使用JSON格式进行更加详细的配置
➢ variable的description的JSON格式配置的前后需要添加<<EOT和EOT
➢ description中的Label属性可以为输入界面定义提示
➢ description中的MinLength和MaxLength可以对输入框进行限制
➢ description中的AllowedValues属性可以将输入框改为单选框
➢ description中的AssociationProperty属性可以调用系统的输入控件
4. 根据动态参数创建云网络资源
Terraform来调用系统界面,从零创建云上资源了,但是大部分的情况下VPC资源都是创建好的,如何在已有的VPC上创建交换机呢?
其实在生产环境中使用脚本,要考虑各种各样的情况,你刚才提到的VPC就是一个最典型的例子。为了能兼容不同的环境。Terrform提供了表达式来做一些简单的逻辑判断,通过逻辑判断就可以让脚本根据用户的不同输入执行不同的流程。
创建是否创建VPC按钮:
在资源栈中页面中,使用新资源创建资源栈,选择Terrform模板格式
首先创建一个bool类型的变量whether_create_vpc。
variable "whether_create_vpc" {
type = bool
description = <<EOT
{
"Label": {
"zh-cn": "是否新建VPC",
"en": "WhetherCreateVpc"
}
}
EOT
default = true
}
输入完毕后,按下一步按钮。会发现bool类型的变量,可以生成一个开关类型的控件。
创建VPC选择控件:
A. 接下来创建一个string类型的变量vpc_id,按上一步按钮返回编辑界面。继续编辑main.tf
B. 然后通过AssociationProperty属性,将这个变量和界面 ALIYUN::ECS::VPC::VPCId 结合
C. 我们在main.tf文件中继续编辑,追加如下内容:
variable "vpc_id" {
type = string
default = null
description = <<EOT
{
"Type": "String",
"Default": "",
"Label": {
"en": "VPC ID",
"zh-cn": "专有网络VPC实例ID"
},
"AssociationProperty": "ALIYUN::ECS::VPC::VPCId"
}
EOT
}
输入完毕后,按下一步按钮。会发现通过下拉框可以选择已经存在的VPC:
创建新VPC控件:
A. 接下来我们将vpc_id变量和whether_create_vpc变量进行关联,只有在whether_create_vpc为false的时候才显示vpc_id控件。
B. 控件的关联可以使用AssociationPropertyMetadata.Visible.Condition属性来控制是否显示。
C. 使用Fn::Equals函数可以对变量的值进判断。
D. 在嵌入的ROS模板中,如果需要引用variable变量,可以使用$${变量名}的方式。
E. 我们在main.tf文件中继续编辑,修改vpc_id变量的定义。
variable "vpc_id" {
type = string
default = null
description = <<EOT
{
"Type": "String",
"Default": "",
"Label": {
"en": "VPC ID",
"zh-cn": "专有网络VPC实例ID"
},
"AssociationProperty": "ALIYUN::ECS::VPC::VPCId",
"AssociationPropertyMetadata": {
"Visible": {
"Condition": {
"Fn::Equals": [
"$${whether_create_vpc}",
false
]
}
}
}
}
EOT
}
输入完毕后,按下一步按钮。会发现我们可以根据whether_create_vpc来控制是否显示vpc_id控件。
创建交换机控件
A. 添加交换机地域和命令控件。
B. 我们在main.tf文件中继续编辑,添加vswitch_name和zone_id
variable "zone_id" {
type = string
description = <<EOT
{
"Label": {
"en": "Availability Zone",
"zh-cn": "可用区"
},
"AssociationProperty": "ALIYUN::ECS::Instance::ZoneId"
}
EOT
}
variable "vswitch_name" {
type = string
default = "交换机名称"
description = <<EOT
{
"Label": {
"en": "VSwitch Name",
"zh-cn": "交换机名称"
},
"MinLength": 2,
"MaxLength": 128
}
EOT
}
A. 接下来来添加交换机cidr输入控件,这时可以使用ALIYUN::VPC::VSwitch::CidrBlock属性控件。
B. 在使用交换机cidr控件时,需要设置地域属性RegionId。这时我们可以使用ALIYUN::Region来获得地域信息。
C. 除了地域之外,交换机cidr控件还需要设置VpcId和VpcCidrBlock。这两个属性,可以从vpc_id和vpc_cidr两个参数中获得。
D. 由于新建VPC和选择已有VPC时,交换机cidr的合规性检查逻辑不同,因此此处我们创建两个控件。然后根据whether_create_vpc来进行控件的切换。
E. 在main.tf文件中继续编辑,添加两个交换机cird控件。create_vswitch_cidr和exist_vswitch_cidr
variable "create_vswitch_cidr" {
type = string
description = <<EOT
{
"Label": {
"en": "VSwitch cidr",
"zh-cn": "交换机CIDR"
},
"Default": "192.168.0.0/24",
"AssociationProperty": "ALIYUN::VPC::VSwitch::CidrBlock",
"AssociationPropertyMetadata": {
"RegionId": "$${ALIYUN::Region}",
"VpcCidrBlock": "$${vpc_cidr}",
"Visible": {
"Condition": {
"Fn::Equals": [
"$${whether_create_vpc}",
true
]
}
}
}
}
EOT
}
variable "exist_vswitch_cidr" {
type = string
description = <<EOT
{
"Label": {
"en": "VSwitch cidr",
"zh-cn": "交换机CIDR"
},
"Default": "192.168.0.0/24",
"AssociationProperty": "ALIYUN::VPC::VSwitch::CidrBlock",
"AssociationPropertyMetadata": {
"RegionId": "$${ALIYUN::Region}",
"VpcId": "$${vpc_id}",
"VpcCidrBlock": "$${vpc_cidr}",
"Visible": {
"Condition": {
"Fn::Equals": [
"$${whether_create_vpc}",
false
]
}
}
}
}
EOT
}
resource "alicloud_vswitch" "ros_vsw" {
vswitch_name = var.vswitch_name
zone_id = var.zone_id
vpc_id = var.whether_create_vpc ? alicloud_vpc.ros_vpc.id : var.vpc_id
cidr_block = var.whether_create_vpc ? var.create_vswitch_cidr : var.exist_vswitch_cidr
}
配置参数创建资源:
模板文件创建完成后,我们点击下一步按钮。可以看到VPC和交换机的配置页面。
完成属性配置之后,点击创建按钮。稍等片刻即可创建资源。
➢ 为variable指定不同的type类型,可以更改输入控件的类型
➢ 在variable的description属性的JSON配置中,可以使用$${参数名}的方式来引用其他的variable
➢ description属性的JSON配置中,可以在AssociationPropertyMetadata属性中,做进一步的配置
➢ 如果description指定了AssociationProperty调用系统界面,AssociationPropertyMetadata可以对系统界面进行配置
➢ 在AssociationPropertyMetadata属性中,还可以使用Visible来控制该控件是否被显示
➢ 在Visible属性中,可以使用Fn::Equals方式进行逻辑判断。实现根据属性值选择是否显示控件
➢ 在terraform中可以使用?:三项算符进行逻辑判断
5. 通过更改集更新模板
用Terraform进行资源管理,可以通过Terraform的ParameterGroups来对参数进行分组,另外,更新资源栈的操作中,除了直接更新资源栈的方法,还有一种使用更改集的方法来升级资源栈。
5.1 更改集入门
更改集 > 创建更改集 > 选择已有模板 > 输入模板
5.2 创建.metadata文件
接下来我们在Terrform中添加一个.metadata文件,通过这个文件来指定输入参数的顺序。
创建文件 > .metadata
{
"ALIYUN::ROS::Interface": {
"ParameterGroups": [
{
"Parameters": [
"whether_create_vpc",
"vpc_id",
"vpc_name",
"vpc_cidr"
],
"Label": {
"default": {
"zh-cn": "VPC属性配置"
}
}
},
{
"Parameters": [
"vswitch_name",
"zone_id",
"create_vswitch_cidr",
"exist_vswitch_cidr"
],
"Label": {
"default": {
"zh-cn": "交换机属性配置"
}
}
}
]
}
}
下一步 > 配置模板参数
在界面中我们发现输入控件已经进行了排序和分类。同时每一个分类都有自己的标签。
Metadata.ALIYUN::ROS::Interface.ParameterGroups属性将输入参数进行了分组。
5.3 创建更改集
通过更改集来更新资源栈
我们在参数编辑框中,对VPC和交换机的名称做一个修改。修改完毕后,我们点击创建更改集按钮
在等待一段时间后,回到更改集选项卡,点击刷新按钮之后,会出现一个新的更改集。
更新集创建完成后,我们回到VPC控制台,会发现VPC和交换机的名称都没有改变。
5.4 执行更改集
更改集创建之后,VPC和交换机的名称并没有改变。更改集创建后还需要执行更改集才可以完成资源的更新,更改集的好处是可以将编写和执行操作分开,使用新模板更新资源栈前,通过更改集查看即将产生的变更(比如新增了哪些资源、删除了哪些资源、对哪些资源的特定属性做了修改),确认无误后再执行,更好的保障更新能够顺利安全的进行。
执行,等待一段时候后,刷新资源栈信息,会发现资源栈的状态变更为更新成功。再回到更改集页面,发现执行成功的更改集已经消失,回到VPC控制台,发现VPC和交换机的名称都已经更新了。
6. 创建安全组和安全组规则
通过表达式和显示控制功能,基本上可以应付绝大多数的场景了
6.1 创建安全组名称控件
首先准备安全组的相关变量和资源。在添加安全组时,只需要设一个安全组名称变量security_group_name,安全组资源则使用security_group_name类型
main.tf
variable "vpc_name" {
type = string
default = "VPC名称"
description = <<EOT
{
"Label": {
"en": "VPC Name",
"zh-cn": "VPC名称"
},
"MinLength": 2,
"MaxLength": 128
}
EOT
}
variable "vpc_cidr" {
type = string
default = "192.168.0.0/16"
description = <<EOT
{
"Label": {
"en": "VPC cidr",
"zh-cn": "VPC CIDR"
},
"AllowedValues": [
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16"
]
}
EOT
}
resource "alicloud_vpc" "ros_vpc" {
vpc_name = var.vpc_name
cidr_block = var.vpc_cidr
}
variable "zone_id" {
type = string
description = <<EOT
{
"Label": {
"en": "Availability Zone",
"zh-cn": "可用区"
},
"AssociationProperty": "ALIYUN::ECS::Instance::ZoneId"
}
EOT
}
variable "vswitch_name" {
type = string
default = "交换机名称"
description = <<EOT
{
"Label": {
"en": "VSwitch Name",
"zh-cn": "交换机名称"
},
"MinLength": 2,
"MaxLength": 128
}
EOT
}
variable "vswitch_cidr" {
type = string
description = <<EOT
{
"Label": {
"en": "VSwitch cidr",
"zh-cn": "交换机CIDR"
},
"Default": "192.168.0.0/24",
"AssociationProperty": "ALIYUN::VPC::VSwitch::CidrBlock",
"AssociationPropertyMetadata": {
"RegionId": "$${ALIYUN::Region}",
"VpcCidrBlock": "$${vpc_cidr}"
}
}
EOT
}
resource "alicloud_vswitch" "ros_vsw" {
vswitch_name = var.vswitch_name
zone_id = var.zone_id
vpc_id = alicloud_vpc.ros_vpc.id
cidr_block = var.vswitch_cidr
}
variable "security_group_name" {
type = string
default = "安全组名称"
description = <<EOT
{
"Label": {
"en": "VSwitch Name",
"zh-cn": "安全组名称"
},
"MinLength": 2,
"MaxLength": 128
}
EOT
}
resource "alicloud_security_group" "ros_sg" {
vpc_id = alicloud_vpc.ros_vpc.id
name = var.security_group_name
}
6.2 列表界面实现安全组规则输入
为安全组添加规则,默认的安全组规则开放22,80,443三个端口。一条安全组规则的配置选项很多,包括协议、端口、cidr、优先级等。一个安全组中又可以包含多条安全组规则。对于复杂配项目可以使用嵌套类型来定义。对于多条安全规则的编辑,可使用列表编辑界面进行编辑。
variable "security_group_rules" {
description = <<EOT
{
"Label": {
"en": "security group rules",
"zh-cn": "安全组规则"
},
"Type": "Json",
"AssociationProperty": "List[Parameters]",
"AssociationPropertyMetadata": {
"Parameters": {
"accept": {
"Type": "Boolean",
"Label": {
"zh-cn": "是否允许访问"
}
},
"port": {
"Type": "Number",
"Label": {
"zh-cn": "端口号"
},
"MinValue": 1,
"MaxValue": 65535,
"AllowedPattern": "\\d"
},
"description": {
"Type": "String",
"Label": {
"zh-cn": "规则表述"
},
"MinLength": 0,
"MaxLength": 64
}
},
"ListMetadata": {
"ShowHeader": true,
"ShowRemove": true,
"ShowAddition": true,
"Order": [
"accept",
"port",
"description"
]
}
},
"Default": [
{
"accept": true,
"port": 22,
"description": "SSH"
},
{
"accept": true,
"port": 80,
"description": "HTTP"
},
{
"accept": true,
"port": 443,
"description": "HTTPS"
}
]
}
EOT
}
resource "alicloud_security_group_rule" "ros_sg_rule" {
security_group_id = alicloud_security_group.ros_sg.id
count = length(var.security_group_rules)
type = "ingress"
priority = 1
ip_protocol = "tcp"
nic_type = "intranet"
cidr_ip = "0.0.0.0/0"
policy = var.security_group_rules[count.index].accept ? "accept" : "drop"
port_range = join("/", [var.security_group_rules[count.index].port, var.security_group_rules[count.index].port])
description = var.security_group_rules[count.index].description
}
6.3 修改.metadata并配置编辑界面
添加安全组的显示分组,新建.metadata文件
{
"ALIYUN::ROS::Interface": {
"ParameterGroups": [
{
"Parameters": [
"vpc_name",
"vpc_cidr"
],
"Label": {
"default": {
"zh-cn": "VPC属性配置"
}
}
},
{
"Parameters": [
"vswitch_name",
"zone_id",
"vswitch_cidr"
],
"Label": {
"default": {
"zh-cn": "交换机属性配置"
}
}
},
{
"Parameters": [
"security_group_name",
"security_group_rules"
],
"Label": {
"default": {
"zh-cn": "安全组属性配置"
}
}
}
]
}
}
常见的用法:
➢ 将AssociationProperty设置为List[Parameters]可以显示列表控件
➢ AssociationPropertyMetadata中的Parameters可以用来具体定义列表中参数的类型
➢ AssociationPropertyMetadata中的ListMetadata可以用来定义列表的显示形式
➢ AssociationPropertyMetadata中的Default可以用来定义界面的默认值,默认值为数组类型
➢ 在terraform的resource段落中,可以使用count函数来获取List类型的数据长度
➢ 使用count函数之后,可以使用类似var.security_group_rules[count.index]的语法来获得List中的一个元素
➢ terraform还提供了join字符串函数。可以将不同类型的参数组合成字符串。
7. 查看和输出资源栈资源的信息
7.1 查看模板关联的资源
模板文件的内容包含的资源越来越多了,可以使用资源栈的资源列表来查看模板关联的资源。
资源栈的资源选项卡显示了模板相关的所有资源,同时在资源的资源状态栏显示着创建成功。
7.2 克隆资源栈
在克隆页面中,ROS会根据之前的资源栈的模板生成新的资源栈,并进入配置页面;在配置界面中,我们选择不创建新的VPC,然后再VPC ID控件中选择一个已有的VPC。然后根据已有VPC的网段,配置交换机的网段,然后点击创建按钮;等克隆的资源栈创建成功后,点击资源选项卡,会看到资源中并没有包含已有的VPC资源。
7.3 执行结果输出
有些资源虽然不是由模板创建的,但是和模板的关系还是很密切的,比如像刚才引用的VPC的信息。除了资源页面,还可以在Terraform脚本中使用output进行信息输出。
在原有资源栈的添加一个更改集,在创建更改集中选择输入模板,然后打开main.tf文件。
在terraform中我们可以使用output段落来输入定制化的内容;
output "vswitch_name" {
value = var.vswitch_name
description = <<EOT
{
"Description": {
"en": "VSwitch Name",
"zh-cn": "交换机名称"
}
}
EOT
}
output "vswitch_id" {
value = alicloud_vswitch.ros_vsw.id
description = <<EOT
{
"Description": {
"en": "VSwitch ID",
"zh-cn": "交换机ID"
}
}
EOT
}
output "vswitch_zone" {
value = alicloud_vswitch.ros_vsw.zone_id
description = <<EOT
{
"Description": {
"en": "VSwitch zone",
"zh-cn": "交换机可用区"
}
}
EOT
}
编辑完毕后按创建按钮,创建更改集,等更改集创建完毕后,再选择执行更改集。更改完成后,进入输出选项卡。
7.4 output表达式的用法
output输出方便很多,可以把模板生成的资源的结构都输出出来;对于一些比较复杂的资源,比如创建VPC或者使用已有VPC,output同样支持表达式和函数,通过表达式就可以将复杂的模板配置解析成便于理解的文字。
在原有资源栈中继续添加更改集,在main.tf中追加如下内容
output "vpc_mode" {
value = var.whether_create_vpc ? "创建新的VPC" : "使用已有VPC"
description = <<EOT
{
"Description": {
"en": "VPC ID",
"zh-cn": "VPC ID"
}
}
EOT
}
output "vpc_id" {
value = var.whether_create_vpc ? alicloud_vpc.ros_vpc[0].id : var.vpc_id
description = <<EOT
{
"Description": {
"en": "VPC ID",
"zh-cn": "VPC ID"
}
}
EOT
}
output "vswitch_cidr" {
value = var.whether_create_vpc ? var.create_vswitch_cidr : var.exist_vswitch_cidr
description = <<EOT
{
"Description": {
"en": "VSwitch CIDR",
"zh-cn": "交换机网段"
}
}
EOT
}
output的使用方式:
➢ output段落的格式为output 名称{属性1=值}。
➢ 在output中,最重要的属性是value,该属性负责指定要输出的内容
➢ value属性的值,可以引用variable,也可以引用resource。
➢ description属性可以和variable一样,嵌入ROS模板。
➢ 在output中可以使用:三项目算符根据变量来进行逻辑判断。
➢ output段落的格式为output 名称{属性1=值}。
➢ 在output中,最重要的属性是value,该属性负责指定要输出的内容
7.5 data段落的用法
当我使用指定VPC的时候,变量中只有VPC的id,如果我想显示指定VPC的时候,可以使用data段落来实现。
继续创建更改集,打开main.tf,并追加如下内容:
data "alicloud_vpcs" "vpcs_ds" {
ids = [var.whether_create_vpc ? alicloud_vpc.ros_vpc[0].id : var.vpc_id]
}
在main.tf中继续追加如下内容:
output "vpc_name" {
value = data.alicloud_vpcs.vpcs_ds.names.0
description = <<EOT
{
"Description": {
"en": "VPC name",
"zh-cn": "VPC 名称"
}
}
EOT
}
output "vpc_cidr" {
value = data.alicloud_vpcs.vpcs_ds.vpcs.0.cidr_block
description = <<EOT
{
"Description": {
"en": "VPC CIDR",
"zh-cn": "VPC 网段"
}
}
EOT
}
data的使用方式:
➢ data的格式为data 类型 名称{筛选属性=筛选属性值}
➢ 不同的data数据类型指向一个类型的数据集,例如alicloud_vpcs指向当前地域的所有vpc。
➢ 通过data的筛选属性,可以筛选出数据集中的指定内容。例如使用ids=[id1, id2]的格式筛选出指定id的vpc
➢ 在data的筛选属性值中同样可以使用表达式。
➢ 在output中引用data时,使用data.类型.名称.子属性的方式。
➢ data的属性一般都是数组。因此当我们需要访问其中一个元素时可以使用类似于data.alicloud_vpcs.vpcs_ds.vpcs.0.cidr_block的语法。
8. 配置并创建ECS资源
VPC、交换机、安全组都创建完毕了,接下来我们就可以创建ECS
8.1 创建ECS.tf文件
模板优化技巧:模板文件的内容过多,长度过长不太利于阅读和修改。这种情况我们一般会用多个tf文件来区分描述不同的资源。
A. 更改已经创建的资源栈
B. 进入选择模板页面,点击目录列表右侧的+,然后点击创建文件
C. 在弹出的对话框中,输入ecs.tf,然后点击确认按钮
D. 在ecs.tf中添加如下内容
output "ecs_test" {
value = "测试ECS.tf文件的加载"
}
E. 修改完成后,我们点击下一步按钮,进入配置模板参数页面。在模板中,对ECS参数进行配置。然后点击确认修改。等待更新完成后,我们切换到输出选项卡,发现ecs.tf中的output已经生效
F. 再切换到模板选项卡。发现资源栈中已经包含了两个tf文件
G. 添加实例规格和镜像的输入控件
创建ECS模板的要求:
➢ 为了熟悉模板先创建一台ECS
➢ 配置时可以指定实例的规格
➢ 配置时可以指定实例的操作系统
➢ 操作系统选择CentOS的版本
➢ 配置时可以指定实例的系统盘规格
➢ 配置时可以初始化实例的密码
首先添加ECS的实例规格和镜像输入参数,通过设置ALIYUN::ECS::Instance::InstanceType可以调用实例规格的输入控件
H. 更改已经创建的资源栈
I. 进入选择模板页面,并打开ecs.tf,然后在文件中追加了如下的内容
variable "instance_type" {
type = string
description = <<EOT
{
"Label": {
"zh-cn": "实例规格",
"en": "Instance Type"
},
"AssociationProperty": "ALIYUN::ECS::Instance::InstanceType",
"AssociationPropertyMetadata": {
"ZoneId": "$${zone_id}"
}
}
EOT
}
variable "ecs_image" {
type = string
default = "CentOS 7.9"
description = <<EOT
{
"Label": {
"en": "ECS Image",
"zh-cn": "ECS 镜像"
},
"AllowedValues": [
"CentOS 7.9",
"CentOS 7.8",
"CentOS 7.7"
]
}
EOT
}
8.2 添加系统磁盘的输入控件
向配置文件中添加系统盘的输入控件。
系统盘的配置分为两个参数,分别是系统盘类型和系统盘的容量,经过调查ALIYUN::ECS::Disk::SystemDiskCategory属性值可以调用系统盘类型控件,需要注意的是ECS中系统盘可选的类型和ECS实例的规格以及所在可用区有关。因此需要在AssociationPropertyMetadata中配置ZoneId和InstanceType两个属性。
在系统盘的容量的输入ui,可以使用基本输入控件,只需要将配置取值范围设定在40-500即可。
在ecs.tf中继续追加
variable "system_disk_category" {
type = string
description = <<EOT
{
"AssociationProperty": "ALIYUN::ECS::Disk::SystemDiskCategory",
"AssociationPropertyMetadata": {
"ZoneId": "$${zone_id}",
"InstanceType": "$${instance_type}"
},
"Label": {
"en": "System Disk Type",
"zh-cn": "系统盘类型"
}
}
EOT
}
variable "system_disk_size" {
type = number
default = 40
description = <<EOT
{
"Description": {
"zh-cn": "取值范围:40~500,单位:GB。",
"en": "range of values: 40~500, units: GB."
},
"Label": {
"zh-cn": "系统盘大小",
"en": "System Disk Space"
},
"MinValue": "40",
"MaxValue": "500"
}
EOT
}
8.3 添加实例密码输入控件
添加实例的密码输入控件,根据公司的安全规范,密码在输入的时候需要使用不可见控件,同时密码的长度要在8-30个字符之间,同时需要包含大写字母、小写字母、数字和特定符号。发现在variable中直接将sensitive属性设置为true,即可实现密码的不可见输入。同时在description中设置AllowedPattern属性,可以为输入内容配置正则表达式检查规则。
在ecs.tf中继续追加
variable "instance_password" {
type = string
sensitive = true
description = <<EOT
{
"Label": {
"en": "Instance Password",
"zh-cn": "登录密码"
},
"ConstraintDescription": {
"en": "only contain Capital letters, lowercase letters, numbers, ()`~!@#$%^&*_-+=|{}[]:;<>,.?/ .",
"zh-cn": "只能包含大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;<>,.?/ 。"
},
"AllowedPattern": "^[a-zA-Z0-9-\\(\\)\\`\\~\\!\\@\\#\\$\\%\\^\\&\\*\\_\\-\\+\\=\\|\\{\\}\\[\\]\\:\\;\\<\\>\\,\\.\\?\\/]*$",
"MinLength": 8,
"MaxLength": 30
}
EOT
}
8.4 添加ECS资源
输入控件添加完毕之后,最后就是要添加resource资源了。在resource中,alicloud_instance表示ECS资源类型。需要注意的是alicloud_instance中的image_id属性用来表示ECS系统盘的镜像ID,为了能将var.ecs_image输入框的内容转换成镜像ID,使用了两个?:三项操作符嵌套。
在ecs.tf中继续追加
resource "alicloud_instance" "instance" {
availability_zone = var.zone_id
security_groups = [alicloud_security_group.ros_sg.id]
instance_type = var.instance_type
system_disk_size = var.system_disk_size
system_disk_category = var.system_disk_category
image_id = var.ecs_image == "CentOS 7.9" ? "centos_7_9_x64_20G_alibase_20230613.vhd" : (var.ecs_image == "CentOS 7.8" ? "centos_7_8_x64_20G_alibase_20230613.vhd" : "centos_7_7_x64_20G_alibase_20230613.vhd") vswitch_id = alicloud_vswitch.ros_vsw.id
password = var.instance_password
instance_charge_type = "PostPaid"
}
8.5 完成模板的编辑、配置和资源创建
ecs.tf文件编辑完成后,最后对.metadata文件进行编辑,添加输入参数排序和标题。
.metadata文件
{
"ALIYUN::ROS::Interface": {
"ParameterGroups": [
{
"Parameters": [
"whether_create_vpc",
"vpc_id",
"vpc_name",
"vpc_cidr"
],
"Label": {
"default": {
"zh-cn": "VPC属性配置"
}
}
},
{
"Parameters": [
"vswitch_name",
"zone_id",
"create_vswitch_cidr",
"exist_vswitch_cidr"
],
"Label": {
"default": {
"zh-cn": "交换机属性配置"
}
}
},
{
"Parameters": [
"security_group_name",
"security_group_rules"
],
"Label": {
"default": {
"zh-cn": "安全组属性配置"
}
}
},
{
"Parameters": [
"instance_type",
"ecs_image",
"system_disk_category",
"system_disk_size",
"instance_password"
],
"Label": {
"default": {
"zh-cn": "ECS属性配置"
}
}
}
]
}
}
模板编辑完成后,我们点击下一步按钮,在配置模板参数界面,出现了ECS属性配置的段落,在模板参数界面中输入参数之后,我们首先确保账户中有足够的费用,然后点击确认修改按钮。等待一段时间后资源栈的状态变为更新成功,点击资源选项卡,发现资源中出现了类型为alicloud_instance的ECS资源,点击ECS资源的资源ID,会跳转到ECS控制台。在控制台中,可以看到ECS的基本信息和模板一致。
9. 部署LAMP生产环境
一般的LAMP环境包括如下的条件:
➢ LAMP环境分为数据库和PHP运行环境两个ECS集群
➢ 两个集群都使用CentOS操作系统
➢ 数据库集群只包含一台ECS
➢ 数据库集群的ECS安装mariadb,并使用固定IP地址
➢ PHP运行环境集群可以包含2-5台ECS
➢ PHP集群的ECS中安装Apache,并在Apache中安装php插件
➢ 为PHP集群提供一个CLB作为对外访问入口
9.1 资源准备
在创建LAMP环境之前,先把之前创建的ECS实例释放。
只要从Terraform中删除相关资源的resource段落就可以释放相应的资源。
同时由于要在数据库集群中部署mariadb,所以需要开放3306端口,可以对资源栈进行编辑,更改已经创建的资源栈。
打开ecs.tf文件,删除resource "alicloud_instance" "instance"段落。
点击下一步之后进入配置模板参数页面,在这里我们点击+新建按钮,为安全组添加新的规则,开放3306端口
点击确认更改后,等待资源更新成功后。点击资源选项卡。会发现之前创建ECS资源已经消失,同时新的安全组规则已经创建
进入ECS控制台。会发现ECS实例已经被释放。
9.2 添加数据库集群的资源
添加数据库集群的相关资源,为了简化ECS的配置模板,可以复用ecs.tf中的配置,让LAMP环境中的所有的ECS都采用相同的规格,系统盘的系统密码配置。数据库集群只需要一台ECS服务器,但是需要指定ECS的IP地址和初始密码。于是决定为数据库集群添加两个输入控件,分别是服务器地址和初始化密码。
更改已经创建的资源栈
进入选择模板页面,点击目录列表右侧的+,然后点击创建文件
在弹出的对话框中,输入db.tf,然后点击确认按钮
variable "db_service_ip" {
type = string
default = ""
description = <<EOT
{
"Label": {
"en": "DB Service IP",
"zh-cn": "DB服务器IP"
}
}
EOT
}
variable "db_password" {
type = string
sensitive = true
description = <<EOT
{
"Label": {
"en": "DB Password",
"zh-cn": "数据库密码"
},
"ConstraintDescription": {
"en": "only contain Capital letters, lowercase letters, numbers, ()`~!@#$%^&*_-+=|{}[]:;<>,.?/ .",
"zh-cn": "只能包含大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;<>,.?/ 。"
},
"AllowedPattern": "^[a-zA-Z0-9-\\(\\)\\`\\~\\!\\@\\#\\$\\%\\^\\&\\*\\_\\-\\+\\=\\|\\{\\}\\[\\]\\:\\;\\<\\>\\,\\.\\?\\/]*$",
"MinLength": 8,
"MaxLength": 30
}
EOT
}
resource "alicloud_instance" "db_instance" {
availability_zone = var.zone_id
security_groups = [alicloud_security_group.ros_sg.id]
instance_type = var.instance_type
system_disk_size = var.system_disk_size
system_disk_category = var.system_disk_category
image_id = var.ecs_image == "CentOS 7.9" ? "centos_7_9_x64_20G_alibase_20230613.vhd" : (var.ecs_image == "CentOS 7.8" ? "centos_7_8_x64_20G_alibase_20230613.vhd" : "centos_7_7_x64_20G_alibase_20230613.vhd") vswitch_id = alicloud_vswitch.ros_vsw.id
vswitch_id = alicloud_vswitch.ros_vsw.id
password = var.instance_password
internet_charge_type = "PayByTraffic"
internet_max_bandwidth_out = 30
instance_charge_type = "PostPaid"
private_ip = var.db_service_ip
user_data = format(file("${path.cwd}/db_userdata.sh"), var.db_password)
}
添加数据库服务器的初始化脚本。在初始化脚本中,要将用户设置的密码添加到脚本中。terraform中提供了format函数,通过%s占位符来表示密码的位置。然后通过format函数可以将参数添加到字符串中。同时由于数据库的初始化脚本内容过长,如果放在tf文件里,会非常难以阅读,因此将初始化脚本放在一个单独的文件db_userdata.sh中。然后使用file函数来读取文件的内容。
进入选择模板页面,点击目录列表右侧的+,然后点击创建文件
输入db_userdata.sh,然后点击确认按钮
#!/bin/bash
dbuser="DBUser"
dbpassword="%s"
dbname="DB"
dbrootpassword="DBRootPassword"
yum -y install mariadb mariadb-server
systemctl start mariadb.service
systemctl enable mariadb.service
mysqladmin -u root password "$dbrootpassword"
$(mysql $dbname -u root --password="$dbrootpassword" >/dev/null 2>&1 </dev/null); (( $? != 0 ))
echo CREATE DATABASE $dbname \; > /tmp/setup.mysql
echo GRANT ALL ON $dbname.* TO "$dbuser"@"localhost" IDENTIFIED BY "'$dbpassword'" \; >> /tmp/setup.mysql
mysql -u root --password="$dbrootpassword" < /tmp/setup.mysql
$(mysql $dbname -u root --password="$dbrootpassword" >/dev/null 2>&1 </dev/null); (( $? != 0 ))
9.3 添加PHP集群相关资源
创建PHP集群的ECS。和数据库集群不同的是时,PHP集群中可以包含多个ECS。因此为PHP集群添加了一个number类型的输入控件表示集群中ECS的数量。编写PHP集群的初始化脚本。在初始化脚本中,同样需要用%s表示数据库的连接地址和初始化密码。然后再在使用format进行初始化。
进入选择模板页面,点击目录列表右侧的+,然后点击创建文件php.tf
variable "php_service_count" {
type = number
default = 2
description = <<EOT
{
"Label": {
"en": "PHP Service Count",
"zh-cn": "PHP服务器数量"
},
"MinValue": 2,
"MaxValue": 5
}
EOT
}
resource "alicloud_instance" "php_instance" {
availability_zone = var.zone_id
security_groups = [alicloud_security_group.ros_sg.id]
instance_type = var.instance_type
system_disk_size = var.system_disk_size
system_disk_category = var.system_disk_category
image_id = var.ecs_image == "CentOS 7.9" ? "centos_7_9_x64_20G_alibase_20230613.vhd" : (var.ecs_image == "CentOS 7.8" ? "centos_7_8_x64_20G_alibase_20230613.vhd" : "centos_7_7_x64_20G_alibase_20230613.vhd") vswitch_id = alicloud_vswitch.ros_vsw.id
vswitch_id = alicloud_vswitch.ros_vsw.id
password = var.instance_password
internet_charge_type = "PayByTraffic"
internet_max_bandwidth_out = 30
instance_charge_type = "PostPaid"
private_ip = var.db_service_ip
user_data = format(file("${path.cwd}/php_userdata.sh"), var.db_service_ip, var.db_password)
}
创建php_userdata.sh
#!/bin/bash
# 注意!我们需要用数据库ECS地址的代替mysqlhost的值。
mysqlhost="%s"
dbuser="DBUser"
dbpassword="%s"
export HOME=/root
export HOSTNAME=`hostname`
systemctl stop firewalld.service
systemctl disable firewalld.service
sed -i 's/^SELINUX=/# SELINUX=/' /etc/selinux/config
sed -i '/# SELINUX=/a SELINUX=disabled' /etc/selinux/config
setenforce 0
yum -y install httpd httpd-manual mod_ssl mod_perl mod_auth_mysql
yum install yum-priorities -y
yum -y install php-fpm
systemctl start php-fpm.service
systemctl enable php-fpm.service
yum -y install php php-mysql php-gd libjpeg* php-ldap php-odbc php-pear php-xml php-xmlrpc php-mbstring php-bcmath php-mhash php-mcrypt
MDSRING=`find / -name mbstring.so`
echo extension=$MDSRING >> /etc/php.ini
systemctl start httpd.service
cd /root
systemctl restart php-fpm.service
echo \<?php > /var/www/html/test.php
echo \$conn=mysql_connect\("'$mysqlhost'", "'$dbuser'", "'$dbpassword'"\)\; >> /var/www/html/test.php
echo if \(\$conn\){ >> /var/www/html/test.php
echo echo \"LAMP platform connect to mysql is successful\!\"\; >> /var/www/html/test.php
echo }else{ >> /var/www/html/test.php
echo echo \"LAMP platform connect to mysql is failed\!\"\; >> /var/www/html/test.php
echo } >> /var/www/html/test.php
echo \?\> >> /var/www/html/test.php
.metadata文件加入DB集群和PHP集群的输入控件信息
{
"ALIYUN::ROS::Interface": {
"ParameterGroups": [
{
"Parameters": [
"whether_create_vpc",
"vpc_id",
"vpc_name",
"vpc_cidr"
],
"Label": {
"default": {
"zh-cn": "VPC属性配置"
}
}
},
{
"Parameters": [
"vswitch_name",
"zone_id",
"create_vswitch_cidr",
"exist_vswitch_cidr"
],
"Label": {
"default": {
"zh-cn": "交换机属性配置"
}
}
},
{
"Parameters": [
"security_group_name",
"security_group_rules"
],
"Label": {
"default": {
"zh-cn": "安全组属性配置"
}
}
},
{
"Parameters": [
"db_service_ip",
"db_password"
],
"Label": {
"default": {
"zh-cn": "DB集群属性配置"
}
}
},
{
"Parameters": [
"php_service_count"
],
"Label": {
"default": {
"zh-cn": "PHP集群属性配置"
}
}
}
]
}
}
9.4 添加CLB的相关资源
添加负载均衡相关的资源,在使用负载均衡的时候,一般会涉及到三个概念,负载均衡实例,负载均衡监听和负载均衡服务器组。其中负载均衡实例主要用于统一管理所需要的资源,监听和服务器组都是负载均衡实例下面的资源。负载均衡监听负责管理用户访问负载均衡时的端口和协议,负载均衡服务器组则用来管理后端具体相应网络请求的ECS实例。
Terraform中和负载均衡相关的四个资源,分别是alicloud_slb_load_balancer,alicloud_slb_listener,alicloud_slb_server_group,alicloud_slb_server_group_server_attachment。其中前三个资源对应着负载均衡的实例,监听和服务器组,而第四个资源alicloud_slb_server_group_server_attachment则是用来负责将ECS服务器真正的添加到服务器组中。该资源除了包含port端口和weight权重之外,还包含了count属性,通过这个属性配合alicloud_instance资源中的count,我们可以一次性将alicloud_instance资源创建的多个ECS绑定到负载均衡服务器组中。
由于CLB负载均衡资源的作用是用于为PHP集群提供服务,因此可以将这些资源添加到PHP.tf中。同时为了能验证CLB创建的正确性,使用output将CLB的地址进行了输出。
PHP.tf文件,并在文件中追加如下内容
resource "alicloud_slb_load_balancer" "ros_clb" {
vswitch_id = alicloud_vswitch.ros_vsw.id
load_balancer_spec = "slb.s2.small"
internet_charge_type = "paybytraffic"
address_type = "internet"
}
resource "alicloud_slb_listener" "ros_clb_listener" {
load_balancer_id = alicloud_slb_load_balancer.ros_clb.id
backend_port = 80
frontend_port = 80
protocol = "tcp"
bandwidth = 10
}
resource "alicloud_slb_server_group" "ros_clb_group" {
load_balancer_id = alicloud_slb_load_balancer.ros_clb.id
}
resource "alicloud_slb_server_group_server_attachment" "ros_clb_group_att" {
count = var.php_service_count
server_group_id = alicloud_slb_server_group.ros_clb_group.id
server_id = alicloud_instance.instance[count.index].id
port = 80
weight = 100
}
output "clb_ip"{
value = alicloud_slb_load_balancer.address
description = <<EOT
{
"Description": {
"en": "CLB Address",
"zh-cn": "负载均衡IP地址"
}
}
EOT
}
全部资源添加完毕后,我们按下下一步。进入配置模板参数页面。在配置了相关属性了之后,点击确认修改。等待资源创建完毕。在output中找到创建的CLB实例的IP地址,在浏览器中访问该地址的/test.php路径。会看到PHP环境可以成功的连接到DB集群,同时CLB统一访问入口也已经生效。
10. Summary
资源编排(ROS)可以帮助用户通过IaC的方式创建云上资源,通过这种方法可以将资源创建和编排规范化,文档化,便于审计和复用。
● 资源栈是ROS最基本管理单元。资源栈中又包含了模板文件和云上资源
● 模板文件可以使用ROS模板或Terraform模板。运行模板文件可以创建相应的云上资源
● 资源(Resources)用于描述资源栈中每个资源的属性和资源之间的依赖关系
● 变量(variable)可以为模板提供用户输入参数的界面
● 输出(Outputs)是在调用查询资源栈接口时返回的值
●更改集可以将编写和执行操作分开,使用新模板更新资源栈前,通过更改集查看即将产生的变更(比如新增了哪些资源、删除了哪些资源、对哪些资源的特定属性做了修改),确认无误后再执行,更好的保障更新能够顺利安全的进行。
● 如何通过ROS的Terraform模板创建专有网络VPC,交换机等基础网络环境,以及安全组和安全组规则配置。
● 如何通过ROS的Terraform模板创建ECS,并创建一个带有CLB的LAMP生产环境集群。

浙公网安备 33010602011771号