从手动运维到基础设施即代码:华为云 Terraform 实战避坑指南

举报
yd_243668775 发表于 2026/11/04 08:17:53 2026/11/04
【摘要】 如果你还在控制台里点点点,手动创建 VPC、子网、安全组、ECS……那你一定经历过这些痛苦:测试环境要重建,又得从头点一遍同事问"线上安全组开了哪些端口",你打开控制台翻了半天手滑删错了资源,连夜写事故报告基础设施即代码(Infrastructure as Code,简称 IaC)就是解决这些问题的。用 Terraform 把基础设施描述成代码,一条命令创建,一条命令销毁,版本可控,复用性强...

如果你还在控制台里点点点,手动创建 VPC、子网、安全组、ECS……那你一定经历过这些痛苦:

  • 测试环境要重建,又得从头点一遍
  • 同事问"线上安全组开了哪些端口",你打开控制台翻了半天
  • 手滑删错了资源,连夜写事故报告

基础设施即代码(Infrastructure as Code,简称 IaC)就是解决这些问题的。用 Terraform 把基础设施描述成代码,一条命令创建,一条命令销毁,版本可控,复用性强。

本文不讲 Terraform 入门语法(官方文档已经很详细了),只讲我在华为云上用 Terraform 实战踩过的坑,以及怎么避开它们。


一、provider 配置:别把 AK/SK 写进代码里

最常见的第一个坑,就是直接把 AK/SK 明文写进 .tf 文件:

provider "huaweicloud" {
  access_key  = "AKXXXXXXXXXXXXXX"
  secret_key  = "SKXXXXXXXXXXXXXXXXXXXXXXXXXX"
  region      = "cn-north-4"
}

这段代码能跑,但一旦提交到 Git,你的密钥就泄露了。

正确做法:用环境变量或 profile 方式:

provider "huaweicloud" {
  region      = "cn-north-4"
  # AK/SK 通过环境变量 HW_ACCESS_KEY / HW_SECRET_KEY 传入
  # 或者使用 credentials 文件中的 profile
}

在执行 terraform plan 之前,先 export 环境变量:

export HW_ACCESS_KEY="你的AK"
export HW_SECRET_KEY="你的SK"

这样代码可以安全提交,密钥只存在本地环境。


二、VPC 和子网:别忘了 IPv6 和可用区

很多人创建 VPC 时只配了 IPv4 CIDR,后来业务需要 IPv6,又得重建。

建议一开始就考虑清楚:

resource "huaweicloud_vpc" "main" {
  name       = "vpc-prod"
  cidr       = "192.168.0.0/16"
  enable_ipv6 = true  # 提前开启,避免后期重建
}

resource "huaweicloud_vpc_subnet" "web" {
  name       = "subnet-web"
  vpc_id     = huaweicloud_vpc.main.id
  cidr       = "192.168.1.0/24"
  gateway_ip = "192.168.1.1"
  # 指定可用区,多子网分布在不同 AZ 实现高可用
  availability_zone = "cn-north-4a"
}

坑点:华为云不同区域可用区名称不一样,比如 cn-north-4 是 4a/4b,cn-east-3 是 3a/3b。写代码前先确认目标区域的可用区列表,别想当然。


三、安全组:最小权限原则不是一句口号

我见过太多安全组直接开 0.0.0.0/0 全端口,这在测试环境也许无所谓,但一旦带到生产就是灾难。

分层设计安全组:

# Web 层安全组:只开 80 和 443
resource "huaweicloud_networking_secgroup" "web" {
  name        = "sg-web"
  description = "Web layer security group"
}

resource "huaweicloud_networking_secgroup_rule" "web_http" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 80
  port_range_max    = 80
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = huaweicloud_networking_secgroup.web.id
}

resource "huaweicloud_networking_secgroup_rule" "web_https" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 443
  port_range_max    = 443
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = huaweicloud_networking_secgroup.web.id
}

# 数据库层安全组:只允许 Web 层访问 3306
resource "huaweicloud_networking_secgroup" "db" {
  name        = "sg-db"
  description = "Database layer security group"
}

