基于terraform和课程示例,完成使用terraform管理阿里云资源,包括ecs,vpc等资源
Terraform
8.1 快速入门
8.1.1 云上实验
8.1.1.1 Cloud Shell
版本切换
powershell
# 查看当前 Terraform 版本, 低于 1.2 建议切换新版
shell@Alicloud:~$ terraform version
Terraform v0.12.31
Your version of Terraform is out of date! The latest version
is 1.17.1. You can update by downloading from https://www.terraform.io/downloads.html
powershell
# 查看当前支持的版本
shell@Alicloud:~$ tfenv list
1.9.5
1.5.7
1.3.7
0.13.7
* 0.12.31 (set by /usr/local/tfenv/version)
powershell
# 切换到指定版本
tfenv use 1.9.5
# 确认
shell@Alicloud:~$ terraform version
Terraform v1.9.5
定制 ECS 模板
powershell
mkdir tf-demo && cd tf-demo
cat > main.tf << 'EOF'
provider "alicloud" {
region = var.region
}
# 资源创建的地域
variable "region" {
default = "cn-beijing"
}
# ECS 实例名称
variable "instance_name" {
default = "tf-cloudshell-test"
}
# ECS 实例规格
variable "instance_type" {
default = "ecs.n2.small"
}
# 公网出带宽, 值为 0 时, 表示不开通公网
variable "internet_bandwidth" {
default = 10
}
# ECS 系统镜像
variable "image_id" {
default = "ubuntu_18_04_64_20G_alibase_20190624.vhd"
}
# ECS 系统盘类型
variable "disk_category" {
default = "cloud_efficiency"
}
# ECS 实例登录密码
variable "password" {
default = "TF-test@1234"
}
# 创建 VPC
resource "alicloud_vpc" "vpc" {
vpc_name = "tf_test_foo"
cidr_block = "172.16.0.0/12"
}
# 根据实例规格和云盘类型查询存在资源的可用区
data "alicloud_zones" "default" {
available_disk_category = var.disk_category
available_resource_creation = "VSwitch"
available_instance_type = var.instance_type
}
# 创建 vSwitch
resource "alicloud_vswitch" "vsw" {
vpc_id = alicloud_vpc.vpc.id
cidr_block = "172.16.0.0/21"
zone_id = data.alicloud_zones.default.zones.0.id
}
# 创建安全组
resource "alicloud_security_group" "default" {
security_group_name = "default"
vpc_id = alicloud_vpc.vpc.id
}
# 创建安全组入方向规则
resource "alicloud_security_group_rule" "allow_all_tcp" {
type = "ingress"
ip_protocol = "tcp"
nic_type = "intranet"
policy = "accept"
port_range = "1/65535"
priority = 1
security_group_id = alicloud_security_group.default.id
cidr_ip = "0.0.0.0/0"
}
resource "alicloud_instance" "instance" {
# 绑定安全组
security_groups = alicloud_security_group.default.*.id
instance_type = var.instance_type
system_disk_category = var.disk_category
image_id = var.image_id
instance_name = var.instance_name
# 所在交换机
vswitch_id = alicloud_vswitch.vsw.id
internet_max_bandwidth_out = var.internet_bandwidth
password = var.password
}
EOF
初始化环境
powershell
terraform init
powershell
文件解读
shell@Alicloud:~/tf-demo$ ls -a
main.tf # Terraform 配置文件
.terraform # terraform init 生成的目录, 存储已下载的 Provider 插件
.terraform.lock.hcl # 锁文件, 记录了 Provider 的源和版本
powershell
# 重新初始化
terraform init -force-copy
# 更新方式初始化
terraform init -upgrade
查看资源创建计划
powershell
terraform plan
powershell
符号解读
加号 (+) : 资源旁边的加号意味着 Terraform 将创建该资源, 并展示将要设置的属性
减号斜线加号 (-/+) : 表示 Terraform 将销毁当前资源并重新创建一个新的资源, 而不是就地更新它的属性
波浪号 (~) : 表示 Terraform 将就地更新资源属性
减号 (-) : 表示销毁当前资源实例
powershell
# 将生成的计划保存为文件, 之后可以将文件作为执行计划传递给 terraform apply 来执行
terraform plan -out=tf.tfplan
执行计划
powershell
terraform apply
...
Enter a value: yes # 输入 yes 确认执行
...
# 注意输出的各资源 ID
alicloud_vpc.vpc: Creation complete after 6s [id=vpc-2ze7zzyf0hp1qzr6gfkkm]
...
alicloud_security_group.default: Creation complete after 1s [id=sg-2ze64iasa1glc7tzjd5e]
...
alicloud_vswitch.vsw: Creation complete after 4s [id=vsw-2zeruztpu0viyg1i2k7y3]
...
alicloud_instance.instance: Creation complete after 16s [id=i-2ze0iy1k84ivvoq6vwd3]
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
浏览器查看阿里云 ECS

