O Alibaba Cloud Resource Orchestration Service (ROS) usa um modelo do Terraform para criar automaticamente uma infraestrutura de rede em cloud completa e uma instância ECS, além de instalar e iniciar o NGINX na instância em uma única operação. Esta solução é ideal para cenários que exigem a construção rápida e repetitiva de ambientes de servidor web.
Visão geral da solução
Esta solução executa um modelo do Terraform por meio do Alibaba Cloud Resource Orchestration Service (ROS) para criar automaticamente uma infraestrutura de rede em cloud completa e uma instância ECS em uma única operação, instalando e iniciando o service NGINX na instância ECS automaticamente. Ela se aplica a cenários que exigem a construção rápida e recorrente de ambientes de servidor web.
Cenário de negócios
Crie uma instância ECS em uma VPC da Alibaba Cloud, configure um grupo de segurança que permita acesso via Secure Shell (SSH) na porta 22 e acesso HTTP/HTTPS nas portas 80 a 443, anexe um disco de dados e use um script de inicialização user_data para instalar e iniciar o service NGINX automaticamente.
Recursos criados pelo modelo
|
Nº |
Recurso |
Tipo de recurso Terraform |
Descrição |
|
1 |
VPC |
|
Fornece um ambiente de rede isolado |
|
2 |
|
|
Aloca uma sub-rede dentro da VPC e especifica a zona |
|
3 |
Grupo de segurança |
|
Controla as regras de acesso à rede da instância ECS |
|
4 |
Regra de grupo de segurança (SSH) |
|
Permite acesso de entrada na porta 22 |
|
5 |
Regra de grupo de segurança (Web) |
|
Permite acesso de entrada nas portas 80 a 443 |
|
6 |
Regra de grupo de segurança (saída) |
|
Permite todo o tráfego de saída |
|
7 |
Instância ECS |
|
Nó de computação que executa o NGINX, com um disco de dados |
Resultados após a implantação
Após a execução bem-sucedida do modelo, você obtém os seguintes recursos:
Ambiente de rede VPC completo, incluindo a VPC, o
vSwitch, o grupo de segurança e as regras de grupo de segurançaInstância ECS com um disco de dados de 100 GB e um endereço IP público atribuído
Service NGINX instalado e iniciado automaticamente na instância ECS
Acesso à página padrão do NGINX pelo endereço IP público retornado na saída
Diagrama de arquitetura