resource "huaweicloud_networking_secgroup_rule" "db_mysql" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 3306
  port_range_max    = 3306
  remote_group_id   = huaweicloud_networking_secgroup.web.id  # 只允许 Web 层访问
  security_group_id = huaweicloud_networking_secgroup.db.id
}

关键点:数据库安全组的入站规则用 remote_group_id 引用 Web 层安全组,而不是写死 IP。这样 Web 层扩容时新 ECS 自动就能访问数据库,不用改安全组规则。


四、ECS 创建:镜像 ID 会变,别写死

新手常犯的错误是直接写死镜像 ID:

# ❌ 错误做法
resource "huaweicloud_compute_instance" "web" {
  image_id = "a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx"  # 这个 ID 会过期!
  ...
}

华为云公共镜像会定期更新,旧镜像 ID 可能被下架。

正确做法:用 data source 动态查询:

data "huaweicloud_images_image" "ubuntu" {
  name        = "Ubuntu 22.04 server 64bit"
  most_recent = true
}

resource "huaweicloud_compute_instance" "web" {
  name        = "web-server-01"
  image_id    = data.huaweicloud_images_image.ubuntu.id
  flavor_id   = "s6.large.2"
  ...
}

most_recent = true 会自动取最新版本,不用手动维护镜像 ID。


五、状态文件管理:本地 state 是定时炸弹

Terraform 默认把状态存在本地 terraform.tfstate 文件里。单人开发没问题,团队协作时这是灾难:

  • 两个人同时 apply,state 冲突
  • 本地文件误删,基础设施"失联",只能手动 import
  • state 文件里明文存着密码等敏感信息

建议用 OBS 做远程后端:

terraform {
  backend "s3" {
    bucket   = "my-terraform-state"
    key      = "prod/terraform.tfstate"
    region   = "cn-north-4"
    endpoint = "obs.cn-north-4.myhuaweicloud.com"
    # OBS 兼容 S3 协议,Terraform 可以直接用
  }
}

配合 DynamoDB 做状态锁(华为云对应 DCS 或自建方案),防止并发写入。


六、destroy 是不可逆的:养成 plan 习惯

terraform destroy 会删除所有资源,没有回收站。我见过有人把生产环境的 state 和测试环境搞混,一条 destroy 把线上全删了。

养成三个习惯:

  1. 先 plan 再 apply:terraform plan 会告诉你哪些资源要创建、修改、删除,看清楚再执行
  2. 加 prevent_destroy 生命周期保护:
resource "huaweicloud_compute_instance" "prod_db" {
  name      = "prod-mysql-master"
  ...

  lifecycle {
    prevent_destroy = true  # terraform destroy 时会拒绝删除此资源
  }
}
  1. 不同环境用不同的 state 目录:environments/dev/、environments/prod/ 物理隔离,别用一个 state 文件管所有环境。

七、变量管理:别把所有东西都写死

好的 IaC 代码应该是参数化的。用 variables.tf 管理输入,main.tf 管理资源,outputs.tf 管理输出:

# variables.tf
variable "env" {
  type    = string
  default = "dev"
}

variable "instance_count" {
  type    = number
  default = 2
}

# main.tf
resource "huaweicloud_compute_instance" "web" {
  count = var.instance_count
  name  = "web-${var.env}-${count.index + 1}"
  ...
}

这样切换环境只需要传不同的变量值,不用改代码:

terraform apply -var="env=prod" -var="instance_count=4"

写在最后

从手动运维到 IaC,最大的转变不是工具,而是思维方式:

  • 一切皆代码:基础设施和业务代码一样,有版本、有 review、有 CI/CD
  • 幂等性:同一个配置 apply 一百次,结果一样,不会创建出重复资源
  • 可复现:新人 clone 代码,一条命令就能拉起一套一模一样的环境

Terraform 的学习曲线不算陡,但华为云 provider 的文档有些地方确实不够详细,需要结合实际操作来摸索。希望这些避坑经验能帮你少走弯路。

如果你也在用 Terraform 管理华为云资源,欢迎在评论区交流你的经验和问题。我们一起把踩过的坑填平。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。