8.1.1.2 Cloud Shell 命令
查看概览信息
powershell
# 查看资源状态
shell@Alicloud:~/tf-demo$ terraform state list
data.alicloud_zones.default
alicloud_instance.instance
alicloud_security_group.default
alicloud_security_group_rule.allow_all_tcp
alicloud_vpc.vpc
alicloud_vswitch.vsw
# 查看 ECS 实例的详细状态
terraform state show alicloud_instance.instance
# 查看资源依赖关系
terraform graph
查看详细信息
powershell
# 查看 VPC 详情
aliyun vpc DescribeVpcs --VpcId "vpc-2ze7zzyf0hp1qzr6gfkkm" --RegionId cn-beijing
# 查看交换机详情
aliyun vpc DescribeVSwitches --VSwitchId "vsw-2zeruztpu0viyg1i2k7y3" --RegionId cn-beijing
# 查看安全组所有出入站规则
aliyun ecs DescribeSecurityGroupAttribute --SecurityGroupId "sg-2ze64iasa1glc7tzjd5e" --RegionId cn-beijing
# 查看 ECS 实例详情, 确认规格、带宽、镜像等
aliyun ecs DescribeInstances --InstanceIds "['i-2ze0iy1k84ivvoq6vwd3']" --RegionId cn-beijing
获取统计信息脚本
powershell
cat > get_resource_info.sh << 'EOF'
#!/bin/bash
# *************************************
# * 功能: 获取ECS相关的信息
# * 作者:
# * 联系:
# * 版本:
# *************************************
# 核心资源 ID 配置
VPC_ID="vpc-2ze7zzyf0hp1qzr6gfkkm"
VSWITCH_ID="vsw-2zeruztpu0viyg1i2k7y3"
SG_ID="sg-2ze64iasa1glc7tzjd5e"
ECS_ID="i-2ze0iy1k84ivvoq6vwd3"
REGION="cn-beijing"
# 统一处理: 去除所有缩进/空格, 便于精准匹配
get_vpc_info() {
aliyun vpc DescribeVpcs --VpcId $VPC_ID --RegionId $REGION | tr -d '\t '
}
get_vswitch_info() {
aliyun vpc DescribeVSwitches --VSwitchId $VSWITCH_ID --RegionId $REGION | tr -d '\t '
}
get_sg_info() {
aliyun ecs DescribeSecurityGroupAttribute --SecurityGroupId $SG_ID --RegionId $REGION | tr -d '\t '
}
get_ecs_info() {
aliyun ecs DescribeInstances --InstanceIds "[\"$ECS_ID\"]" --RegionId $REGION | tr -d '\t '
}
# 1. 验证 VPC (兼容缩进, 精准提取)
echo -e "【VPC】"
echo "名称: $(get_vpc_info | grep -o '"VpcName":"[^"]*' | cut -d'"' -f4)"
echo "ID: $VPC_ID"
echo "地域: $REGION"
echo "网段: $(get_vpc_info | grep -o '"CidrBlock":"[^"]*' | cut -d'"' -f4)"
echo "状态: $(get_vpc_info | grep -o '"Status":"[^"]*' | cut -d'"' -f4)"
# 2. 验证交换机
echo -e "\n【交换机】"
echo "ID: $VSWITCH_ID"
echo "所属VPC: $(get_vswitch_info | grep -o '"VpcId":"[^"]*' | cut -d'"' -f4)"
echo "地域: $REGION"
echo "可用区: $(get_vswitch_info | grep -o '"ZoneId":"[^"]*' | cut -d'"' -f4)"
echo "网段: $(get_vswitch_info | grep -o '"CidrBlock":"[^"]*' | cut -d'"' -f4)"
echo "状态: $(get_vswitch_info | grep -o '"Status":"[^"]*' | cut -d'"' -f4)"
# 3. 验证安全组规则
echo -e "\n【安全组规则】"
echo "安全组ID: $SG_ID"
echo "地域: $REGION"
echo "协议: $(get_sg_info | grep -o '"IpProtocol":"[^"]*' | cut -d'"' -f4)"
echo "端口范围: $(get_sg_info | grep -o '"PortRange":"[^"]*' | cut -d'"' -f4)"
echo "源IP: $(get_sg_info | grep -o '"SourceCidrIp":"[^"]*' | cut -d'"' -f4)"
echo "策略: $(get_sg_info | grep -o '"Policy":"[^"]*' | cut -d'"' -f4)"
# 4. 验证 ECS 实例 (适配无引号字段)
echo -e "\n【ECS实例】"
echo "名称: $(get_ecs_info | grep -o '"InstanceName":"[^"]*' | cut -d'"' -f4)"
echo "ID: $ECS_ID"
echo "地域: $REGION"
echo "规格: $(get_ecs_info | grep -o '"InstanceType":"[^"]*' | cut -d'"' -f4)"
echo "公网带宽: $(get_ecs_info | grep -o '"InternetMaxBandwidthOut":[0-9]*' | cut -d':' -f2)"
echo "状态: $(get_ecs_info | grep -o '"Status":"[^"]*' | cut -d'"' -f4)"
EOF
powershell
shell@Alicloud:~/tf-demo$ bash get_resource_info.sh
【VPC】
名称: tf_test_foo
ID: vpc-2ze7zzyf0hp1qzr6gfkkm
地域: cn-beijing
网段: 172.16.0.0/12
状态: Available
【交换机】
ID: vsw-2zeruztpu0viyg1i2k7y3
所属VPC: vpc-2ze7zzyf0hp1qzr6gfkkm
地域: cn-beijing
可用区: cn-beijing-g
网段: 172.16.0.0/21
状态: Available
【安全组规则】
安全组ID: sg-2ze64iasa1glc7tzjd5e
地域: cn-beijing
协议: TCP
端口范围: 1/65535
源IP: 0.0.0.0/0
策略: Accept
【ECS实例】
名称: tf-cloudshell-test
ID: i-2ze0iy1k84ivvoq6vwd3
地域: cn-beijing
规格: ecs.n2.small
公网带宽: 10
状态: Running
释放资源
powershell
terraform destroy
...
Enter a value: yes # 输入 yes 确认执行
powershell
# 确认效果
terraform state list
# 输出为空
8.1.1.3 Explorer
Terraform Explorer 是阿里云基于 Terraform 开源版提供的一款在线调试 Terraform 配置文件的工具
访问: https://api.aliyun.com/terraform

创建 VPC
填入参数, 点击发起调试, 相当于 plan
powershell
产品: VPC
资源类型: 默认 (alicloud_vp)
区域: 选择华北6
VPC私网网段: 192.168.0.0/16
VPC描述信息: 使用 Terraform 创建的 VPC

点击执行, 相当于 apply

点击 资源详情 查看详细信息

查看创建的 VPC

释放资源
点击 销毁资源

点击 执行

8.1.2 本地实践
8.1.2.1 认证环境
创建 RAM 账号
使用阿里云主账号或拥有 RAM 管理员权限的账号登录 RAM 访问控制

powershell
用户名: terraform-test
访问方式: 勾选使用永久 AccessKey 访问

powershell
保存 AccessKey
AccessKey ID: LTAI5t6QQZ1z3td7pLC6fSQ5
AccessKey Secret: 3k32ovlkuHW2jX0w0699ZViZxeZEb5

RAM 账号授权

权限策略里的搜索框可以搜索权限, 依次添加 6 个资源权限, 点击 确认新增授权

账号禁用方法
如果不想删除账号, 可以禁用 AccessKey


安装测试工具
安装阿里云CLI工具, 测试 账号权限
powershell
# 下载最新版解压
curl https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz | tar -xzv
# 下载历史版本解压
curl -L https://aliyuncli.alicdn.com/aliyun-cli-linux-3.0.170-amd64.tgz | tar -xzv
mv aliyun /usr/local/bin/
powershell
# 非交互式配置 RAM 账号的
aliyun configure set \
--profile default \
--access-key-id LTAI5t6QQZ1z3td7pLC6fSQ5 \
--access-key-secret 3k32ovlkuHW2jX0w0699ZViZxeZEb5 \
--region cn-hangzhou \
--language zh
powershell
# 交互式配置, 推荐, 避免命令写入历史记录
[root@master1 ~ ]#aliyun configure
Configuring profile 'default' in 'AK' authenticate mode...
Access Key Id [*********************SQ5]: LTAI5t6QQZ1z3td7pLC6fSQ5
Access Key Secret [***************************Eb5]: 3k32ovlkuHW2jX0w0699ZViZxeZEb5
Default Region Id [cn-hangzhou]:
Default Output Format [json]: json (Only support json)
Default Language [zh|en] zh:
Saving profile[default] ...Done.
Configure Done!!!
powershell
# 查看当前配置
aliyun configure list
查看测试
powershell
# 测试 VPC 查询权限
aliyun vpc DescribeVpcs
powershell
# 测试 ECS 查询权限
aliyun ecs DescribeInstances
powershell
# 测试 SLB 查询权限
aliyun slb DescribeLoadBalancers
powershell
# 测试 RDS 查询权限
aliyun rds DescribeDBInstances
powershell
# 测试 EIP 查询权限
aliyun vpc DescribeEipAddresses
EIP 测试
powershell
# 创建测试 EIP
aliyun vpc AllocateEipAddress --RegionId cn-hangzhou --Name terraform-test-eip
powershell
# 确认 EIP 信息
aliyun vpc DescribeEipAddresses
powershell
# 精准查询指定 EIP
aliyun vpc DescribeEipAddresses --AllocationId "eip-bp1l3hc8g0zq946rpesj6"
powershell
# 释放 EIP
aliyun vpc ReleaseEipAddress --AllocationId "eip-bp1l3hc8g0zq946rpesj6"
# 验证
aliyun vpc DescribeEipAddresses --AllocationId "eip-bp1l3hc8g0zq946rpesj6"
8.1.2.2 工具实践
Ubuntu 安装工具
参考网站: https://developer.hashicorp.com/terraform/install
powershell
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(grep -oP '(?<=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
powershell
# 确认效果
[root@ubuntu-240 ~ ]#terraform -version
Terraform v1.17.1
on linux_amd64
基础环境
powershell
terraform init
powershell
# 在当前用户家目录配置镜像加速
cat > ~/.terraformrc << 'EOF'
provider_installation {
network_mirror {
url = "https://mirrors.aliyun.com/terraform/"
# 限制只有阿里云相关 Provider 从国内镜像源下载
include = ["registry.terraform.io/aliyun/alicloud",
"registry.terraform.io/hashicorp/alicloud"
]
}
direct {
# 声明除了阿里云相关 Provider, 其它 Provider 保持原有的下载链路
exclude = ["registry.terraform.io/aliyun/alicloud",
"registry.terraform.io/hashicorp/alicloud"
]
}
}
EOF
powershell
# 如果当前环境已存在 providers, 执行命令使配置生效
terraform init -upgrade
简单实践
提前在乌兰察布区域创建一个 OSS 对象 bucket sswang-magedu-test1
注意 RAM 账号需要拥有 AliyunOSSFullAccess 权限
powershell
# 创建配置文件
mkdir terraform-aliyun && cd terraform-aliyun
cat > main.tf << 'EOF'
terraform {
required_providers {
alicloud = {
source = "aliyun/alicloud"
version = "~> 1.266.0"
}
}
# 没有下面的配置的话, state 文件是保存到本地目录下
backend "oss" {
bucket = "sswang-magedu-test1" # 替换成你已创建的 OSS bucket
key = "terraform.tfstate" # 存到 bucket 里面的文件名
region = "cn-wulanchabu" # 必须与你 bucket 所在地域一致
}
}
# 配置乌兰察布地域, 数据源自动继承该地域
provider "alicloud" {
region = "cn-wulanchabu"
}
# 数据源:仅空块即可, 自动读取 Provider 的地域, 无任何多余参数
data "alicloud_zones" "default" {}
# 创建乌兰察布 VPC
resource "alicloud_vpc" "vpc" {
vpc_name = "tf_wulcb_vpc"
cidr_block = "172.16.0.0/16"
}
# 创建乌兰察布交换机
resource "alicloud_vswitch" "vsw" {
vpc_id = alicloud_vpc.vpc.id
cidr_block = "172.16.0.0/24"
zone_id = data.alicloud_zones.default.zones.0.id
}
EOF
powershell
# 创建环境变量, 存放 RAM 账号信息
cat > terraform_secret << 'EOF'
export ALICLOUD_ACCESS_KEY="LTAI5t6QQZ1z3td7pLC6fSQ5"
export ALICLOUD_SECRET_KEY="3k32ovlkuHW2jX0w0699ZViZxeZEb5"
export ALICLOUD_REGION="cn-wulanchabu"
EOF
source terraform_secret
powershell
# 环境初始化
terraform init
powershell
# 预览
terraform plan
powershell
# 执行计划
terraform apply
...
Enter a value: yes
...
powershell
# 确认效果
terraform state list
# 确认依赖关系
terraform graph
powershell
# 确认 OSS 文件数量
[root@master1 ~ ]#aliyun oss ls oss://sswang-magedu-test1 -e oss-cn-wulanchabu.aliyuncs.com
LastModifiedTime Size(B) StorageClass ETAG ObjectName
2026-10-31 12:45:32 +0800 CST 5629 Standard 7EC1E9968315717101256F011C47E244 oss://sswang-magedu-test1/env:/terraform.tfstate
Object Number is: 1
0.186802(s) elapsed
# 查看文件状态
[root@master1 ~ ]#aliyun oss stat oss://sswang-magedu-test1 -e oss-cn-wulanchabu.aliyuncs.com
Name : sswang-magedu-test1
Location : oss-cn-wulanchabu
CreationDate : 2026-10-31 12:39:22 +0800 CST
ExtranetEndpoint : oss-cn-wulanchabu.aliyuncs.com
IntranetEndpoint : oss-cn-wulanchabu-internal.aliyuncs.com
ACL : private
Owner : 1140913766715991
StorageClass : Standard
RedundancyType : LRS
TransferAcceleration : Disabled
CrossRegionReplication: Disabled
AccessMonitor : Disabled
0.185616(s) elapsed
powershell
# 查看指定区域的 VPC
aliyun vpc DescribeVpcs --RegionId cn-wulanchabu
# 查看 VPC 信息
[root@master1 ~ ]#aliyun vpc DescribeVpcs --RegionId cn-wulanchabu | jq -r '.Vpcs.Vpc[] | "VPC名称:\(.VpcName) | 网段:\(.CidrBlock)"'
VPC名称:tf_wulcb_vpc | 网段:172.16.0.0/16
# 查看交换机信息
[root@master1 ~ ]#aliyun vpc DescribeVSwitches --RegionId cn-wulanchabu | jq -r '.VSwitches.VSwitch[] | "交换机名称:\(.VSwitchName) | 网段:\(.CidrBlock) | 可用区:\(.ZoneId) | 所属VPC:\(.VpcId)"'
交换机名称: | 网段:172.16.0.0/24 | 可用区:cn-wulanchabu-a | 所属VPC:vpc-0jlcos91cqhf213expxeg
powershell
# 移除资源, -auto-approve 跳过确认步骤
terraform destroy -auto-approve
# 确认效果
terraform state list
8.1.3 ECS 案例
8.1.3.1 环境准备
powershell
# 确认环境
[root@ubuntu-240 ~ ]#source terraform_secret
[root@ubuntu-240 ~ ]#env | grep ALICLOUD
ALICLOUD_REGION=cn-wulanchabu
ALICLOUD_SECRET_KEY=3k32ovlkuHW2jX0w0699ZViZxeZEb5
ALICLOUD_ACCESS_KEY=LTAI5t6QQZ1z3td7pLC6fSQ5
powershell
# 创建项目目录
mkdir -p /data/case/ecs && cd /data/case/ecs/
# 配置镜像加速
cat > .terraformrc << 'EOF'
provider_installation {
network_mirror {
url = "https://mirrors.aliyun.com/terraform/"
# 限制只有阿里云相关 Provider 从国内镜像源下载
include = ["registry.terraform.io/aliyun/alicloud",
"registry.terraform.io/hashicorp/alicloud"
]
}
direct {
# 声明除了阿里云相关 Provider, 其它 Provider 保持原有的下载链路
exclude = ["registry.terraform.io/aliyun/alicloud",
"registry.terraform.io/hashicorp/alicloud"
]
}
}
EOF
8.1.3.2 基础环境属性
查询镜像和规格
powershell
# 查询镜像 ID, 默认仅方法第一页 10 条数据
aliyun ecs DescribeImages --RegionId cn-wulanchabu | awk '/ImageId/{print $1,$2}'
# 查询该地域下, 支持 ecs.e-c1m1.large 这个特定实例规格的镜像 ID
aliyun ecs DescribeImages --RegionId cn-wulanchabu --InstanceType ecs.e-c1m1.large | grep "ImageId"
# 查询第一页的 100 个镜像, 过滤出带 ubuntu 的镜像
aliyun ecs DescribeImages --RegionId cn-wulanchabu --PageSize 100 | grep -E 'ImageId' | grep ubuntu
# 循环查询 10 页, 每页 100 个 镜像, 过滤出所有带 ubuntu 的镜像
for p in {1..10};do aliyun ecs DescribeImages --RegionId cn-wulanchabu --PageNumber $p --PageSize 100 | grep ImageId | grep ubuntu; done
# 统计乌兰察布地域下, 总共有多少种可用的实例规格
aliyun ecs DescribeInstanceTypes --RegionId cn-wulanchabu --pager | jq -r '.InstanceTypes.InstanceType[] | .InstanceTypeId' | wc -l
# 确认该地域是否有 ecs.e-c1m1.large 这个规格
aliyun ecs DescribeInstanceTypes --RegionId cn-wulanchabu --pager | jq -r '.InstanceTypes.InstanceType[] | .InstanceTypeId' | grep e-c1m1.large
配置 Provider 和版本
powershell
cat > providers.tf << 'EOF'
# 定义 Terraform 版本和阿里云 Provider
terraform {
required_providers {
alicloud = {
source = "aliyun/alicloud"
version = "~> 1.266.0"
}
}
required_version = ">= 1.5.0"
}
# 阿里云 Provider 配置 (乌兰察布地域)
provider "alicloud" {
region = "cn-wulanchabu"
}
EOF
配置数据源 (可用区和镜像)
powershell
cat > data-sources.tf << 'EOF'
# 数据源1: 获取乌兰察布可用区 (自动继承 Provider 地域)
data "alicloud_zones" "default" {}
# 数据源2: 获取 Ubuntu 24.04 官方镜像
data "alicloud_images" "default" {
name_regex = "^ubuntu_24_04.*"
most_recent = true
owners = "system"
}
# locals 本地变量兜底: 优先用匹配到的镜像 ID, 无结果则硬编码 CLI 查到的 ID
locals {
ubuntu_image_id = data.alicloud_images.default.images != [] ? data.alicloud_images.default.images.0.id : "ubuntu_24_04_x64_20G_alibase_20261016.vhd"
}
EOF
powershell
镜像名字匹配问题
name_regex = "^ubuntu_24_04.*"
这个正则匹配, 有可能匹配到很多个不适合的镜像, 所以会发生报错, 解决问题就是, 匹配的方法更加精准一些, 比如
name_regex = "^ubuntu_24_04_x64_20G"
name_regex = "^ubuntu_24_04_x64_20G_alibase_20261016.vhd"
只要能够通过关键字, 找到配套的镜像名字就可以
8.1.3.3 定制网络配置
powershell
cat > network.tf << 'EOF'
# VPC (专有网络)
resource "alicloud_vpc" "vpc" {
vpc_name = "ecs-practice-vpc"
cidr_block = "172.16.0.0/16"
}
# 交换机 (VSwitch) - 关联 VPC 和可用区
resource "alicloud_vswitch" "vsw" {
vpc_id = alicloud_vpc.vpc.id
cidr_block = "172.16.1.0/24"
zone_id = data.alicloud_zones.default.zones.0.id
}
EOF
8.1.3.4 定制 ECS 安全组
powershell
cat > security-group.tf << 'EOF'
# ECS 安全组 (关联 VPC)
resource "alicloud_security_group" "sg" {
security_group_name = "ecs-practice-sg"
vpc_id = alicloud_vpc.vpc.id
}
# 入站规则1: 开放 22 端口 (SSH 登录)
resource "alicloud_security_group_rule" "sg_ingress_22" {
type = "ingress"
ip_protocol = "tcp"
port_range = "22/22"
cidr_ip = "0.0.0.0/0"
security_group_id = alicloud_security_group.sg.id
}
# 入站规则2: 开放 80 端口 (HTTP)
resource "alicloud_security_group_rule" "sg_ingress_80" {
type = "ingress"
ip_protocol = "tcp"
port_range = "80/80"
cidr_ip = "0.0.0.0/0"
security_group_id = alicloud_security_group.sg.id
}
EOF
8.1.3.5 定制 ECS 配置
powershell
cat > ecs.tf << 'EOF'
# ECS 实例配置
resource "alicloud_instance" "ecs" {
# 基础标识
instance_name = "ecs-practice-instance"
# 硬件配置 (入门级, 按需计费)
instance_type = "ecs.e-c1m1.large"
image_id = data.alicloud_images.default.images.0.id
# 网络配置 (关联交换机和安全组)
vswitch_id = alicloud_vswitch.vsw.id
security_groups = [alicloud_security_group.sg.id]
# 存储配置 (20GB 云盘)
system_disk_category = "cloud_essd_entry"
data_disks {
category = "cloud_essd_entry"
size = 20
}
# 标签 (便于资源管理)
tags = {
Environment = "Practice"
Region = "Wulanchabu"
ManagedBy = "Terraform"
}
}
EOF
8.1.3.6 实例操作
powershell
terraform init
terraform plan
terraform apply -auto-approve
terraform state list

8.1.3.7 更改 ECS 配置
powershell
cat > ecs.tf << 'EOF'
# ECS 实例配置
resource "alicloud_instance" "ecs" {
# 基础标识, 增加 主机名和 内网 IP 配置
instance_name = "my-custom-ecs"
host_name = "ecs-static-ip-host"
private_ip = "172.16.1.100"
# 硬件配置 (入门级, 按需计费)
instance_type = "ecs.e-c1m1.large"
image_id = data.alicloud_images.default.images.0.id
# 网络配置 (关联交换机和安全组)
vswitch_id = alicloud_vswitch.vsw.id
security_groups = [alicloud_security_group.sg.id]
# 存储配置 (20GB 云盘)
system_disk_category = "cloud_essd_entry"
data_disks {
category = "cloud_essd_entry"
size = 20
}
# 标签 (便于资源管理)
tags = {
Environment = "Practice"
Region = "Wulanchabu"
ManagedBy = "Terraform"
}
}
EOF
8.1.3.8 增加 EIP
powershell
cat > eip.tf << 'EOF'
# 创建静态公网 EIP
resource "alicloud_eip_address" "ecs_eip" {
address_name = "ecs-static-eip"
}
# 将 EIP 绑定到 ECS (实现公网静态 IP)
resource "alicloud_eip_association" "eip_bind" {
allocation_id = alicloud_eip_address.ecs_eip.id
instance_id = alicloud_instance.ecs.id
}
EOF
8.1.3.9 定制信息输出
powershell
cat > outputs.tf << 'EOF'
# 输出 ECS 关键信息
output "ecs_instance_name" {
value = alicloud_instance.ecs.instance_name
description = "自定义 ECS 实例名称"
}
output "ecs_host_name" {
value = alicloud_instance.ecs.host_name
description = "ECS 系统主机名"
}
output "ecs_private_ip" {
value = alicloud_instance.ecs.private_ip
description = "定制的静态私有 IP"
}
output "ecs_public_eip" {
value = alicloud_eip_address.ecs_eip.ip_address
description = "绑定的静态公网 EIP"
}
EOF
8.1.3.10 实例更新
powershell
# 重新初始化 (确保资源依赖加载)
terraform init -reconfigure
# 预览变更计划, 确认静态 IP/名称配置
terraform plan
...
+ ecs_host_name = "ecs-static-ip-host"
+ ecs_instance_name = "my-custom-ecs"
+ ecs_private_ip = "172.16.1.100"
+ ecs_public_eip = (known after apply)
...
# 执行创建, 自动创建 ECS + 绑定静态 IP/EIP
terraform apply --auto-approve
# 确认信息输出效果
[root@ubuntu-240 ecs ]#terraform output
ecs_host_name = "ecs-static-ip-host"
ecs_instance_name = "my-custom-ecs"
ecs_private_ip = "172.16.1.100"
ecs_public_eip = "8.130.143.61"
# 其他信息输出
terraform state list
7.1.3.11 实例销毁
powershell
terraform destroy --auto-approve
7.1.3.12 反复 apply 的流程
- 配置文件未改变
- 首次执行创建 ECS, 记录到 State
- 后续执行对比配置文件和实际状态未发生改变, 无任何操作
- 配置文件修改可在线更新参数
- 例如修改实例名称
- Terraform 发现「实例名称」不匹配, 且阿里云支持 ECS 名称在线更新
- 直接更新旧 ECS 的名称, 不删除、不重建
- 配置文件修改不可在线更新参数
- 例如实例规格/镜像
- Terraform 发现「实例规格」不匹配, 且阿里云不支持 ECS 规格在线更新
- 先删除旧 ECS,再创建新 ECS
8.1.4 WEB 服务
8.1.4.1 Flask 应用
powershell
# 部署 python 环境
apt update && apt -y install python3 python3-pip nginx build-essential python3-dev python3.12-venv
powershell
# 创建 python 虚拟环境
cd /data/
python3 -m venv myflaskenv
[root@ubuntu-240 data ]#ls
case myflaskenv
source myflaskenv/bin/activate
powershell
# 安装 Flask 环境
# 方法一: 手工指定源
pip install flask --index-url https://mirrors.aliyun.com/pypi/simple/
# 方法二: 默认源安装
pip config set global.index-url https://pypi.doubanio.com/simple
pip config set install.trusted-host pypi.doubanio.com
pip config list global.index-url='https://pypi.doubanio.com/simple'
pip install flask
powershell
# 创建 flask 项目
mkdir flaskapp && cd flaskapp/
cat > app.py << 'EOF'
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!\n'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=True)
EOF
powershell
# 后台运行项目
nohup python3 app.py >/dev/null 2>&1 &
powershell
# 测试
[root@ubuntu-240 flaskapp ]#ss -ntlp | grep 80
LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("python3",pid=29955,fd=6),("python3",pid=29955,fd=5),("python3",pid=29954,fd=5))
[root@ubuntu-240 flaskapp ]#curl localhost
Hello, World!
powershell
# 还原环境
kill -9 29954
# 退出 python 虚拟环境
deactivate
8.1.4.2 Web 应用
环境准备
powershell
cd /data/case/ && cp -a ecs ecs-2 && cd ecs-2/ && rm -rf .terraform .terraform.lock.hcl
powershell
# 准备测试文件
cat > app.py << 'EOF'
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!\n'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=True)
EOF
更改 ECS 属性
powershell
# 新增变量定义, 用于 SSH 连接
cat >> providers.tf << 'EOF'
# 新增: 定义 ECS 登录密码变量
variable "ecs_password" {
type = string
default = "Admin@123"
description = "ECS 实例 root 用户密码"
}
EOF
powershell
# 在最后一个 } 上方新增 SSH 认证配置
vim ecs.tf
...
# 主机密码
password = var.ecs_password
# user_data 自动启用 root 密码登录
user_data = <<-EOF
#!/bin/bash
# 启用 SSH 密码登录
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config
sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
systemctl restart sshd
EOF
}
powershell
user_data 是阿里云 ECS 的核心参数, 作用是:
ECS 实例第一次启动时, 自动执行 EOF 之间的脚本 (相当于 "开机自执行脚本")
无需手动登录 ECS, 就能完成初始化配置 (比如启用密码登录、安装软件、配置服务等)
部署应用配置
powershell
cat > deploy_app.tf << 'EOF'
# 新增: 部署 Flask 应用的 null_resource
resource "null_resource" "deploy_flask" {
# 触发条件: ECS 实例创建完成 + app.py 文件变更
triggers = {
# 触发条件1: ECS 实例 ID 变化 (比如 ECS 被重建)
ecs_id = alicloud_instance.ecs.id
# 触发条件2: 本地 app.py 文件内容变化 (文件哈希值变化)
script_hash = filesha256("app.py") # 现在 app.py 已存在, 可正常读取
}
# 依赖: 等待 ECS 实例创建完成、EIP 绑定完成
depends_on = [
alicloud_instance.ecs,
alicloud_eip_association.eip_bind
]
# 第一步: 上传 app.py 到 ECS
provisioner "file" {
connection {
type = "ssh"
user = "root"
password = var.ecs_password
host = alicloud_eip_address.ecs_eip.ip_address
port = 22
timeout = "5m" # 增加超时, 避免 ECS SSH 未就绪
}
source = "app.py" # 本地 app.py 路径 (已创建)
destination = "/tmp/app.py" # ECS 目标路径
}
# 第二步: 远程执行部署命令
provisioner "remote-exec" {
connection {
type = "ssh"
user = "root"
password = var.ecs_password
host = alicloud_eip_address.ecs_eip.ip_address
port = 22
timeout = "5m"
}
inline = [
# 更新系统包 (Ubuntu 24.04 默认无 pip)
"apt update -y",
"apt install -y python3-pip python3-flask",
# 安装 Flask (兼容方式)
"pip3 install flask --upgrade",
# 停止旧进程 (避免端口占用)
"pkill -f 'python3 /tmp/app.py' || true",
# 后台运行 Flask, 日志输出到 /tmp/flask.log
"nohup python3 /tmp/app.py > /tmp/flask.log 2>&1 &",
"sleep 3", # 等待应用启动
# 验证 80 端口是否监听
"ss -ntlp | grep :80 || echo 'Flask 应用启动失败, 请查看 /tmp/flask.log'"
]
}
# 用 provisioner 的重试逻辑
provisioner "local-exec" {
command = "echo 'Flask 部署完成, 访问地址: http://${alicloud_eip_address.ecs_eip.ip_address}'"
}
}
EOF
powershell
# 新版推荐 terraform_data 替换 null_resource
resource "terraform_data" "deploy_flask" {
# null_resource 用 triggers
# terraform_data 用 input, 注意格式
input = join(",", [
alicloud_instance.ecs.id,
filesha256("app.py")
])
执行计划
powershell
terraform init
terraform plan
terraform apply --auto-approve
测试效果
powershell
terraform state list
[root@ubuntu-240 ecs-2 ]#terraform output
ecs_host_name = "ecs-static-ip-host"
ecs_instance_name = "my-custom-ecs"
ecs_private_ip = "172.16.1.100"
ecs_public_eip = "8.130.169.240"
powershell
# 访问公网 IP
[root@ubuntu-240 ecs-2 ]#curl http://8.130.169.240
Hello, World!
8.1.4.3 增加 MySQL
环境准备
powershell
cd /data/case/ && cp -a ecs-2 ecs-3 && cd ecs-3/ && rm -rf .terraform .terraform.lock.hcl
powershell
# 修改测试文件
cat > app.py << 'EOF'
from flask import Flask
import pymysql
from pymysql.err import OperationalError
# 初始化 Flask 应用
app = Flask(__name__)
# RDS MySQL 连接配置 (和 Terraform 中的 RDS 配置一致)
MYSQL_CONFIG = {
'host': '172.16.1.202', # RDS 的私有 IP
'port': 3306, # MySQL 默认端口
'user': 'flaskdb', # 用户名
'password': 'flaskdb@123', # 密码
'database': 'flaskdb', # 数据库名
'charset': 'utf8mb4' # 字符集
}
# 测试 MySQL 连接的函数
def test_mysql_connection():
try:
# 建立连接
conn = pymysql.connect(**MYSQL_CONFIG)
# 创建游标
cursor = conn.cursor()
# 执行测试查询
cursor.execute("SELECT VERSION();")
version = cursor.fetchone()[0]
# 关闭连接
cursor.close()
conn.close()
return f"MySQL连接成功!MySQL版本:{version}"
except OperationalError as e:
return f"MySQL连接失败!错误信息:{str(e)}"
except Exception as e:
return f"未知错误:{str(e)}"
# 根路由:保留 Hello World + 输出 MySQL 连接状态
@app.route('/')
def hello_world():
mysql_status = test_mysql_connection()
return f"""
<h1>Hello, World!</h1>
<h3>{mysql_status}</h3>
<p>RDS配置: 主机={MYSQL_CONFIG['host']}, 数据库={MYSQL_CONFIG['database']}</p>
"""
# 启动应用 (监听 80 端口, 允许所有 IP 访问)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=True)
EOF
创建 RDS
powershell
cat > rds.tf << 'EOF'
# RDS MySQL 实例配置 (严格适配 Provider 1.266.0)
resource "alicloud_db_instance" "flask_rds" {
# 基础配置 (必填)
engine = "MySQL"
engine_version = "8.0"
instance_type = "rds.mysql.t1.small"
instance_name = "flask-mysql-db"
zone_id = data.alicloud_zones.default.zones.0.id
vswitch_id = alicloud_vswitch.vsw.id
security_group_ids = [alicloud_security_group.sg.id]
# 存储配置 (必填)
instance_storage = 20
# 网络配置 (必填)
vpc_id = alicloud_vpc.vpc.id
private_ip_address = "172.16.1.202"
# 标签
tags = {
Environment = "Practice"
Region = "Wulanchabu"
ManagedBy = "Terraform"
}
}
# 单独创建数据库账号
resource "alicloud_db_account" "flask_db_account" {
db_instance_id = alicloud_db_instance.flask_rds.id
account_name = "flaskdb"
account_type = "Normal"
account_password = "flaskdb@123"
}
# 单独创建 flaskdb 数据库
resource "alicloud_db_database" "flask_db" {
instance_id = alicloud_db_instance.flask_rds.id
name = "flaskdb"
character_set = "utf8mb4"
}
# 授权账号访问数据库 (适配 1.266.0)
resource "alicloud_db_account_privilege" "flask_privilege" {
instance_id = alicloud_db_instance.flask_rds.id
account_name = alicloud_db_account.flask_db_account.account_name
db_names = [alicloud_db_database.flask_db.name]
privilege = "ReadWrite"
}
EOF
完善安全组
powershell
cat >> security-group.tf << 'EOF'
# 新增: 开放 3306 端口 (MySQL), 仅允许 VPC 内访问
resource "alicloud_security_group_rule" "sg_ingress_3306" {
type = "ingress"
ip_protocol = "tcp"
port_range = "3306/3306"
cidr_ip = "172.16.0.0/16" # 仅允许 VPC 内 IP 访问 (更安全)
security_group_id = alicloud_security_group.sg.id
}
EOF
ECS 完善
powershell
# 在最后一个 EOF 上方新增 MySQL Python 依赖
vim ecs.tf
...
# 安装 MySQL Python 依赖 (关键)
apt update -y
apt install -y python3-pip python3-flask net-tools
pip3 install pymysql cryptography # pymysql 是 Python 连接 MySQL 的核心库
EOF
}
部署应用配置
powershell
cat > deploy_app.tf << 'EOF'
# 新增: 部署 Flask 应用的 null_resource
resource "null_resource" "deploy_flask" {
# 触发条件: ECS 实例创建完成 + app.py 文件变更
triggers = {
ecs_id = alicloud_instance.ecs.id
script_hash = filesha256("app.py") # 现在 app.py 已存在, 可正常读取
}
# 依赖: 等待 ECS 实例创建完成、EIP 绑定完成、数据库已创建
depends_on = [
alicloud_instance.ecs,
alicloud_eip_association.eip_bind, # 注意这个位置的逗号
alicloud_db_database.flask_db # 确保数据库已创建
]
# 第一步: 上传 app.py 到 ECS
provisioner "file" {
connection {
type = "ssh"
user = "root"
password = var.ecs_password
host = alicloud_eip_address.ecs_eip.ip_address
port = 22
timeout = "5m" # 增加超时, 避免 ECS SSH 未就绪
}
source = "app.py" # 本地 app.py 路径 (已创建)
destination = "/tmp/app.py" # ECS 目标路径
}
# 第二步: 远程执行部署命令
provisioner "remote-exec" {
connection {
type = "ssh"
user = "root"
password = var.ecs_password
host = alicloud_eip_address.ecs_eip.ip_address
port = 22
timeout = "5m"
}
inline = [
# 更新系统包 (Ubuntu 24.04 默认无 pip)
"apt update -y",
# 更改如下三条命令
"apt install -y python3-pip python3-venv python3-flask",
"python3 -m venv /opt/flask_venv",
"/opt/flask_venv/bin/pip install flask pymysql --upgrade",
"pkill -f 'python3 /tmp/app.py' || true",
# 更改下面一条命令: 后台运行 Flask, 日志输出到 /tmp/flask.log
"nohup /opt/flask_venv/bin/python3 /tmp/app.py > /tmp/flask.log 2>&1 &",
"sleep 3", # 等待应用启动
# 验证 80 端口是否监听
"ss -ntlp | grep :80 || echo 'Flask 应用启动失败, 请查看 /tmp/flask.log'"
]
}
# 用 provisioner 的重试逻辑
provisioner "local-exec" {
command = "echo 'Flask 部署完成, 访问地址: http://${alicloud_eip_address.ecs_eip.ip_address}'"
}
}
EOF
定制输出
powershell
cat > outputs.tf << 'EOF'
# 输出 ECS 关键信息
output "ecs_instance_name" {
value = alicloud_instance.ecs.instance_name
description = "自定义 ECS 实例名称"
}
output "ecs_host_name" {
value = alicloud_instance.ecs.host_name
description = "ECS 系统主机名"
}
output "ecs_private_ip" {
value = alicloud_instance.ecs.private_ip
description = "定制的静态私有 IP"
}
output "ecs_public_eip" {
value = alicloud_eip_address.ecs_eip.ip_address
description = "绑定的静态公网 EIP"
}
# 输出 Flask 应用访问地址
output "flask_app_url" {
value = format("http://%s", alicloud_eip_address.ecs_eip.ip_address)
description = "Flask 应用访问地址 (80 端口)"
}
# 新增:输出 RDS MySQL 连接信息
output "rds_mysql_info" {
value = {
host = alicloud_db_instance.flask_rds.private_ip_address
port = 3306
username = "flaskdb"
password = "flaskdb@123"
database = "flaskdb"
}
description = "RDS MySQL 连接信息 (VPC内访问)"
}
EOF
执行计划
powershell
terraform init -reconfigure
terraform plan
terraform apply --auto-approve
测试效果
powershell
terraform state list
[root@ubuntu-240 ecs-3 ]#terraform output
ecs_host_name = "ecs-static-ip-host"
ecs_instance_name = "my-custom-ecs"
ecs_private_ip = "172.16.1.100"
ecs_public_eip = "8.130.169.240"
flask_app_url = "http://8.130.169.240"
rds_mysql_info = {
"database" = "flaskdb"
"host" = "172.16.1.202"
"password" = "flaskdb@123"
"port" = 3306
"username" = "flaskdb"
}
# 访问公网 IP
[root@ubuntu-240 ecs-3 ]#curl http://8.130.169.240
<h1>Hello, World!</h1>
<h3>MySQL连接成功!MySQL版本:8.0.36</h3>
<p>RDS配置: 主机=172.16.1.202, 数据库=flaskdb</p>
8.1.4.4 增加 SLB
改造思路
powershell
私网 CLB 的核心是:
- 改造 flask 程序: 将 flask 应用所在 ECS 的 ip 地址信息显示出来
- 创建两个 ECS
基于现有 alicloud_instance.ecs 配置, 新增第二个 ECS 资源即可
第一个 ECS ip 设为 172.16.1.100, 第二个 ECS ip 设为 172.16.1.200
- 创建内网 CLB
指定 CLB 类型为 intranet (私网), 关联现有 VPC 和 VSwitch
配置 80 端口监听, 对外和对内端口均设为 80
启用 TCP/HTTP 健康检查, 检查路径为 / (Flask 根路由)
- 将两个 ECS 加入 CLB 后端
基于 CLB 实例创建后端服务器组, 类型为 默认服务器组 或 自定义服务器
- 调整 EIP 绑定
解除 EIP 与第一个 ECS 的关联
将 EIP 重新绑定到私网 CLB
环境准备
powershell
cd /data/case && cp -a ecs-3 ecs-4 && cd ecs-4
powershell
# 修改测试文件
cat > app.py << 'EOF'
from flask import Flask
import pymysql
from pymysql.err import OperationalError
# 新增: 如下模块
import socket
import fcntl
import struct
# 新增: 获取本地主机 ip 地址
def get_local_ip():
target_nics = ['eth0', 'ens33']
for nic in target_nics:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
nic_bytes = nic.encode('utf-8')[:15]
ifreq = struct.pack('16sH14s', nic_bytes, socket.AF_INET, b'\x00'*14)
res = fcntl.ioctl(s.fileno(), 0x8915, ifreq)
ip = socket.inet_ntoa(res[20:24])
s.close()
if not ip.startswith('127.'):
return ip
except Exception as e:
continue
# 初始化 Flask 应用
app = Flask(__name__)
# RDS MySQL 连接配置 (和 Terraform 中的 RDS 配置一致)
MYSQL_CONFIG = {
'host': '172.16.1.202', # RDS的私有 IP
'port': 3306, # MySQL 默认端口
'user': 'flaskdb', # 用户名
'password': 'flaskdb@123', # 密码
'database': 'flaskdb', # 数据库名
'charset': 'utf8mb4' # 字符集
}
# 测试 MySQL 连接的函数
def test_mysql_connection():
try:
# 建立连接
conn = pymysql.connect(**MYSQL_CONFIG)
# 创建游标
cursor = conn.cursor()
# 执行测试查询
cursor.execute("SELECT VERSION();")
version = cursor.fetchone()[0]
# 关闭连接
cursor.close()
conn.close()
return f"MySQL 连接成功!MySQL 版本: {version}"
except OperationalError as e:
return f"MySQL 连接失败!错误信息: {str(e)}"
except Exception as e:
return f"未知错误: {str(e)}"
# 根路由: 保留 Hello World + 输出 MySQL 连接状态
@app.route('/')
def hello_world():
mysql_status = test_mysql_connection()
local_ip = get_local_ip() # 新增: 本地 ip 获取逻辑
return f"""
<h1>Hello, World!</h1>
<h3>本地ip: {local_ip}</h3>
<h3>{mysql_status}</h3>
<p>RDS配置: 主机={MYSQL_CONFIG['host']}, 数据库={MYSQL_CONFIG['database']}</p>
"""
# 启动应用 (监听 80 端口, 允许所有 IP 访问)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=True)
EOF
调整 ECS 配置
powershell
cat > ecs.tf << 'EOF'
# 第一个 ECS 实例
resource "alicloud_instance" "ecs_1" {
# 自定义 ECS 实例名称、主机名、静态 IP (差异化命名)
instance_name = "my-custom-ecs-1"
host_name = "ecs-static-ip-host-1"
private_ip = "172.16.1.100"
# 基础标识 (基本不变)
instance_type = "ecs.e-c1m1.large"
image_id = data.alicloud_images.default.images.0.id
vswitch_id = alicloud_vswitch.vsw.id
security_groups = [alicloud_security_group.sg.id]
# 增加主机密码
password = var.ecs_password
# user_data自动启用密码登录
user_data = <<-eof
#!/bin/bash
# 启用 SSH 密码登录
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config
sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
systemctl restart sshd
# 安装 MySQL Python 依赖 (关键)
apt update -y
apt install -y python3-pip python3-venv python3-flask net-tools
pip3 install pymysql cryptography
# ========== 解码生成 app.py ==========
echo "ZnJvbSBmbGFzayBpbXBvcnQgRmxhc2sKaW1wb3J0IHB5bXlzcWwKZnJvbSBweW15c3FsLmVyciBpbXBvcnQgT3BlcmF0aW9uYWxFcnJvcgoKIyDmlrDlop7vvJrlpoLkuIvmqKHlnZcKaW1wb3J0IHNvY2tldAppbXBvcnQgZmNudGwKaW1wb3J0IHN0cnVjdAoKIyDmlrDlop7vvJrojrflj5bmnKzlnLDkuLvmnLppcOWcsOWdgApkZWYgZ2V0X2xvY2FsX2lwKCk6CiAgICB0YXJnZXRfbmljcyA9IFsnZXRoMCcsICdlbnMzMyddCiAgICBmb3IgbmljIGluIHRhcmdldF9uaWNzOgogICAgICAgIHRyeToKICAgICAgICAgICAgcyA9IHNvY2tldC5zb2NrZXQoc29ja2V0LkFGX0lORVQsIHNvY2tldC5TT0NLX0RHUkFNKQogICAgICAgICAgICBuaWNfYnl0ZXMgPSBuaWMuZW5jb2RlKCd1dGYtOCcpWzoxNV0KICAgICAgICAgICAgaWZyZXEgPSBzdHJ1Y3QucGFjaygnMTZzSDE0cycsIG5pY19ieXRlcywgc29ja2V0LkFGX0lORVQsIGInXHgwMCcqMTQpCiAgICAgICAgICAgIHJlcyA9IGZjbnRsLmlvY3RsKHMuZmlsZW5vKCksIDB4ODkxNSwgaWZyZXEpCiAgICAgICAgICAgIGlwID0gc29ja2V0LmluZXRfbnRvYShyZXNbMjA6MjRdKQogICAgICAgICAgICBzLmNsb3NlKCkKICAgICAgICAgICAgaWYgbm90IGlwLnN0YXJ0c3dpdGgoJzEyNy4nKToKICAgICAgICAgICAgICAgIHJldHVybiBpcAogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgY29udGludWUKCgojIOWIneWni+WMlkZsYXNr5bqU55SoCmFwcCA9IEZsYXNrKF9fbmFtZV9fKQoKIyBSRFMgTXlTUUzov57mjqXphY3nva7vvIjlkoxUZXJyYWZvcm3kuK3nmoRSRFPphY3nva7kuIDoh7TvvIkKTVlTUUxfQ09ORklHID0gewogICAgJ2hvc3QnOiAnMTcyLjE2LjEuMjAyJywgICAgICAgIyBSRFPnmoTnp4HmnIlJUAogICAgJ3BvcnQnOiAzMzA2LCAgICAgICAgICAgICAgICAgIyBNeVNRTOm7mOiupOerr+WPowogICAgJ3VzZXInOiAnZmxhc2tkYicsICAgICAgICAgICAgIyDnlKjmiLflkI0KICAgICdwYXNzd29yZCc6ICdmbGFza2RiQDEyMycsICAgICMg5a+G56CBCiAgICAnZGF0YWJhc2UnOiAnZmxhc2tkYicsICAgICAgICAjIOaVsOaNruW6k+WQjQogICAgJ2NoYXJzZXQnOiAndXRmOG1iNCcgICAgICAgICAgIyDlrZfnrKbpm4YKfQoKIyDmtYvor5VNeVNRTOi/nuaOpeeahOWHveaVsApkZWYgdGVzdF9teXNxbF9jb25uZWN0aW9uKCk6CiAgICB0cnk6CiAgICAgICAgIyDlu7rnq4vov57mjqUKICAgICAgICBjb25uID0gcHlteXNxbC5jb25uZWN0KCoqTVlTUUxfQ09ORklHKQogICAgICAgICMg5Yib5bu65ri45qCHCiAgICAgICAgY3Vyc29yID0gY29ubi5jdXJzb3IoKQogICAgICAgICMg5omn6KGM5rWL6K+V5p+l6K+iCiAgICAgICAgY3Vyc29yLmV4ZWN1dGUoIlNFTEVDVCBWRVJTSU9OKCk7IikKICAgICAgICB2ZXJzaW9uID0gY3Vyc29yLmZldGNob25lKClbMF0KICAgICAgICAjIOWFs+mXrei/nuaOpQogICAgICAgIGN1cnNvci5jbG9zZSgpCiAgICAgICAgY29ubi5jbG9zZSgpCiAgICAgICAgcmV0dXJuIGYiTXlTUUzov57mjqXmiJDlip/vvIFNeVNRTOeJiOacrO+8mnt2ZXJzaW9ufSIKICAgIGV4Y2VwdCBPcGVyYXRpb25hbEVycm9yIGFzIGU6CiAgICAgICAgcmV0dXJuIGYiTXlTUUzov57mjqXlpLHotKXvvIHplJnor6/kv6Hmga/vvJp7c3RyKGUpfSIKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICByZXR1cm4gZiLmnKrnn6XplJnor6/vvJp7c3RyKGUpfSIKCiMg5qC56Lev55Sx77ya5L+d55WZSGVsbG8gV29ybGQgKyDovpPlh7pNeVNRTOi/nuaOpeeKtuaAgQpAYXBwLnJvdXRlKCcvJykKZGVmIGhlbGxvX3dvcmxkKCk6CiAgICBteXNxbF9zdGF0dXMgPSB0ZXN0X215c3FsX2Nvbm5lY3Rpb24oKQogICAgbG9jYWxfaXAgPSBnZXRfbG9jYWxfaXAoKSAgICAgIyDmlrDlop7vvJrmnKzlnLBpcOiOt+WPlumAu+i+kQogICAgcmV0dXJuIGYiIiIKICAgIDxoMT5IZWxsbywgV29ybGQhPC9oMT4KICAgIDxoMz7mnKzlnLBpcDoge2xvY2FsX2lwfTwvaDM+CiAgICA8aDM+e215c3FsX3N0YXR1c308L2gzPgogICAgPHA+UkRT6YWN572u77ya5Li75py6PXtNWVNRTF9DT05GSUdbJ2hvc3QnXX3vvIzmlbDmja7lupM9e01ZU1FMX0NPTkZJR1snZGF0YWJhc2UnXX08L3A+CiAgICAiIiIKCiMg5ZCv5Yqo5bqU55So77yI55uR5ZCsODDnq6/lj6PvvIzlhYHorrjmiYDmnIlJUOiuv+mXru+8iQppZiBfX25hbWVfXyA9PSAnX19tYWluX18nOgogICAgYXBwLnJ1bihob3N0PScwLjAuMC4wJywgcG9ydD04MCwgZGVidWc9VHJ1ZSkK" | base64 -d > /tmp/app.py
python3 -m venv /opt/flask_venv
/opt/flask_venv/bin/pip install flask pymysql --upgrade
pkill -f 'python3 /tmp/app.py' || true
nohup /opt/flask_venv/bin/python3 /tmp/app.py > /tmp/flask.log 2>&1 &
eof
# 存储配置 (20GB 云盘)
system_disk_category = "cloud_essd_entry"
data_disks {
category = "cloud_essd_entry"
size = 20
}
# 标签 (便于资源管理, 差异化标识)
tags = {
Environment = "Practice"
Region = "Wulanchabu"
ManagedBy = "Terraform"
InstanceID = "ECS-1"
}
}
# 第二个 ECS 实例
resource "alicloud_instance" "ecs_2" {
# 自定义 ECS 实例名称、主机名、静态 IP (差异化)
instance_name = "my-custom-ecs-2"
host_name = "ecs-static-ip-host-2"
private_ip = "172.16.1.200"
# 基础标识 (与第一个 ECS 完全一致)
instance_type = "ecs.e-c1m1.large"
image_id = data.alicloud_images.default.images.0.id
vswitch_id = alicloud_vswitch.vsw.id
security_groups = [alicloud_security_group.sg.id]
# 主机密码 (复用变量, 保持一致)
password = var.ecs_password
# user_data自动启用密码登录
user_data = <<-eof
#!/bin/bash
# 启用 SSH 密码登录
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config
sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
systemctl restart sshd
# 安装 MySQL Python 依赖
apt update -y
apt install -y python3-pip python3-venv python3-flask net-tools
pip3 install pymysql cryptography
# ========== 解码生成 app.py ==========
echo "ZnJvbSBmbGFzayBpbXBvcnQgRmxhc2sKaW1wb3J0IHB5bXlzcWwKZnJvbSBweW15c3FsLmVyciBpbXBvcnQgT3BlcmF0aW9uYWxFcnJvcgoKIyDmlrDlop7vvJrlpoLkuIvmqKHlnZcKaW1wb3J0IHNvY2tldAppbXBvcnQgZmNudGwKaW1wb3J0IHN0cnVjdAoKIyDmlrDlop7vvJrojrflj5bmnKzlnLDkuLvmnLppcOWcsOWdgApkZWYgZ2V0X2xvY2FsX2lwKCk6CiAgICB0YXJnZXRfbmljcyA9IFsnZXRoMCcsICdlbnMzMyddCiAgICBmb3IgbmljIGluIHRhcmdldF9uaWNzOgogICAgICAgIHRyeToKICAgICAgICAgICAgcyA9IHNvY2tldC5zb2NrZXQoc29ja2V0LkFGX0lORVQsIHNvY2tldC5TT0NLX0RHUkFNKQogICAgICAgICAgICBuaWNfYnl0ZXMgPSBuaWMuZW5jb2RlKCd1dGYtOCcpWzoxNV0KICAgICAgICAgICAgaWZyZXEgPSBzdHJ1Y3QucGFjaygnMTZzSDE0cycsIG5pY19ieXRlcywgc29ja2V0LkFGX0lORVQsIGInXHgwMCcqMTQpCiAgICAgICAgICAgIHJlcyA9IGZjbnRsLmlvY3RsKHMuZmlsZW5vKCksIDB4ODkxNSwgaWZyZXEpCiAgICAgICAgICAgIGlwID0gc29ja2V0LmluZXRfbnRvYShyZXNbMjA6MjRdKQogICAgICAgICAgICBzLmNsb3NlKCkKICAgICAgICAgICAgaWYgbm90IGlwLnN0YXJ0c3dpdGgoJzEyNy4nKToKICAgICAgICAgICAgICAgIHJldHVybiBpcAogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICAgICAgY29udGludWUKCgojIOWIneWni+WMlkZsYXNr5bqU55SoCmFwcCA9IEZsYXNrKF9fbmFtZV9fKQoKIyBSRFMgTXlTUUzov57mjqXphY3nva7vvIjlkoxUZXJyYWZvcm3kuK3nmoRSRFPphY3nva7kuIDoh7TvvIkKTVlTUUxfQ09ORklHID0gewogICAgJ2hvc3QnOiAnMTcyLjE2LjEuMjAyJywgICAgICAgIyBSRFPnmoTnp4HmnIlJUAogICAgJ3BvcnQnOiAzMzA2LCAgICAgICAgICAgICAgICAgIyBNeVNRTOm7mOiupOerr+WPowogICAgJ3VzZXInOiAnZmxhc2tkYicsICAgICAgICAgICAgIyDnlKjmiLflkI0KICAgICdwYXNzd29yZCc6ICdmbGFza2RiQDEyMycsICAgICMg5a+G56CBCiAgICAnZGF0YWJhc2UnOiAnZmxhc2tkYicsICAgICAgICAjIOaVsOaNruW6k+WQjQogICAgJ2NoYXJzZXQnOiAndXRmOG1iNCcgICAgICAgICAgIyDlrZfnrKbpm4YKfQoKIyDmtYvor5VNeVNRTOi/nuaOpeeahOWHveaVsApkZWYgdGVzdF9teXNxbF9jb25uZWN0aW9uKCk6CiAgICB0cnk6CiAgICAgICAgIyDlu7rnq4vov57mjqUKICAgICAgICBjb25uID0gcHlteXNxbC5jb25uZWN0KCoqTVlTUUxfQ09ORklHKQogICAgICAgICMg5Yib5bu65ri45qCHCiAgICAgICAgY3Vyc29yID0gY29ubi5jdXJzb3IoKQogICAgICAgICMg5omn6KGM5rWL6K+V5p+l6K+iCiAgICAgICAgY3Vyc29yLmV4ZWN1dGUoIlNFTEVDVCBWRVJTSU9OKCk7IikKICAgICAgICB2ZXJzaW9uID0gY3Vyc29yLmZldGNob25lKClbMF0KICAgICAgICAjIOWFs+mXrei/nuaOpQogICAgICAgIGN1cnNvci5jbG9zZSgpCiAgICAgICAgY29ubi5jbG9zZSgpCiAgICAgICAgcmV0dXJuIGYiTXlTUUzov57mjqXmiJDlip/vvIFNeVNRTOeJiOacrO+8mnt2ZXJzaW9ufSIKICAgIGV4Y2VwdCBPcGVyYXRpb25hbEVycm9yIGFzIGU6CiAgICAgICAgcmV0dXJuIGYiTXlTUUzov57mjqXlpLHotKXvvIHplJnor6/kv6Hmga/vvJp7c3RyKGUpfSIKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZToKICAgICAgICByZXR1cm4gZiLmnKrnn6XplJnor6/vvJp7c3RyKGUpfSIKCiMg5qC56Lev55Sx77ya5L+d55WZSGVsbG8gV29ybGQgKyDovpPlh7pNeVNRTOi/nuaOpeeKtuaAgQpAYXBwLnJvdXRlKCcvJykKZGVmIGhlbGxvX3dvcmxkKCk6CiAgICBteXNxbF9zdGF0dXMgPSB0ZXN0X215c3FsX2Nvbm5lY3Rpb24oKQogICAgbG9jYWxfaXAgPSBnZXRfbG9jYWxfaXAoKSAgICAgIyDmlrDlop7vvJrmnKzlnLBpcOiOt+WPlumAu+i+kQogICAgcmV0dXJuIGYiIiIKICAgIDxoMT5IZWxsbywgV29ybGQhPC9oMT4KICAgIDxoMz7mnKzlnLBpcDoge2xvY2FsX2lwfTwvaDM+CiAgICA8aDM+e215c3FsX3N0YXR1c308L2gzPgogICAgPHA+UkRT6YWN572u77ya5Li75py6PXtNWVNRTF9DT05GSUdbJ2hvc3QnXX3vvIzmlbDmja7lupM9e01ZU1FMX0NPTkZJR1snZGF0YWJhc2UnXX08L3A+CiAgICAiIiIKCiMg5ZCv5Yqo5bqU55So77yI55uR5ZCsODDnq6/lj6PvvIzlhYHorrjmiYDmnIlJUOiuv+mXru+8iQppZiBfX25hbWVfXyA9PSAnX19tYWluX18nOgogICAgYXBwLnJ1bihob3N0PScwLjAuMC4wJywgcG9ydD04MCwgZGVidWc9VHJ1ZSkK" | base64 -d > /tmp/app.py
python3 -m venv /opt/flask_venv
/opt/flask_venv/bin/pip install flask pymysql --upgrade
pkill -f 'python3 /tmp/app.py' || true
nohup /opt/flask_venv/bin/python3 /tmp/app.py > /tmp/flask.log 2>&1 &
eof
# 存储配置
system_disk_category = "cloud_essd_entry"
data_disks {
category = "cloud_essd_entry"
size = 20
}
# 标签
tags = {
Environment = "Practice"
Region = "Wulanchabu"
ManagedBy = "Terraform"
InstanceID = "ECS-2"
}
}
EOF
powershell
# 因为将 flask 应用部署逻辑内嵌到了 ECS 自动化执行任务中, 所以 deploy_app.tf 文件不再需要
rm deploy_app.tf
创建 CLB
powershell
cat > clb.tf << 'EOF'
# 1. 创建内网 CLB 实例 (适配官方 alicloud_slb_load_balancer 资源)
resource "alicloud_slb_load_balancer" "private_clb" {
load_balancer_name = "flask-app-private-clb" # 官方标准参数
load_balancer_spec = "slb.s1.small" # 官方标准参数
address_type = "intranet" # 私网 CLB (公网为 internet)
address_ip_version = "ipv4" # 官方示例必选
vswitch_id = alicloud_vswitch.vsw.id # 关联现有 VSwitch (自动归属 VPC/可用区)
instance_charge_type = "PayBySpec" # 官方示例计费类型 (按需)
# 标签保持和现有资源一致
tags = {
Environment = "Practice"
Region = "Wulanchabu"
ManagedBy = "Terraform"
}
}
# 2. 创建 CLB 后端服务器组 (官方标准资源)
resource "alicloud_slb_server_group" "flask_server_group" {
load_balancer_id = alicloud_slb_load_balancer.private_clb.id
name = "flask-app-server-group" # 服务器组名称
}
# 3. 延迟初始化 (避免服务器组创建未完成就添加 ECS, 官方示例必加)
resource "time_sleep" "clb_sleep" {
depends_on = [alicloud_slb_server_group.flask_server_group]
create_duration = "30s" # 等待30秒确保服务器组初始化完成
}
# 4. 将第一个 ECS 添加到服务器组 (官方标准关联方式)
resource "alicloud_slb_server_group_server_attachment" "ecs_1_attach" {
depends_on = [time_sleep.clb_sleep]
server_group_id = alicloud_slb_server_group.flask_server_group.id
server_id = alicloud_instance.ecs_1.id
port = 80 # 关联 ECS 的 80 端口
weight = 100 # 权重 100
type = "ecs" # 服务器类型为 ECS
description = "ecs-1" # 描述便于识别
}
# 5. 将第二个 ECS 添加到服务器组
resource "alicloud_slb_server_group_server_attachment" "ecs_2_attach" {
depends_on = [time_sleep.clb_sleep]
server_group_id = alicloud_slb_server_group.flask_server_group.id
server_id = alicloud_instance.ecs_2.id
port = 80
weight = 100
type = "ecs"
description = "ecs-2"
}
# 6. 配置 CLB 80 端口 HTTP 监听 (含健康检查, 官方标准参数)
resource "alicloud_slb_listener" "clb_http_80" {
load_balancer_id = alicloud_slb_load_balancer.private_clb.id
server_group_id = alicloud_slb_server_group.flask_server_group.id
frontend_port = 80 # CLB 对外端口
backend_port = 80 # 转发到 ECS 的端口
protocol = "http" # HTTP 协议
bandwidth = 10 # 带宽限制 (官方示例默认)
# 健康检查配置 (官方标准参数)
health_check = "on"
health_check_uri = "/" # 检查 Flask 根路由
health_check_connect_port = 80 # 检查端口
healthy_threshold = 3 # 健康阈值
unhealthy_threshold = 3 # 不健康阈值
health_check_timeout = 5 # 超时时间
health_check_interval = 2 # 检查间隔
health_check_http_code = "http_2xx" # 健康状态码
# 会话保持 (测试场景关闭)
sticky_session = "off"
}
EOF
EIP 改造
powershell
cat > eip.tf << 'EOF'
# 创建静态公网 EIP (保留原有配置)
resource "alicloud_eip_address" "ecs_eip" {
address_name = "ecs-static-eip"
}
# 将 EIP 绑定到新的私网 CLB (适配官方 CLB 资源名)
resource "alicloud_eip_association" "eip_bind" {
allocation_id = alicloud_eip_address.ecs_eip.id
instance_id = alicloud_slb_load_balancer.private_clb.id # 关联新的 CLB 资源
instance_type = "SlbInstance" # 必须指定 CLB 类型
}
EOF
定制输出
powershell
cat > outputs.tf << 'EOF'
# 原有 ECS 输出调整为适配双 ECS
output "ecs_1_private_ip" {
value = alicloud_instance.ecs_1.private_ip
description = "第一个 ECS 的静态私有 IP"
}
output "ecs_2_private_ip" {
value = alicloud_instance.ecs_2.private_ip
description = "第二个 ECS 的静态私有 IP"
}
# 新增 CLB 相关输出
output "clb_private_ip" {
value = alicloud_slb_load_balancer.private_clb.address
description = "内网 CLB 的私有 IP"
}
output "clb_public_eip" {
value = alicloud_eip_address.ecs_eip.ip_address
description = "绑定到 CLB 的公网 EIP"
}
output "clb_app_url" {
value = format("http://%s", alicloud_eip_address.ecs_eip.ip_address)
description = "通过 CLB 访问 Flask 应用的地址 (80 端口)"
}
# 输出 RDS MySQL 连接信息
output "rds_mysql_info" {
value = {
host = alicloud_db_instance.flask_rds.private_ip_address
port = 3306
username = "flaskdb"
password = "flaskdb@123"
database = "flaskdb"
}
description = "RDS MySQL 连接信息 (VPC 内访问)"
}
EOF
执行计划
powershell
terraform init -reconfigure
terraform plan
terraform apply --auto-approve
测试效果
powershell
terraform state list
[root@ubuntu-240 ecs-4 ]#terraform output
clb_app_url = "http://8.130.169.240"
clb_private_ip = "172.16.1.235"
clb_public_eip = "8.130.169.240"
ecs_1_private_ip = "172.16.1.100"
ecs_2_private_ip = "172.16.1.200"
rds_mysql_info = {
"database" = "flaskdb"
"host" = "172.16.1.202"
"password" = "flaskdb@123"
"port" = 3306
"username" = "flaskdb"
}
# 访问公网 IP
[root@ubuntu-240 ecs-4 ]#curl http://8.130.169.240
<h1>Hello, World!</h1>
<h3>本地ip: 172.16.1.200</h3>
<h3>MySQL连接成功!MySQL版本:8.0.36</h3>
<p>RDS配置:主机=172.16.1.202,数据库=flaskdb</p>
[root@ubuntu-240 ecs-4 ]#curl http://8.130.169.240
<h1>Hello, World!</h1>
<h3>本地ip: 172.16.1.100</h3>
<h3>MySQL连接成功!MySQL版本:8.0.36</h3>
<p>RDS配置:主机=172.16.1.202,数据库=flaskdb</p>
清理环境
powershell
terraform destroy --auto-approve
8.1.5 模块化实践
8.1.5.1 根模块
环境准备
powershell
cd /data/case/ && mkdir -p terraform-flask-app/modules && cd /data/case/terraform-flask-app
根模块文件
powershell
# 定制供应商信息
cat > providers.tf << 'EOF'
# Terraform 版本约束
terraform {
required_providers {
# 阿里云Provider (指定版本, 避免兼容问题)
alicloud = {
source = "aliyun/alicloud"
version = "~> 1.266.0"
}
# time模块 (CLB 延迟初始化用, 提前引入)
time = {
source = "hashicorp/time"
version = "~> 0.12.0"
}
}
# 版本约束 (避免过低版本 Terraform 不兼容)
required_version = ">= 1.5.0"
}
# 阿里云 Provider 配置 (地域通过变量传入)
provider "alicloud" {
region = var.region
}
EOF
powershell
# 定义全局变量文件
cat > variables.tf << 'EOF'
# ========== 全局基础配置 ==========
variable "region" {
type = string
default = "cn-wulanchabu"
description = "阿里云地域"
}
# ========== 网络模块变量 ==========
variable "vpc_name" {
type = string
default = "ecs-practice-vpc"
description = "VPC名称"
}
variable "vpc_cidr" {
type = string
default = "172.16.0.0/16"
description = "VPC 网段"
}
variable "vswitch_cidr" {
type = string
default = "172.16.1.0/24"
description = "交换机网段"
}
variable "security_group_name" {
type = string
default = "ecs-practice-sg"
description = "安全组名称"
}
# ========== ECS 模块变量 ==========
variable "ecs_instance_type" {
type = string
default = "ecs.e-c1m1.large"
description = "ECS 实例规格 (需确保地域有该规格)"
}
variable "ecs_password" {
type = string
default = "Admin@123"
sensitive = true # 敏感参数不显示明文
description = "ECS root 用户密码 (需符合阿里云密码规则)"
}
variable "ecs_1_private_ip" {
type = string
default = "172.16.1.100"
description = "ECS 1 静态私有 IP"
}
variable "ecs_2_private_ip" {
type = string
default = "172.16.1.200"
description = "ECS 2 静态私有 IP"
}
# ========== RDS 模块变量 ==========
variable "rds_instance_name" {
type = string
default = "flask-mysql-db"
description = "RDS 实例名称"
}
variable "rds_instance_type" {
type = string
default = "rds.mysql.t1.small"
description = "RDS 实例规格"
}
variable "rds_storage" {
type = number
default = 20
description = "RDS 存储大小 (GB)"
}
variable "rds_private_ip" {
type = string
default = "172.16.1.202"
description = "RDS 静态私有 IP"
}
variable "rds_db_name" {
type = string
default = "flaskdb"
description = "RDS 数据库名称"
}
variable "rds_db_user" {
type = string
default = "flaskdb"
description = "RDS 数据库账号"
}
variable "rds_db_password" {
type = string
default = "flaskdb@123"
sensitive = true
description = "RDS 数据库密码"
}
# ========== CLB 模块变量 ==========
variable "clb_instance_name" {
type = string
default = "flask-app-private-clb"
description = "CLB 实例名称"
}
variable "clb_instance_spec" {
type = string
default = "slb.s1.small"
description = "CLB 实例规格"
}
EOF
powershell
# 定义变量赋值文件
cat > terraform.tfvars << 'EOF'
# 基础配置 (可根据实际需求修改)
region = "cn-wulanchabu"
# ECS 配置 (密码建议修改为符合阿里云规则的)
ecs_password = "Admin@123"
ecs_1_private_ip = "172.16.1.100"
ecs_2_private_ip = "172.16.1.200"
# RDS 配置
rds_db_password = "flaskdb@123"
rds_private_ip = "172.16.1.202"
EOF
powershell
# 定义根模块核心文件
cat > main.tf << 'EOF'
# 根模块: 暂未调用任何子模块 (先验证初始化)
# 后续步骤会逐步添加
module "network" {
source = "./modules/network"
region = var.region
vpc_name = var.vpc_name
vpc_cidr = var.vpc_cidr
vswitch_cidr = var.vswitch_cidr
security_group_name = var.security_group_name
}
module "rds" {
source = "./modules/rds"
region = var.region
vpc_id = module.network.vpc_id
vswitch_id = module.network.vswitch_id
security_group_id = module.network.security_group_id
rds_instance_name = var.rds_instance_name
rds_instance_type = var.rds_instance_type
rds_storage = var.rds_storage
rds_private_ip = var.rds_private_ip
rds_db_user = var.rds_db_user
rds_db_password = var.rds_db_password
rds_db_name = var.rds_db_name
}
module "ecs_1" {
source = "./modules/ecs"
instance_name = "my-custom-ecs-1"
host_name = "ecs-static-ip-host-1"
private_ip = var.ecs_1_private_ip
instance_type = var.ecs_instance_type
ecs_password = var.ecs_password
vswitch_id = module.network.vswitch_id
security_group_id = module.network.security_group_id
rds_ip = module.rds.rds_private_ip
rds_user = module.rds.rds_db_user
rds_pass = module.rds.rds_db_password
rds_db = module.rds.rds_db_name
}
module "ecs_2" {
source = "./modules/ecs"
instance_name = "my-custom-ecs-2"
host_name = "ecs-static-ip-host-2"
private_ip = var.ecs_2_private_ip
instance_type = var.ecs_instance_type
ecs_password = var.ecs_password
vswitch_id = module.network.vswitch_id
security_group_id = module.network.security_group_id
rds_ip = module.rds.rds_private_ip
rds_user = module.rds.rds_db_user
rds_pass = module.rds.rds_db_password
rds_db = module.rds.rds_db_name
}
module "clb" {
source = "./modules/clb"
region = var.region
clb_name = var.clb_instance_name
clb_spec = var.clb_instance_spec
vswitch_id = module.network.vswitch_id
}
# 挂载 ECS 1 → CLB
resource "alicloud_slb_server_group_server_attachment" "ecs1" {
depends_on = [module.clb.wait_ready]
server_group_id = module.clb.server_group_id
server_id = module.ecs_1.instance_id
port = 80
weight = 100
type = "ecs"
}
# 挂载 ECS 2 → CLB
resource "alicloud_slb_server_group_server_attachment" "ecs2" {
depends_on = [module.clb.wait_ready]
server_group_id = module.clb.server_group_id
server_id = module.ecs_2.instance_id
port = 80
weight = 100
type = "ecs"
}
EOF
powershell
# 定义输出信息文件
cat > outputs.tf << 'EOF'
# ========== 测试用输出 (验证变量) ==========
output "test_region" {
value = var.region
description = "测试: 输出地域变量"
}
output "test_ecs_password_masked" {
value = var.ecs_password != "" ? "******" : "未配置"
description = "测试: 验证 ECS 密码变量 (掩码显示)"
sensitive = true # 标记为敏感输出
}
# ========== 子模块输出骨架 (后续填充) ==========
# 根模块输出: 后续会汇总所有子模块的关键输出
# 网络模块输出
output "vpc_id" {
value = module.network.vpc_id
description = "VPC ID"
}
output "vswitch_id" {
value = module.network.vswitch_id
description = "交换机 ID"
}
# ECS 模块输出
output "ecs_1_private_ip" {
value = module.ecs_1.private_ip
description = "ECS 1 私有 IP"
}
output "ecs_2_private_ip" {
value = module.ecs_2.private_ip
description = "ECS 2 私有 IP"
}
# RDS 模块输出
output "rds_private_ip" {
value = module.rds.rds_private_ip
description = "RDS 私有 IP"
}
# CLB 模块输出
output "clb_public_eip" {
value = module.clb.clb_public_ip
description = "CLB 公网 EIP"
}
# CLB 的对外地址输出
output "clb_app_url" {
value = "http://${module.clb.clb_public_ip}"
}
EOF
8.1.5.2 网络模块
powershell
mkdir -p modules/network && cd modules/network
powershell
# 创建主配置文件
cat > main.tf << 'EOF'
# 自动获取可用区
data "alicloud_zones" "default" {}
# VPC (专有网络)
resource "alicloud_vpc" "vpc" {
vpc_name = var.vpc_name
cidr_block = var.vpc_cidr
}
# 交换机 (VSwitch)
resource "alicloud_vswitch" "vsw" {
vpc_id = alicloud_vpc.vpc.id
cidr_block = var.vswitch_cidr
zone_id = data.alicloud_zones.default.zones.0.id
}
# 安全组
resource "alicloud_security_group" "sg" {
security_group_name = var.security_group_name
vpc_id = alicloud_vpc.vpc.id
}
# 入站规则: SSH 22
resource "alicloud_security_group_rule" "sg_ingress_22" {
type = "ingress"
ip_protocol = "tcp"
port_range = "22/22"
cidr_ip = "0.0.0.0/0"
security_group_id = alicloud_security_group.sg.id
}
# 入站规则: HTTP 80
resource "alicloud_security_group_rule" "sg_ingress_80" {
type = "ingress"
ip_protocol = "tcp"
port_range = "80/80"
cidr_ip = "0.0.0.0/0"
security_group_id = alicloud_security_group.sg.id
}
# 入站规则: MySQL 3306 (仅 VPC 内)
resource "alicloud_security_group_rule" "sg_ingress_3306" {
type = "ingress"
ip_protocol = "tcp"
port_range = "3306/3306"
cidr_ip = var.vpc_cidr
security_group_id = alicloud_security_group.sg.id
}
EOF
powershell
# 创建环境变量文件
cat > variables.tf << 'EOF'
variable "region" {
type = string
}
variable "vpc_name" {
type = string
}
variable "vpc_cidr" {
type = string
}
variable "vswitch_cidr" {
type = string
}
variable "security_group_name" {
type = string
}
EOF
powershell
# 创建输出信息文件
cat > outputs.tf << 'EOF'
output "vpc_id" {
value = alicloud_vpc.vpc.id
}
output "vswitch_id" {
value = alicloud_vswitch.vsw.id
}
output "security_group_id" {
value = alicloud_security_group.sg.id
}
output "zone_id" {
value = data.alicloud_zones.default.zones.0.id
}
EOF
8.1.5.3 RDS 模块
powershell
cd /data/case/terraform-flask-app && mkdir -p modules/rds && cd modules/rds
powershell
# 创建主配置文件
cat > main.tf << 'EOF'
# 获取可用区
data "alicloud_zones" "default" {}
# RDS MySQL 实例
resource "alicloud_db_instance" "flask_rds" {
engine = "MySQL"
engine_version = "8.0"
instance_type = var.rds_instance_type
instance_name = var.rds_instance_name
zone_id = data.alicloud_zones.default.zones.0.id
vswitch_id = var.vswitch_id
security_group_ids = [var.security_group_id]
instance_storage = var.rds_storage
vpc_id = var.vpc_id
private_ip_address = var.rds_private_ip
tags = {
Environment = "Practice"
ManagedBy = "Terraform"
}
}
# 创建数据库账号
resource "alicloud_db_account" "flask_db_account" {
db_instance_id = alicloud_db_instance.flask_rds.id
account_name = var.rds_db_user
account_type = "Normal"
account_password = var.rds_db_password
}
# 创建数据库 (新版本的 terrform 不支持 name 属性,使用 data_base_name)
resource "alicloud_db_database" "flask_db" {
instance_id = alicloud_db_instance.flask_rds.id
data_base_name = var.rds_db_name
character_set = "utf8mb4"
}
# 授权账号访问数据库
resource "alicloud_db_account_privilege" "flask_privilege" {
instance_id = alicloud_db_instance.flask_rds.id
account_name = alicloud_db_account.flask_db_account.account_name
db_names = [alicloud_db_database.flask_db.data_base_name]
privilege = "ReadWrite"
}
EOF
powershell
# 创建环境变量文件
cat > variables.tf << 'EOF'
variable "region" {
type = string
}
variable "vpc_id" {
type = string
}
variable "vswitch_id" {
type = string
}
variable "security_group_id" {
type = string
}
variable "rds_instance_name" {
type = string
}
variable "rds_instance_type" {
type = string
}
variable "rds_storage" {
type = number
}
variable "rds_private_ip" {
type = string
}
variable "rds_db_user" {
type = string
}
variable "rds_db_password" {
type = string
}
variable "rds_db_name" {
type = string
}
EOF
powershell
# 创建输出信息文件
cat > outputs.tf << 'EOF'
output "rds_id" {
value = alicloud_db_instance.flask_rds.id
}
output "rds_private_ip" {
value = alicloud_db_instance.flask_rds.private_ip_address
}
output "rds_db_user" {
value = var.rds_db_user
}
output "rds_db_password" {
value = var.rds_db_password
sensitive = true
}
output "rds_db_name" {
value = var.rds_db_name
}
EOF
8.1.5.4 ECS 模块
powershell
cd /data/case/terraform-flask-app && mkdir -p modules/ecs && cd modules/ecs
powershell
cp /data/case/ecs-4/app.py ./
powershell
# 创建主配置文件
cat > main.tf << 'EOF'
# 获取 Ubuntu 24.04 镜像
data "alicloud_images" "default" {
name_regex = "^ubuntu_24_04.*"
most_recent = true
owners = "system"
}
locals {
image_id = data.alicloud_images.default.images != [] ? data.alicloud_images.default.images.0.id : "ubuntu_24_04_x64_20G_alibase_20261016.vhd"
}
# 创建 ECS 实例
resource "alicloud_instance" "single" {
instance_name = var.instance_name
host_name = var.host_name
private_ip = var.private_ip
instance_type = var.instance_type
image_id = local.image_id
vswitch_id = var.vswitch_id
security_groups = [var.security_group_id]
password = var.ecs_password
# 初始化脚本: 启用 SSH + 安装 Flask + 自动连接 RDS
user_data = <<-eof
#!/bin/bash
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config
sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
systemctl restart ssh
apt update -y
apt install -y python3-pip python3-venv python3-flask net-tools
pip3 install pymysql cryptography
# ========== 解码生成 app.py ==========
cat > /tmp/app.py << 'PYEOF'
from flask import Flask
import pymysql
from pymysql.err import OperationalError
# 新增: 如下模块
import socket
import fcntl
import struct
# 新增: 获取本地主机 ip 地址
def get_local_ip():
target_nics = ['eth0', 'ens33']
for nic in target_nics:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
nic_bytes = nic.encode('utf-8')[:15]
ifreq = struct.pack('16sH14s', nic_bytes, socket.AF_INET, b'\x00'*14)
res = fcntl.ioctl(s.fileno(), 0x8915, ifreq)
ip = socket.inet_ntoa(res[20:24])
s.close()
if not ip.startswith('127.'):
return ip
except Exception as e:
continue
# 初始化 Flask 应用
app = Flask(__name__)
# RDS MySQL 连接配置 (和 Terraform 中的 RDS 配置一致)
MYSQL_CONFIG = {
'host': '172.16.1.202', # RDS的私有 IP
'port': 3306, # MySQL 默认端口
'user': 'flaskdb', # 用户名
'password': 'flaskdb@123', # 密码
'database': 'flaskdb', # 数据库名
'charset': 'utf8mb4' # 字符集
}
# 测试 MySQL 连接的函数
def test_mysql_connection():
try:
# 建立连接
conn = pymysql.connect(**MYSQL_CONFIG)
# 创建游标
cursor = conn.cursor()
# 执行测试查询
cursor.execute("SELECT VERSION();")
version = cursor.fetchone()[0]
# 关闭连接
cursor.close()
conn.close()
return f"MySQL 连接成功!MySQL 版本: {version}"
except OperationalError as e:
return f"MySQL 连接失败!错误信息: {str(e)}"
except Exception as e:
return f"未知错误: {str(e)}"
# 根路由: 保留 Hello World + 输出 MySQL 连接状态
@app.route('/')
def hello_world():
mysql_status = test_mysql_connection()
local_ip = get_local_ip() # 新增: 本地 ip 获取逻辑
return f"""
<h1>Hello, World!</h1>
<h3>本地ip: {local_ip}</h3>
<h3>{mysql_status}</h3>
<p>RDS配置: 主机={MYSQL_CONFIG['host']}, 数据库={MYSQL_CONFIG['database']}</p>
"""
# 启动应用 (监听 80 端口, 允许所有 IP 访问)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=True)
PYEOF
python3 -m venv /opt/flask_venv
/opt/flask_venv/bin/pip install flask pymysql --upgrade
pkill -f 'python3 /tmp/app.py' || true
nohup /opt/flask_venv/bin/python3 /tmp/app.py > /tmp/flask.log 2>&1 &
eof
system_disk_category = "cloud_essd_entry"
data_disks {
category = "cloud_essd_entry"
size = 20
}
tags = {
Environment = "Practice"
ManagedBy = "Terraform"
}
}
EOF
powershell
# 创建环境变量文件
cat > variables.tf << 'EOF'
variable "instance_name" { type = string }
variable "host_name" { type = string }
variable "private_ip" { type = string }
variable "instance_type" { type = string }
variable "ecs_password" { type = string }
variable "vswitch_id" { type = string }
variable "security_group_id" { type = string }
variable "rds_ip" { type = string }
variable "rds_user" { type = string }
variable "rds_pass" { type = string }
variable "rds_db" { type = string }
EOF
powershell
# 创建输出信息文件
cat > outputs.tf << 'EOF'
output "instance_id" {
value = alicloud_instance.single.id
}
output "private_ip" {
value = alicloud_instance.single.private_ip
}
EOF
8.1.5.5 CLB 模块
powershell
cd /data/case/terraform-flask-app && mkdir -p modules/clb && cd modules/clb
powershell
# 创建主配置文件
cat > main.tf << 'EOF'
# 私网 CLB
resource "alicloud_slb_load_balancer" "private_clb" {
load_balancer_name = var.clb_name
load_balancer_spec = var.clb_spec
address_type = "intranet"
address_ip_version = "ipv4"
vswitch_id = var.vswitch_id
instance_charge_type = "PayBySpec"
tags = {
Environment = "Practice"
ManagedBy = "Terraform"
}
}
# EIP (公网 IP)
resource "alicloud_eip_address" "ecs_eip" {
address_name = "${var.clb_name}-eip"
}
# 绑定 EIP → CLB
resource "alicloud_eip_association" "eip_bind" {
allocation_id = alicloud_eip_address.ecs_eip.id
instance_id = alicloud_slb_load_balancer.private_clb.id
instance_type = "SlbInstance"
}
# 后端服务器组
resource "alicloud_slb_server_group" "app_group" {
load_balancer_id = alicloud_slb_load_balancer.private_clb.id
name = "${var.clb_name}-server-group"
}
# 等待 30 秒 (必须)
resource "time_sleep" "wait" {
depends_on = [alicloud_slb_server_group.app_group]
create_duration = "30s"
}
# 监听 80 端口
resource "alicloud_slb_listener" "http_80" {
load_balancer_id = alicloud_slb_load_balancer.private_clb.id
server_group_id = alicloud_slb_server_group.app_group.id
frontend_port = 80
backend_port = 80
protocol = "http"
bandwidth = 10
health_check = "on"
health_check_uri = "/"
health_check_connect_port = 80
health_check_http_code = "http_2xx"
sticky_session = "off"
}
EOF
powershell
# 创建环境变量文件
cat > variables.tf << 'EOF'
variable "region" { type = string }
variable "clb_name" { type = string }
variable "clb_spec" { type = string }
variable "vswitch_id" { type = string }
EOF
powershell
# 创建输出信息文件
cat > outputs.tf << 'EOF'
output "clb_id" {
value = alicloud_slb_load_balancer.private_clb.id
}
output "clb_private_ip" {
value = alicloud_slb_load_balancer.private_clb.address
}
output "clb_public_ip" {
value = alicloud_eip_address.ecs_eip.ip_address
}
output "server_group_id" {
value = alicloud_slb_server_group.app_group.id
}
output "wait_ready" {
value = time_sleep.wait.id
}
EOF
8.1.5.6 执行测试
powershell
cd /data/case/terraform-flask-app
# 目录结构
[root@ubuntu-240 terraform-flask-app ]#tree
.
├── main.tf
├── modules
│ ├── clb
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ ├── ecs
│ │ ├── app.py
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ ├── network
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ └── rds
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── outputs.tf
├── providers.tf
├── terraform.tfvars
└── variables.tf
powershell
# 执行计划
terraform init
terraform plan
terraform apply -auto-approve
powershell
# 测试效果
[root@ubuntu-240 terraform-flask-app ]#terraform output
clb_app_url = "http://8.146.227.102"
clb_public_eip = "8.146.227.102"
ecs_1_private_ip = "172.16.1.100"
ecs_2_private_ip = "172.16.1.200"
rds_private_ip = "172.16.1.202"
test_ecs_password_masked = <sensitive>
test_region = "cn-wulanchabu"
vpc_id = "vpc-0jljb8kl7mmekzt50bn28"
vswitch_id = "vsw-0jl8sffebun6pslusu6a1"
[root@ubuntu-240 terraform-flask-app ]#curl http://8.146.227.102
<h1>Hello, World!</h1>
<h3>本地ip: 172.16.1.100</h3>
<h3>MySQL 连接成功!MySQL 版本: 8.0.36</h3>
<p>RDS配置: 主机=172.16.1.202, 数据库=flaskdb</p>
[root@ubuntu-240 terraform-flask-app ]#curl http://8.146.227.102
<h1>Hello, World!</h1>
<h3>本地ip: 172.16.1.200</h3>
<h3>MySQL 连接成功!MySQL 版本: 8.0.36</h3>
<p>RDS配置: 主机=172.16.1.202, 数据库=flaskdb</p>
8.1.5.6 环境清理
powershell
terraform destroy -auto-approve
总结SRE可靠性治理和指标体系建设
- SRE 起源
- SRE 起源于 Google
- 2003 年左右由 Google 的工程师 Ben Treynor Sloss 提出了一种全新的思路:用软件工程的方法来解决运维问题
- 他组建了一支由软件工程师组成的团队来负责系统运维,这支团队就是最早的 SRE 团队
- 后来,Google 将 SRE 的理念和实践总结成书(《Site Reliability Engineering》),向业界推广,SRE 由此成为现代云原生运维的重要标准。
- 核心使命
- SRE 的核心使命是在保障系统高可靠性的同时,维持快速的迭代交付速度
- 保护与推进(Protect and Progress):在系统的稳定性(可靠性)与功能迭代速度之间找到最佳平衡点,避免为了稳定而停滞不前,也避免为了速度而牺牲稳定性
- 工程化解决运维问题:通过编写代码、开发自动化工具来替代重复的手工操作,消除"运维工作"(Toil),让系统具备自我修复和自动化扩展的能力
- 风险量化管理:通过服务等级目标(SLO)和错误预算(Error Budget)来量化系统的可靠性,用数据驱动的方式决定何时发布新功能、何时停止发布去修复稳定性问题
- DevOps 与 SRE 的关系
- DevOps 是一场文化运动:强调打破开发与运维之间的墙,让两者协作、共享责任,核心理念是自动化、度量、共享(CAMS),目标是更快交付更稳定
- SRE是具体实践:由 Google 提出,是 DevOps 文化的一种具体实现方法论------通过工程化手段(代码化、自动化、SLO、错误预算)落地DevOps理念
- 关系:两者互补而非替代------DevOps 回答"为什么"(文化、协作),SRE 回答"怎么做"(具体工程实践);团队可同时采用DevOps 文化+SRE 实践; SRE 提供了可操作的度量(SLO/错误预算)让"可靠性"可量化
- 可靠性指标体系
- SLI(服务等级指标):衡量服务质量的量化指标,如登录接口 P95 延迟、请求成功率、可用性------是 "测量什么"
- SLO(服务等级目标):为SLI设定的目标值,如 "登录接口 P99 延迟 < 500ms" "API 可用性 > 99.9%"------是 "期望达到什么"
- SLA(服务等级协议):对用户的正式承诺及违约补偿,如 "月度可用性 < 99.9% 赔偿 10% 月费"------是 "对外承诺什么"
- 关系:SLI 是基础指标,SLO 基于 SLI 设定内部目标,SLA 基于 SLO 对外承诺且通常更宽松(内部目标高于外部承诺),三者构成服务质量度量体系
- 示例:SLI = 请求成功率,SLO = 99.9%,SLA = 99.95% 月度可用性(对外承诺,含赔偿条款)
使用 opencode 结合 Ruoyi skill 完成课程实践
RuoYi-Vue 案例
环境准备
powershell
# 解压 SKILL 压缩包
[root@ubuntu-240 ~ ]#tar xf ruoyi-deploy.tar.gz
# 为脚本文件赋予权限
[root@ubuntu-240 ~ ]#chmod +x ruoyi-deploy/scripts/*.sh
# 创建项目目录
[root@ubuntu-240 ~ ]#mkdir -p /data/ops/.opencode/skills
# 将 SKILL 文件拷贝到项目目录
[root@ubuntu-240 ~ ]#cp -r ruoyi-deploy /data/ops/.opencode/skills/
# 目录结构
[root@ubuntu-240 ~ ]#tree /data/ops/.opencode/skills/ruoyi-deploy/
/data/ops/.opencode/skills/ruoyi-deploy/
├── assets
│ ├── ruoyi-admin.service.tpl
│ └── ruoyi.conf.tpl
├── references
│ ├── parameters.md
│ ├── rollback.md
│ ├── troubleshoot.md
│ └── verify.md
├── scripts
│ ├── onekey_install_ruoyi_ubuntu24.sh
│ ├── preflight_check.sh
│ ├── run_with_log.sh
│ └── ruoyi_collect_status_check.sh
└── SKILL.md
powershell
# 准备自定义命令
mkdir -p /data/ops/.opencode/commands
cat > /data/ops/.opencode/commands/deploy.md << 'EOF'
---
description: 部署若依(调用 ruoyi-deploy skill)
agent: build
---
使用 ruoyi-deploy skill 完成单机部署
要求:
1. 先执行 scripts/preflight_check.sh, 把结果逐项输出
2. 出现「严重」项就停下, 不要继续
3. 我确认后再执行 scripts/run_with_log.sh
4. 结束后按 SKILL.md 的输出格式汇报
补充说明: $ARGUMENTS
EOF
powershell
# 项目执行前确认
cd /data/ops && opencode run "读一下 .opencode/skills/ruoyi-deploy/SKILL.md,然后执行 scripts/preflight_check.sh"
使用 SKILL
powershell
# 开启会话窗口
opencode
查看 SKILL

查看 SKILL 目录结构

powershell
# 使用自定义命令执行部署
/deploy
# 执行完基础检查需要确认继续部署
继续
部署完成浏览器访问验证
故障演练
powershell
# 备份相关数据
mysqldump -uroot -p'Root@2026' --single-transaction --all-databases > /root/bak-$(date +%F).sql
tar czf /root/upload-$(date +%F).tar.gz /data/ruoyi/uploadPath
ls -lh /root/bak-*.sql /root/upload-*.tar.gz
powershell
# 模拟后端端口被占用
systemctl stop ruoyi-admin
python3 -m http.server 8080 >/dev/null 2>&1 &
powershell
# OpenCode 故障分析, 只诊断, 不处理
opencode serve --hostname 0.0.0.0 --port 10020
# 浏览器打开 OpenCode 页面, 发送语句
现象: ruoyi-admin服务没有启动成功, 请按 references/troubleshoot.md 的排查顺序定位, 先只读诊断, 不要修改任何文件; 给出结论和 2 个候选解决方案"