Pré-requisitos
Antes de usar este modelo, certifique-se de que as seguintes condições sejam atendidas:
Permissões da conta: A conta Alibaba Cloud possui permissões para criar recursos ECS e VPC.
Conhecimento básico: Familiaridade com a sintaxe e a estrutura básicas dos modelos do Terraform. Para mais informações, consulte Structure of Terraform templates.
Visualize tipos de recursos: Consulte os detalhes das propriedades de recursos do Terraform no Índice de recursos do Terraform Provider alicloud. Cada propriedade é marcada como
OptionalouRequired.
Tutorial de criação de modelos
Este tutorial demonstra o processo de criação de modelos do Terraform em duas etapas:
Etapa 1 (básica): Usa valores de parâmetros fixos e foca nas definições e dependências de recursos do Terraform.
Etapa 2 (avançada): Adiciona filtragem dinâmica de parâmetros com
AssociationProperty, agrupamento de parâmetros e restrições de parâmetros para melhorar a experiência no console.
Etapa 1: Modelo básico — defina recursos e suas dependências
Passo 1: Defina os recursos básicos de rede
Os recursos básicos de rede incluem alicloud_vpc, alicloud_vswitch e alicloud_security_group, que servem de base para todos os outros recursos. O Terraform estabelece dependências automaticamente por meio de referências de propriedades entre recursos.
# ============================================================
# Basic network-layer resources: VPC → VSwitch → SecurityGroup
# ============================================================
resource "alicloud_vpc" "vpc" {
cidr_block = "192.168.0.0/16" # VPC CIDR block, which holds up to 65,534 private IP addresses
}
resource "alicloud_vswitch" "vsw" {
vpc_id = alicloud_vpc.vpc.id # References the VPC ID. Terraform establishes the dependency automatically
cidr_block = "192.168.0.0/24" # Subnet CIDR block, which must be a subset of the VPC CIDR block
zone_id = "cn-beijing-h" # This scenario uses zone H in the China (Beijing) region as an example
}
resource "alicloud_security_group" "security_group" {
name = "sg-for-nginx" # Security group name
vpc_id = alicloud_vpc.vpc.id # Binds the security group to the same VPC
}
Pontos principais:
-
Dependência implícita versus dependência explícita (depends_on):
A propriedade
vpc_iddealicloud_vswitch.vswreferenciaalicloud_vpc.vpc.id. O Terraform reconhece essa dependência automaticamente e garante que a VPC seja criada antes dovSwitch.O uso de
depends_on(dependência explícita) é necessário apenas quando dois recursos não possuem referência direta entre si, mas a lógica de negócios exige uma ordem específica.
Passo 2: Defina as regras do grupo de segurança
O recurso alicloud_security_group_rule controla o tráfego de rede de entrada e saída da instância ECS. Este cenário requer que a porta de conexão remota SSH e as portas de acesso web estejam abertas.
# ============================================================
# Security group rules: inbound SSH + inbound Web + all outbound traffic allowed
# ============================================================
# Inbound rule 1: opens SSH port 22 (for remote management)
resource "alicloud_security_group_rule" "allow_ssh" {
security_group_id = alicloud_security_group.security_group.id # Associates the rule with the security group
type = "ingress" # Inbound direction
cidr_ip = "0.0.0.0/0" # Allows access from all IP addresses (restrict the IP range in production environments)
policy = "accept" # Accept policy
ip_protocol = "tcp" # TCP protocol
port_range = "22/22" # Port range: port 22 only
priority = 1 # Priority: 1 (highest)
}
# Inbound rule 2: opens web ports 80 to 443 (HTTP/HTTPS access to NGINX)
resource "alicloud_security_group_rule" "allow_web" {
security_group_id = alicloud_security_group.security_group.id
type = "ingress"
cidr_ip = "0.0.0.0/0"
policy = "accept"
ip_protocol = "tcp"
port_range = "80/443" # Port range: 80 to 443
priority = 1
}
# Outbound rule: allows all outbound traffic (the ECS instance can access external networks such as yum repositories)
resource "alicloud_security_group_rule" "allow_egress" {
security_group_id = alicloud_security_group.security_group.id
type = "egress" # Outbound direction
cidr_ip = "0.0.0.0/0"
policy = "accept"
ip_protocol = "tcp"
port_range = "1/65535" # All ports
priority = 1
}
Pontos principais:
O formato de intervalo de portas no Terraform é
"porta inicial/porta final". Por exemplo,"80/443"indica todas as portas de 80 a 443, e uma única porta é escrita como"22/22".Em ambientes de produção, restrinja o
cidr_ipda regra SSH aos endereços IP corporativos para evitar varreduras maliciosas.Para abrir outra porta, como a 8080, adicione outro recurso
alicloud_security_group_rule.
Passo 3: Defina a instância ECS e o script de inicialização
O recurso alicloud_instance é o nó de computação que executa o NGINX. A propriedade user_data especifica o script de inicialização executado na primeira inicialização.
Script de inicialização user_data (user-data.sh):
#!/bin/bash -v
# ---- Format and mount the data disk ----
cat >> /root/InitDataDisk.sh << "EOF"
#!/bin/bash
echo "p
n
p
w
" | fdisk -u /dev/vdb
EOF
/bin/bash /root/InitDataDisk.sh
rm -f /root/InitDataDisk.sh
mkfs -t ext4 /dev/vdb1
cp /etc/fstab /etc/fstab.bak
mkdir /disk1
echo `blkid /dev/vdb1 | awk '{print $2}' | sed 's/\"//g'` /disk1 ext4 defaults 0 0 >> /etc/fstab
mount -a
# ---- Install and start NGINX ----
yum install -y nginx
/usr/sbin/nginx
Definição do recurso de instância ECS:
# ============================================================
# Compute layer: ECS instance (data disk + NGINX initialization)
# ============================================================
resource "alicloud_instance" "instance" {
availability_zone = "cn-beijing-h"
security_groups = [alicloud_security_group.security_group.id] # Binds security groups in array format
host_name = "app-for-nginx"
instance_type = "ecs.c6e.large" # Instance type (compute optimized, 2 vCPUs and 4 GiB of memory)
system_disk_size = 100 # System disk size
system_disk_category = "cloud_essd" # System disk type
image_id = "centos_7_9_x64_20G_alibase_20210318.vhd" # CentOS 7.9 image
vswitch_id = alicloud_vswitch.vsw.id # Binds the instance to the vSwitch
password = "<YourPassword>" # Replace with the actual password at deployment (must contain uppercase and lowercase letters, digits, and special characters)
internet_charge_type = "PayByTraffic" # Pay-by-traffic billing for the public bandwidth
internet_max_bandwidth_out = 30 # 30 Mbit/s outbound public bandwidth
instance_charge_type = "PostPaid" # Billing type (pay-as-you-go or subscription)
user_data = file("${path.cwd}/user-data.sh") # Loads the initialization script
data_disks { # Data disk configuration
size = 100 # Data disk size
category = "cloud_essd" # Data disk type
}
}
# Outputs the NGINX access URL
output "nginx_ip" {
value = "http://${alicloud_instance.instance.public_ip}:80"
}
Pontos principais: Métodos de referência de variáveis do Terraform
|
Método de referência |
Sintaxe |
Descrição |
|
Referência de variável |
|
Recupera o valor de um parâmetro definido em um bloco |
|
Referência de propriedade de recurso |
|
Recupera uma propriedade de saída de outro recurso, como |
|
Carregamento de arquivo |
|
Carrega o conteúdo de um arquivo no diretório de trabalho atual |
Extensões e variantes do modelo:
O
user_datacarrega um arquivo de script externo usando a funçãofile(), facilitando a manutenção e a reutilização do script.A configuração
internet_max_bandwidth_out = 30define uma largura de banda de saída de 30 Mbit/s, e a instância ECS obtém um endereço IP público automaticamente.O bloco
data_diskspode adicionar vários discos de dados ou ser omitido caso nenhum disco de dados seja anexado.Após o modelo criar os recursos, as informações de
outputpodem consultar diretamente as propriedades internas desses recursos. Assim, defina um blocooutputpara retornar o endereço de acesso público da instância ECS, que corresponde ao endereço de acesso do service NGINX.
Modelo básico completo
O modelo do Terraform possui a seguinte estrutura.
Conteúdo do arquivo main.tf:
resource "alicloud_vpc" "vpc" {
cidr_block = "192.168.0.0/16"
}
resource "alicloud_vswitch" "vsw" {
vpc_id = alicloud_vpc.vpc.id
cidr_block = "192.168.0.0/24"
zone_id = "cn-beijing-h"
}
# Basic security group configuration
resource "alicloud_security_group" "security_group" {
name = "sg-for-nginx"
description = "nginx scg"
vpc_id = alicloud_vpc.vpc.id
}
# Inbound rule 1: SSH
resource "alicloud_security_group_rule" "allow_ssh" {
security_group_id = alicloud_security_group.security_group.id
type = "ingress"
cidr_ip = "0.0.0.0/0"
policy = "accept"
ip_protocol = "tcp"
port_range = "22/22"
priority = 1
}
# Inbound rule 2: Web
resource "alicloud_security_group_rule" "allow_web" {
security_group_id = alicloud_security_group.security_group.id
type = "ingress"
cidr_ip = "0.0.0.0/0"
policy = "accept"
ip_protocol = "tcp"
port_range = "80/443"
priority = 1
}
# Outbound rule: all traffic allowed
resource "alicloud_security_group_rule" "allow_egress" {
security_group_id = alicloud_security_group.security_group.id
type = "egress"
cidr_ip = "0.0.0.0/0"
policy = "accept"
ip_protocol = "tcp"
port_range = "1/65535"
priority = 1
}
# Basic instance configuration
resource "alicloud_instance" "instance" {
availability_zone = "cn-beijing-h"
security_groups = [alicloud_security_group.security_group.id]
host_name = "app-for-nginx"
instance_type = "ecs.c6e.large"
system_disk_size = 100
system_disk_category = "cloud_essd"
image_id = "centos_7_9_x64_20G_alibase_20210318.vhd"
vswitch_id = alicloud_vswitch.vsw.id
password = "<YourPassword>" # Replace with the actual password at deployment
internet_charge_type = "PayByTraffic"
internet_max_bandwidth_out = 30
instance_charge_type = "PostPaid"
user_data = file("${path.cwd}/user-data.sh")
data_disks {
size = 100
category = "cloud_essd"
}
}
# Returns the NGINX access URL
output "nginx_ip" {
value = "http://${alicloud_instance.instance.public_ip}:80"
}
Conteúdo do arquivo user-data.sh:
#!/bin/bash -v
# Mount the disk to /disk1
cat >> /root/InitDataDisk.sh << "EOF"
#!/bin/bash
echo "p
n
p
w
" | fdisk -u /dev/vdb
EOF
/bin/bash /root/InitDataDisk.sh
rm -f /root/InitDataDisk.sh
mkfs -t ext4 /dev/vdb1
cp /etc/fstab /etc/fstab.bak
mkdir /disk1
echo `blkid /dev/vdb1 | awk '{print $2}' | sed 's/\"//g'` /disk1 ext4 defaults 0 0 >> /etc/fstab
mount -a
# Install NGINX
yum install -y nginx
# Start NGINX
/usr/sbin/nginx
O modelo a seguir é a versão completa e pronta para uso, combinando os três passos anteriores. Para implantar um modelo do Terraform usando o ROS, encapsule o modelo no formato ROS Workspace.
Modelo completo no formato ROS Workspace:
ROSTemplateFormatVersion: '2015-09-01'
Transform: Aliyun::Terraform-v1.2
Workspace:
main.tf: |-
resource "alicloud_vpc" "vpc" {
cidr_block = "192.168.0.0/16"
}
resource "alicloud_vswitch" "vsw" {
vpc_id = alicloud_vpc.vpc.id
cidr_block = "192.168.0.0/24"
zone_id = "cn-beijing-h"
}
# Basic security group configuration
resource "alicloud_security_group" "security_group" {
name = "sg-for-nginx"
description = "nginx scg"
vpc_id = alicloud_vpc.vpc.id
}
# Inbound rule 1: SSH
resource "alicloud_security_group_rule" "allow_ssh" {
security_group_id = alicloud_security_group.security_group.id
type = "ingress"
cidr_ip = "0.0.0.0/0"
policy = "accept"
ip_protocol = "tcp"
port_range = "22/22"
priority = 1
}
# Inbound rule 2: Web
resource "alicloud_security_group_rule" "allow_web" {
security_group_id = alicloud_security_group.security_group.id
type = "ingress"
cidr_ip = "0.0.0.0/0"
policy = "accept"
ip_protocol = "tcp"
port_range = "80/443"
priority = 1
}
# Outbound rule: all traffic allowed
resource "alicloud_security_group_rule" "allow_egress" {
security_group_id = alicloud_security_group.security_group.id
type = "egress"
cidr_ip = "0.0.0.0/0"
policy = "accept"
ip_protocol = "tcp"
port_range = "1/65535"
priority = 1
}
# Basic instance configuration
resource "alicloud_instance" "instance" {
availability_zone = "cn-beijing-h"
security_groups = [alicloud_security_group.security_group.id]
host_name = "app-for-nginx"
instance_type = "ecs.c6e.large"
system_disk_size = 100
system_disk_category = "cloud_essd"
image_id = "centos_7_9_x64_20G_alibase_20210318.vhd"
vswitch_id = alicloud_vswitch.vsw.id
password = "<YourPassword>"
internet_charge_type = "PayByTraffic"
internet_max_bandwidth_out = 30
instance_charge_type = "PostPaid"
user_data = file("${path.cwd}/user-data.sh")
data_disks {
size = 100
category = "cloud_essd"
}
}
# Returns the NGINX access URL
output "nginx_ip" {
value = "http://${alicloud_instance.instance.public_ip}:80"
}
user-data.sh: |-
#!/bin/bash -v
# Mount the disk to /disk1
cat >> /root/InitDataDisk.sh << "EOF"
#!/bin/bash
echo "p
n
p
w
" | fdisk -u /dev/vdb
EOF
/bin/bash /root/InitDataDisk.sh
rm -f /root/InitDataDisk.sh
mkfs -t ext4 /dev/vdb1
cp /etc/fstab /etc/fstab.bak
mkdir /disk1
echo `blkid /dev/vdb1 | awk '{print $2}' | sed 's/\"//g'` /disk1 ext4 defaults 0 0 >> /etc/fstab
mount -a
# Install NGINX
yum install -y nginx
# Start NGINX
/usr/sbin/nginx
Etapa 2: Modelo avançado — parametrização e configuração dinâmica
O modelo básico não define blocos variable nem as configurações de filtragem dinâmica, como description e default, suportadas por variable. Ao usar o modelo básico no console ROS, os parâmetros não são vinculados entre si, permitindo a seleção de combinações inválidas, como um tipo de instância indisponível na zona selecionada.
As otimizações a seguir melhoram significativamente a experiência no console e a reutilização do modelo:
Filtragem dinâmica de parâmetros (AssociationProperty): Configure
AssociationPropertyem formato JSON nadescriptionde umavariablepara que o console filtre as opções disponíveis automaticamente.Visibilidade condicional (Visible.Condition): Mostre ou oculte dinamicamente os parâmetros de duração da assinatura com base no tipo de faturamento.
Agrupamento de parâmetros (arquivo .metadata): Configure ParameterGroups no arquivo .metadata para que o console exiba os parâmetros em grupos lógicos.
Restrições de parâmetros (ResourcesForParameterConstraints): Defina restrições de recursos no arquivo .metadata para garantir que as combinações de parâmetros sejam válidas.
Gráfico de dependência de parâmetros
pay_type (billing type)
├── pay_period_unit (period unit, displayed only for PrePaid)
└── pay_period (subscription duration, displayed only for PrePaid)
zone_id (zone, the core filter parameter)
├── instance_type (instance type, filtered by zone + billing type)
│ ├── system_disk_category (system disk type, filtered by zone + instance type)
│ └── data_disk_category (data disk type, filtered by zone + instance type)
Abordagem principal: Selecione primeiro a zona e o tipo de faturamento da ECS e, em seguida, filtre os tipos de instância ECS correspondentes (se o faturamento por assinatura for selecionado, também será necessário escolher a unidade de período e a duração da assinatura). Por fim, filtre os tipos de disco com base na zona e no tipo de instância ECS. Cada etapa mostra apenas as opções disponíveis sob as condições atuais, evitando combinações inválidas.
Como funciona o AssociationProperty
1. zone_id — zona (parâmetro base)
Defina AssociationProperty como ALIYUN::ECS::ZoneId para listar todas as zonas na região atual para seleção. Exemplo:
variable "zone_id" {
type = string
description = <<EOT
{
# AssociationProperty specifies the resource property type that the parameter is associated with
# The ROS console lists the available values based on this configuration
"AssociationProperty": "ALIYUN::ECS::ZoneId",
"Label": {
"zh-cn": "VSwitch Availability Zone",
"en": "VSwitch Availability Zone"
},
"Description": {
"zh-cn": "Select availability zone for subsequent parameter filtering",
"en": "Select availability zone for resource filtering"
}
}
EOT
}
Este parâmetro serve como base de filtragem para todos os parâmetros subsequentes. Após selecionar uma zona, as listas de opções dos outros parâmetros são atualizadas conforme necessário.
2. pay_type — tipo de pagamento da ECS
Defina o parâmetro pay_type e use AssociationPropertyMetadata.Visible.Condition para alternar os parâmetros dinamicamente com base em uma condição: os parâmetros de duração da assinatura aparecem apenas quando o tipo de faturamento por assinatura (PrePaid) é selecionado. Exemplo:
variable "pay_type" {
type = string
default = "PostPaid"
description = <<EOT
{
"Label": { "en": "ECS Instance Charge Type", "zh-cn": "Payment Type" },
"AllowedValues": ["PostPaid", "PrePaid"],
"AssociationProperty": "ChargeType"
}
EOT
}
variable "pay_period_unit" {
type = string
default = "Month"
description = <<EOT
{
"Label": { "zh-cn": "Purchase Period Unit" },
"AllowedValues": ["Month", "Year"],
"AssociationProperty": "PayPeriodUnit",
"AssociationPropertyMetadata": {
"Visible": {
"Condition": {
"Fn::Not": {
"Fn::Equals": ["$${pay_type}", "PostPaid"]
}
}
}
}
}
EOT
}
variable "pay_period" {
type = number
default = 1
description = <<EOT
{
"Label": { "en": "Period", "zh-cn": "Purchase Period" },
"AllowedValues": [1, 2, 3, 4, 5, 6, 7, 8, 9],
"AssociationProperty": "PayPeriod",
"AssociationPropertyMetadata": {
"Visible": {
"Condition": {
"Fn::Not": { "Fn::Equals": ["$${pay_type}", "PostPaid"] }
}
}
}
}
EOT
}
Quando o tipo de faturamento conforme o uso (PostPaid) é selecionado no console ROS, os parâmetros de duração da assinatura são ocultados automaticamente. Esses parâmetros aparecem somente quando o tipo de faturamento por assinatura (PrePaid) é escolhido.
3. instance_type — tipo de instância ECS
Defina AssociationProperty como ALIYUN::ECS::Instance::InstanceType para listar os tipos de instância ECS disponíveis e associe ${zone_id} e ${pay_type} para filtrar os tipos de instância disponíveis na zona especificada para o tipo de faturamento indicado. Exemplo:
variable "instance_type" {
type = string
description = <<EOT
{
"Label": { "zh-cn": "Instance Type", "en": "Instance Type" },
"AssociationProperty": "ALIYUN::ECS::Instance::InstanceType",
"AssociationPropertyMetadata": {
"ZoneId": "$${zone_id}",
"InstanceChargeType": "$${pay_type}"
}
}
EOT
}
O estoque de tipos de instância varia por zona. Sem filtragem, pode-se selecionar um tipo de instância indisponível na zona, causando falha na criação.
4. system_disk_category — tipo de disco do sistema
Defina AssociationProperty como ALIYUN::ECS::Disk::SystemDiskCategory para listar os tipos de disco do sistema disponíveis e associe ${zone_id} e ${instance_type} para filtrar os tipos de disco do sistema ECS suportados pela zona e pelo tipo de instância ECS especificados. Exemplo:
variable "system_disk_category" {
type = string
description = <<EOT
{
"Label": { "zh-cn": "System Disk Type", "en": "System Disk Type" },
"Description": {
"zh-cn": "Options: cloud_efficiency, cloud_ssd, cloud_essd",
"en": "Options: cloud_efficiency, cloud_ssd, cloud_essd"
},
"AssociationProperty": "ALIYUN::ECS::Disk::SystemDiskCategory",
"AssociationPropertyMetadata": {
"ZoneId": "$${zone_id}",
"InstanceType": "$${instance_type}"
}
}
EOT
}
A disponibilidade de um tipo de disco do sistema depende tanto da zona (algumas zonas não suportam ESSD) quanto do tipo de instância (alguns tipos de instância suportam apenas tipos específicos de disco).
5. data_disk_category — tipo de disco de dados
Defina AssociationProperty como ALIYUN::ECS::Disk::DataDiskCategory para listar os tipos de disco de dados disponíveis. O mecanismo de filtragem é idêntico ao do tipo de disco do sistema: ${zone_id} e ${instance_type} são associados para filtrar os tipos de disco de dados suportados tanto pela zona atual quanto pelo tipo de instância. Exemplo:
variable "data_disk_category" {
type = string
description = <<EOT
{
"Label": { "zh-cn": "Data Disk Type", "en": "Data Disk Type" },
"Description": {
"zh-cn": "Options: cloud_efficiency, cloud_ssd, cloud_essd",
"en": "Options: cloud_efficiency, cloud_ssd, cloud_essd"
},
"AssociationProperty": "ALIYUN::ECS::Disk::DataDiskCategory",
"AssociationPropertyMetadata": {
"ZoneId": "$${zone_id}",
"InstanceType": "$${instance_type}"
}
}
EOT
}
Modelo de filtragem dinâmica de parâmetros
variable "zone_id" {
type = string
description = <<EOT
{
"AssociationProperty": "ALIYUN::ECS::ZoneId",
"Label": {
"zh-cn": "VSwitch Availability Zone",
"en": "VSwitch Availability Zone"
},
"Description": {
"zh-cn": "Select availability zone for subsequent parameter filtering",
"en": "Select availability zone for resource filtering"
}
}
EOT
}
variable "pay_type" {
type = string
default = "PostPaid"
description = <<EOT
{
"Label": { "en": "ECS Instance Charge Type", "zh-cn": "Payment Type" },
"AllowedValues": ["PostPaid", "PrePaid"],
"AssociationProperty": "ChargeType"
}
EOT
}
variable "pay_period_unit" {
type = string
default = "Month"
description = <<EOT
{
"Label": { "zh-cn": "Purchase Period Unit" },
"AllowedValues": ["Month", "Year"],
"AssociationProperty": "PayPeriodUnit",
"AssociationPropertyMetadata": {
"Visible": {
"Condition": {
"Fn::Not": {
"Fn::Equals": ["$${pay_type}", "PostPaid"]
}
}
}
}
}
EOT
}
variable "pay_period" {
type = number
default = 1
description = <<EOT
{
"Label": { "en": "Period", "zh-cn": "Purchase Period" },
"AllowedValues": [1, 2, 3, 4, 5, 6, 7, 8, 9],
"AssociationProperty": "PayPeriod",
"AssociationPropertyMetadata": {
"Visible": {
"Condition": {
"Fn::Not": { "Fn::Equals": ["$${pay_type}", "PostPaid"] }
}
}
}
}
EOT
}
variable "instance_type" {
type = string
description = <<EOT
{
"Label": { "zh-cn": "Instance Type", "en": "Instance Type" },
"AssociationProperty": "ALIYUN::ECS::Instance::InstanceType",
"AssociationPropertyMetadata": {
"ZoneId": "$${zone_id}",
"InstanceChargeType": "$${pay_type}"
}
}
EOT
}
variable "system_disk_category" {
type = string
description = <<EOT
{
"Label": { "zh-cn": "System Disk Type", "en": "System Disk Type" },
"Description": {
"zh-cn": "Options: cloud_efficiency, cloud_ssd, cloud_essd",
"en": "Options: cloud_efficiency, cloud_ssd, cloud_essd"
},
"AssociationProperty": "ALIYUN::ECS::Disk::SystemDiskCategory",
"AssociationPropertyMetadata": {
"ZoneId": "$${zone_id}",
"InstanceType": "$${instance_type}"
}
}
EOT
}
variable "data_disk_category" {
type = string
description = <<EOT
{
"Label": { "zh-cn": "Data Disk Type", "en": "Data Disk Type" },
"Description": {
"zh-cn": "Options: cloud_efficiency, cloud_ssd, cloud_essd",
"en": "Options: cloud_efficiency, cloud_ssd, cloud_essd"
},
"AssociationProperty": "ALIYUN::ECS::Disk::DataDiskCategory",
"AssociationPropertyMetadata": {
"ZoneId": "$${zone_id}",
"InstanceType": "$${instance_type}"
}
}
EOT
}
Como funciona a filtragem em cascata do AssociationProperty**
Ao criar uma pilha no console ROS, os parâmetros são selecionados em uma sequência em cascata:
Selecione uma zona (
zone_id).Escolha um tipo de faturamento ECS (
pay_type).A lista suspensa de tipos de instância ECS é atualizada automaticamente, mostrando apenas os tipos de instância que suportam o tipo de faturamento selecionado e estão disponíveis na zona escolhida.
-
Após selecionar um tipo de instância ECS, as listas suspensas de tipo de disco do sistema e tipo de disco de dados são atualizadas automaticamente, exibindo apenas os tipos de disco suportados tanto pela zona selecionada quanto pelo tipo de instância escolhido.
Dessa forma, cada etapa apresenta apenas as opções viáveis nas condições atuais, prevenindo falhas de implantação causadas por combinações inválidas de parâmetros desde o início.
Agrupamento de parâmetros de metadados
A configuração ALIYUN::ROS::Interface em Metadata divide os parâmetros em vários grupos lógicos, cada um com um título. O console exibe os parâmetros por grupo, o que melhora significativamente a experiência de especificação.
Estrutura de sintaxe de metadados
Crie um arquivo .metadata na aba Terraform e configure o agrupamento de parâmetros (ParameterGroups) e as restrições de parâmetros (ResourcesForParameterConstraints):
{
"ALIYUN::ROS::Interface": {
"ResourcesForParameterConstraints": {
"resource": {
"Type": "ALIYUN::ECS::Instance",
"Properties": {
}
}
},
"ParameterGroups": [
{
"Parameters": ["parameter_name_1", "parameter_name_2", "parameter_name_3"],
"Label": { "default": { "zh-cn": "XXXX Configuration", "en": "Configuration" } }
}
]
}
}
Regras de sintaxe de metadados:
Tanto
ParametersquantoLabelsão obrigatórios.Os nomes de parâmetros listados em
Parametersdevem ser exatamente iguais aos nomes dos parâmetrosvariable.Um parâmetro não incluído em nenhum grupo é exibido na área sem grupo no console.
Design de agrupamento de parâmetros para este cenário
Este modelo divide os 12 parâmetros em 3 grupos por tipo de recurso:
┌─────────────────────────────────────────────────────┐
│ Network configuration │
│ ├── VSwitch zone (zone_id) │
│ ├── VPC CIDR block (vpc_cidr_block) │
│ └── VSwitch CIDR block (vswitch_cidr_block) │
├─────────────────────────────────────────────────────┤
│ Payment configuration │
│ ├── ECS billing type (pay_type) │
│ ├── ECS subscription period unit (pay_period_unit) │
│ └── ECS subscription duration (pay_period) │
├─────────────────────────────────────────────────────┤
│ ECS instance configuration │
│ ├── ECS instance type (instance_type) │
│ ├── System disk type (system_disk_category) │
│ ├── System disk size (system_disk_size) │
│ ├── Data disk type (data_disk_category) │
│ ├── Data disk size (data_disk_size) │
│ └── ECS instance password (instance_password) │
└─────────────────────────────────────────────────────┘
Configuração do arquivo .metadata
Crie um arquivo .metadata na aba Terraform e configure o agrupamento de parâmetros e as restrições de parâmetros:
{
"ALIYUN::ROS::Interface": {
"ResourcesForParameterConstraints": {
"instance": {
"Type": "ALIYUN::ECS::Instance",
"Properties": {
"InstanceType": { "Ref": "instance_type" },
"ImageId": "centos_7_9_x64_20G_alibase_20210318.vhd",
"ZoneId": { "Ref": "zone_id" },
"SystemDiskCategory": { "Ref": "system_disk_category" },
"SystemDiskSize": { "Ref": "system_disk_size" },
"DataDiskCategory": { "Ref": "data_disk_category" },
"DataDiskSize": { "Ref": "data_disk_size" }
}
}
},
"ParameterGroups": [
{
"Parameters": ["vpc_cidr_block", "zone_id", "vswitch_cidr_block"],
"Label": { "default": { "zh-cn": "Network Configuration", "en": "Network Configuration" } }
},
{
"Parameters": ["pay_type", "pay_period_unit", "pay_period"],
"Label": { "default": { "zh-cn": "Payment Configuration", "en": "Payment Configuration" } }
},
{
"Parameters": ["instance_type", "system_disk_category", "system_disk_size", "data_disk_category", "data_disk_size", "instance_password"],
"Label": { "default": { "zh-cn": "ECS Instance Configuration", "en": "ECS Instance Configuration" } }
}
]
}
}
Pontos principais:
Um recurso virtual
ALIYUN::ECS::Instanceé definido para associar os parâmetros do modelo às propriedades do recurso ECS.O console ROS valida a eficácia da combinação de parâmetros com base nessa definição, verificando, por exemplo, se a zona especificada suporta o tipo de instância e os tipos de disco selecionados.
A sintaxe
Refreferencia o nome da variável do Terraform sem o prefixovar..
Modelo avançado completo
O modelo a seguir é a versão avançada completa, encapsulada no formato ROS Workspace, pronta para implantação direta no console ROS:
ROSTemplateFormatVersion: '2015-09-01'
Transform: Aliyun::Terraform-v1.2
Workspace:
.metadata: |-
{
"ALIYUN::ROS::Interface": {
"ResourcesForParameterConstraints": {
"instance": {
"Type": "ALIYUN::ECS::Instance",
"Properties": {
"InstanceType": { "Ref": "instance_type" },
"ImageId": "centos_7_9_x64_20G_alibase_20210318.vhd",
"ZoneId": { "Ref": "zone_id" },
"SystemDiskCategory": { "Ref": "system_disk_category" },
"SystemDiskSize": { "Ref": "system_disk_size" },
"DataDiskCategory": { "Ref": "data_disk_category" },
"DataDiskSize": { "Ref": "data_disk_size" }
}
}
},
"ParameterGroups": [
{
"Parameters": ["vpc_cidr_block", "zone_id", "vswitch_cidr_block"],
"Label": { "default": { "zh-cn": "Network Configuration", "en": "Network Configuration" } }
},
{
"Parameters": ["pay_type", "pay_period_unit", "pay_period"],
"Label": { "default": { "zh-cn": "Payment Configuration", "en": "Payment Configuration" } }
},
{
"Parameters": ["instance_type", "system_disk_category", "system_disk_size", "data_disk_category", "data_disk_size", "instance_password"],
"Label": { "default": { "zh-cn": "ECS Instance Configuration", "en": "ECS Instance Configuration" } }
}
]
}
}
main.tf: |-
variable "pay_type" {
type = string
default = "PostPaid"
description = <<EOT
{
"Label": { "en": "ECS Instance Charge Type", "zh-cn": "Payment Type" },
"AllowedValues": ["PostPaid", "PrePaid"],
"AssociationProperty": "ChargeType",
"AssociationPropertyMetadata": { "LocaleKey": "InstanceChargeType" }
}
EOT
}
variable "pay_period_unit" {
type = string
default = "Month"
description = <<EOT
{
"Label": { "en": "Pay Period Unit", "zh-cn": "Purchase Period Unit" },
"AllowedValues": ["Month", "Year"],
"AssociationProperty": "PayPeriodUnit",
"AssociationPropertyMetadata": {
"Visible": {
"Condition": {
"Fn::Not": { "Fn::Equals": ["$${pay_type}", "PostPaid"] }
}
}
}
}
EOT
}
variable "pay_period" {
type = number
default = 1
description = <<EOT
{
"Label": { "en": "Period", "zh-cn": "Purchase Period" },
"AllowedValues": [1, 2, 3, 4, 5, 6, 7, 8, 9],
"AssociationProperty": "PayPeriod",
"AssociationPropertyMetadata": {
"Visible": {
"Condition": {
"Fn::Not": { "Fn::Equals": ["$${pay_type}", "PostPaid"] }
}
}
}
}
EOT
}
variable "zone_id" {
type = string
description = <<EOT
{
"AssociationProperty": "ALIYUN::ECS::ZoneId",
"Label": { "zh-cn": "VSwitch Availability Zone", "en": "VSwitch Availability Zone" },
"Description": {
"zh-cn": "Select availability zone. Instance types and disk categories will be filtered accordingly.",
"en": "Select availability zone. Instance types and disk categories will be filtered accordingly."
}
}
EOT
}
variable "vpc_cidr_block" {
type = string
default = "192.168.0.0/16"
description = <<EOT
{
"Label": { "zh-cn": "VPC CIDR Block", "en": "VPC CIDR Block" },
"Description": {
"zh-cn": "VPC IP range. Recommended: 10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16",
"en": "VPC IP range. Recommended: 10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16"
}
}
EOT
}
variable "vswitch_cidr_block" {
type = string
default = "192.168.0.0/24"
description = <<EOT
{
"Label": { "zh-cn": "VSwitch CIDR Block", "en": "VSwitch CIDR Block" },
"Description": {
"zh-cn": "Must be a subnet of VPC CIDR and not overlap with other VSwitches",
"en": "Must be a subnet of VPC CIDR and not overlap with other VSwitches"
}
}
EOT
}
variable "instance_type" {
type = string
description = <<EOT
{
"Label": { "zh-cn": "Instance Type", "en": "Instance Type" },
"AssociationProperty": "ALIYUN::ECS::Instance::InstanceType",
"AssociationPropertyMetadata": {
"InstanceChargeType": "$${pay_type}",
"ZoneId": "$${zone_id}"
}
}
EOT
}
variable "system_disk_category" {
type = string
description = <<EOT
{
"Label": { "zh-cn": "System Disk Type", "en": "System Disk Type" },
"Description": {
"zh-cn": "Options: cloud_efficiency, cloud_ssd, cloud_essd",
"en": "Options: cloud_efficiency, cloud_ssd, cloud_essd"
},
"AssociationProperty": "ALIYUN::ECS::Disk::SystemDiskCategory",
"AssociationPropertyMetadata": {
"ZoneId": "$${zone_id}",
"InstanceType": "$${instance_type}"
}
}
EOT
}
variable "system_disk_size" {
type = number
default = 40
description = <<EOT
{
"Label": { "zh-cn": "System Disk Size", "en": "System Disk Size" },
"Description": {
"zh-cn": "System disk size, range: 40~500 GB",
"en": "System disk size, range: 40~500 GB"
}
}
EOT
}
variable "data_disk_category" {
type = string
description = <<EOT
{
"Label": { "zh-cn": "Data Disk Type", "en": "Data Disk Type" },
"Description": {
"zh-cn": "Options: cloud_efficiency, cloud_ssd, cloud_essd",
"en": "Options: cloud_efficiency, cloud_ssd, cloud_essd"
},
"AssociationProperty": "ALIYUN::ECS::Disk::DataDiskCategory",
"AssociationPropertyMetadata": {
"ZoneId": "$${zone_id}",
"InstanceType": "$${instance_type}"
}
}
EOT
}
variable "data_disk_size" {
type = number
default = 100
description = <<EOT
{
"Label": { "zh-cn": "Data Disk Space", "en": "Data Disk Space" },
"Description": {
"zh-cn": "ECS data disk size, range: 20~32768 GiB",
"en": "ECS data disk size, range: 20~32768 GiB"
},
"MaxValue": 32768,
"MinValue": 20
}
EOT
}
variable "instance_password" {
type = string
sensitive = true
description = <<EOT
{
"Label": { "zh-cn": "Logon Password", "en": "Instance Password" },
"Description": {
"zh-cn": "Length 8-30, must contain at least 3 of: uppercase, lowercase, digits, special characters",
"en": "Length 8-30, must contain at least 3 of: uppercase, lowercase, digits, special characters"
},
"AssociationProperty": "ALIYUN::ECS::Instance::Password",
"MinLength": 8,
"MaxLength": 30
}
EOT
}
# Default resource names
locals {
production_name = "nginx"
new_scg_name = "sg-for-${local.production_name}"
new_host_name = "app-for-${local.production_name}"
}
resource "alicloud_vpc" "vpc" {
cidr_block = var.vpc_cidr_block
}
resource "alicloud_vswitch" "vsw" {
vpc_id = alicloud_vpc.vpc.id
cidr_block = var.vswitch_cidr_block
zone_id = var.zone_id
}
resource "alicloud_security_group" "security_group" {
name = local.new_scg_name
description = "nginx scg"
vpc_id = alicloud_vpc.vpc.id
}
resource "alicloud_security_group_rule" "allow_ssh" {
security_group_id = alicloud_security_group.security_group.id
type = "ingress"
cidr_ip = "0.0.0.0/0"
policy = "accept"
ip_protocol = "tcp"
port_range = "22/22"
priority = 1
}
resource "alicloud_security_group_rule" "allow_web" {
security_group_id = alicloud_security_group.security_group.id
type = "ingress"
cidr_ip = "0.0.0.0/0"
policy = "accept"
ip_protocol = "tcp"
port_range = "80/443"
priority = 1
}
resource "alicloud_security_group_rule" "allow_egress" {
security_group_id = alicloud_security_group.security_group.id
type = "egress"
cidr_ip = "0.0.0.0/0"
policy = "accept"
ip_protocol = "tcp"
port_range = "1/65535"
priority = 1
}
resource "alicloud_instance" "instance" {
availability_zone = var.zone_id
security_groups = [alicloud_security_group.security_group.id]
host_name = local.new_host_name
instance_type = var.instance_type
system_disk_size = var.system_disk_size
system_disk_category = var.system_disk_category
image_id = "centos_7_9_x64_20G_alibase_20210318.vhd"
vswitch_id = alicloud_vswitch.vsw.id
password = var.instance_password
internet_charge_type = "PayByTraffic"
internet_max_bandwidth_out = 30
instance_charge_type = var.pay_type
period = var.pay_period
period_unit = var.pay_period_unit
user_data = file("${path.cwd}/user-data.sh")
data_disks {
size = var.data_disk_size
category = var.data_disk_category
}
}
output "nginx_ip" {
value = "http://${alicloud_instance.instance.public_ip}:80"
}
user-data.sh: |-
#!/bin/bash -v
# Mount the disk to /disk1
cat >> /root/InitDataDisk.sh << "EOF"
#!/bin/bash
echo "p
n
p
w
" | fdisk -u /dev/vdb
EOF
/bin/bash /root/InitDataDisk.sh
rm -f /root/InitDataDisk.sh
mkfs -t ext4 /dev/vdb1
cp /etc/fstab /etc/fstab.bak
mkdir /disk1
echo `blkid /dev/vdb1 | awk '{print $2}' | sed 's/\"//g'` /disk1 ext4 defaults 0 0 >> /etc/fstab
mount -a
# Install NGINX
yum install -y nginx
# Start NGINX
/usr/sbin/nginx
Referência rápida de sintaxe de modelos do Terraform
|
Sintaxe |
Exemplo |
Finalidade neste modelo |
|
|
|
Referencia um valor de parâmetro de entrada definido por uma variável |
|
|
|
Referencia uma variável local definida em um bloco locals |
|
|
|
Referencia uma propriedade de saída de outro recurso e estabelece a dependência automaticamente |
|
|
file("${path.cwd}/user-data.sh") |
Carrega o conteúdo de um arquivo externo como valor de propriedade |
|
|
|
Interpolação de string, que incorpora um valor de variável em uma string |
|
|
|
String multilinha, usada para incorporar configurações JSON |
|
|
|
Referencia o valor atual de outra variável no JSON da descrição |
Implantação
Parâmetros de implantação
Parâmetros de entrada obrigatórios (devem ser especificados na implantação)
|
Parâmetro |
Tipo |
Descrição |
Restrição |
|
|
String |
Zona do |
Seleção dinâmica no console |
|
|
String |
Tipo de instância ECS |
Filtrado automaticamente por zona |
|
|
String |
Tipo de disco do sistema |
Filtrado automaticamente por zona + tipo de instância |
|
|
String |
Tipo de disco de dados |
Filtrado automaticamente por zona + tipo de instância |
|
|
String |
Senha de login da ECS |
Comprimento de 8 a 30 caracteres, devendo conter letra maiúscula, letra minúscula, dígito e caractere especial |
Parâmetros opcionais (possuem valores padrão, podem ser mantidos inalterados)
|
Parâmetro |
Padrão |
Descrição |
|
|
|
Tipo de faturamento ( |
|
|
Month |
Unidade de duração da assinatura (exibido apenas para |
|
|
1 |
Duração da assinatura (exibido apenas para |
|
|
192.168.0.0/16 |
Intervalo de bloco CIDR da VPC |
|
|
192.168.0.0/24 |
Bloco CIDR da sub-rede do |
|
|
40 |
Tamanho do disco do sistema (GB), valores válidos: 40 a 500 |
|
|
100 |
Tamanho do disco de dados (GiB), valores válidos: 20 a 32768 |
Métodos de implantação
Método 1: Implantar através do console ROS
Acesse o console ROS.
No painel de navegação à esquerda, escolha Templates > My Templates > Create Template.
Para o tipo de modelo, selecione o modelo Terraform.
Crie um arquivo
main.tfe cole o conteúdo do main.tf do modelo avançado.Crie um arquivo user-data.sh e cole o conteúdo do script user-data.sh.
Crie um arquivo
.metadatae cole a configuração JSON do .metadata.Após salve o modelo, acesse Stacks > Create Stack > Select an existing template > My Templates e selecione o modelo.
Especifique os parâmetros de configuração por grupo e clique em Create.
Aguarde até que o status da pilha mude para
CREATE_COMPLETEe visualize a URL de acesso do NGINX na aba Outputs.
Método 2: Implantar através do ROS IaC Code
O IaC Code é um assistente de IA para infraestrutura como código (IaC) voltado para infraestrutura em cloud, capaz de gerar, implantar e gerenciar modelos de infraestrutura por meio de prompts em linguagem natural. Sua arquitetura foi projetada para fluxos de trabalho multicloud. O prompt a seguir serve como referência para esta solução:
# Prompt
Help me create an ECS instance and deploy the NGINX service by using a Terraform template.
Requirements:
1. Create a new VPC and `vSwitch` as the basic network.
2. The security group allows inbound access on port 22 (SSH) and ports 80 to 443 (web access).
3. The ECS instance requires a public IP address, with an outbound bandwidth of 30 Mbit/s.
4. Attach a 100 GB data disk, format it, and mount it to /disk1.
5. Install and start NGINX automatically through a user_data script.
6. zone_id, instance_type, system_disk_category, and data_disk_category all require AssociationProperty dynamic filtering.
7. The billing type parameter requires conditional visibility (the subscription duration is displayed only for subscription billing).
Perguntas frequentes
P1: A implantação falha com a mensagem de erro "The specified InstanceType is not available in the zone"
Causa: O tipo de instância ECS selecionado está indisponível na zona especificada.
Solução:
No modelo avançado, o
AssociationPropertytrabalha em conjunto comResourcesForParameterConstraintspara filtrar automaticamente os tipos de instância disponíveis, prevenindo esse problema.Se você estiver usando o modelo básico, verifique a disponibilidade de estoque do tipo de instância na zona alvo na página de Preços do Elastic Compute Service (ECS).
P2: A instância ECS foi criada, mas a página do NGINX não está acessível
Passos para solução de problemas:
Verifique se a regra do grupo de segurança permite acesso de entrada na porta 80 (verifique a regra
allow_web).Login to the ECS instance e execute
systemctl status nginxpara verificar se o NGINX está funcionando conforme esperado.Confira se o script
user_datafoi executado com sucesso: visualizecat /var/log/messages | grep cloud-init.Verifique se a instância ECS possui um endereço IP público (um IP público é atribuído automaticamente quando
internet_max_bandwidth_out > 0).
P3: O script user_data falhou ao executar e o disco de dados não foi montado
Causa: A imagem pode ser incompatível ou o nome do dispositivo do disco de dados pode não corresponder.
Solução:
O caminho
/dev/vdbusado no scriptuser_dataé o nome de dispositivo padrão do disco de dados. Certifique-se de que o blocodata_disksesteja configurado no modelo.Se você utilizar uma imagem diferente do CentOS, talvez seja necessário ajustar o comando de particionamento (por exemplo, usar
partedem vez defdisk).Caso não seja necessário um disco de dados, remova o bloco
data_diskse a parte de montagem de disco do scriptuser_data.
P4: Como converto um modelo do Terraform para um modelo do ROS?
Método 1: Converter no console
No console ROS, escolha Templates > My Templates > Create Template.
Escreva o modelo primeiro na aba de modelo Terraform.
Mude para a aba de modelo ROS e selecione YAML no canto superior direito. O sistema converte automaticamente o modelo do Terraform para o seguinte formato:
ROSTemplateFormatVersion: '2015-09-01'
Transform: Aliyun::Terraform-v1.2
Workspace:
.metadata: |-
{ ... } # Parameter grouping and constraint configuration
main.tf: |-
... # Terraform template content
user-data.sh: |-
... # Initialization script content
Método 2: Usar o Template Transformer
Execute o comando de conversão:
rostran transform templates/terraform/alicloud/main.tf --target-format json
P5: Como implanto outro service web, como Apache ou Tomcat?
Pontos de modificação:
-
Modifique os comandos de instalação e inicialização no arquivo user-data.sh:
Apache:
yum install -y httpd && systemctl start httpdTomcat: instale o JDK e, em seguida, baixe e inicie o Tomcat.
Ajuste o intervalo de portas da regra do grupo de segurança (por exemplo, o Tomcat usa a porta 8080 por padrão).
Modifique o número da porta na saída.
P6: Como uso uma VPC existente em vez de criar uma nova?
Solução: Altere a VPC e o vSwitch para selecionar recursos existentes:
variable "vpc_id" {
type = string
description = <<EOT
{
"Label": { "zh-cn": "Existing VPC" },
"AssociationProperty": "ALIYUN::ECS::VPC::VPCId"
}
EOT
}
variable "vswitch_id" {
type = string
description = <<EOT
{
"Label": { "zh-cn": "Existing VSwitch" },
"AssociationProperty": "ALIYUN::VPC::VSwitch::VSwitchId",
"AssociationPropertyMetadata": {
"VpcId": "$${vpc_id}"
}
}
EOT
}
Em seguida, altere vswitch_id em alicloud_instance para var.vswitch_id e exclua os blocos de recursos alicloud_vpc e alicloud_vswitch.
Para mais informações sobre problemas de implantação de recursos, consulte FAQ.