2. Oracle Cloud基础设施即代码基础

在OCI上做平台工程,绕不开一个核心问题:如何让网络、计算、存储、身份策略这些资源可重复、可审计、可回滚地创建出来。控制台点击虽然直观,但无法版本化,也无法在多个环境之间保持一致。Terraform与OCI资源管理器(Resource Manager)正是解决这个问题的两条路径:前者是业界通用的IaC工具,后者是OCI原生的托管式Terraform服务。理解它们的关系,是掌握OCI自动化交付的起点。

一、Terraform在OCI中的工作模型

Terraform的核心是“声明式配置 + 状态文件 + 执行计划”三件套。你写的是期望状态,Terraform负责计算从当前状态到期望状态的差异,并生成执行计划。在OCI场景中,这个模型具体化为:

  • Provider:provider "oci",通过tenancy_ocid、user_ocid、fingerprint、private_key_path、region等参数建立与OCI的认证连接。
  • Resource:如oci_core_vcn、oci_core_subnet、oci_core_instance,每个资源对应OCI中的一个真实对象。
  • State:记录资源ID与属性的映射,是Terraform判断“资源是否已存在”的唯一依据。
  • Plan/Apply:先plan看差异,再apply执行变更。

一个最小的VCN定义如下,注意CIDR块和DNS标签是后续子网划分的基础:

resource "oci_core_vcn" "prod_vcn" {
  compartment_id = var.compartment_ocid
  cidr_block     = "10.20.0.0/16"
  display_name   = "prod-vcn"
  dns_label      = "prodvcn"
}

resource "oci_core_subnet" "app_subnet" {
  compartment_id    = var.compartment_ocid
  vcn_id            = oci_core_vcn.prod_vcn.id
  cidr_block        = "10.20.10.0/24"
  display_name      = "app-subnet"
  dns_label         = "app"
  security_list_ids = [oci_core_vcn.prod_vcn.default_security_list_id]
}

这里有一个关键细节:oci_core_subnet引用了oci_core_vcn.prod_vcn.id,Terraform会自动推导出依赖关系,先建VCN再建子网。这种隐式依赖是Terraform相比脚本化API调用的最大优势之一。

二、状态管理:本地、远程与锁定

状态文件是Terraform的“记忆”。默认情况下,terraform apply会在当前目录生成terraform.tfstate。这在单人实验中可行,但在团队协作中会立刻出问题:两个人同时apply,状态文件互相覆盖,资源就会失控。

OCI场景下推荐的做法是把状态放到OCI Object Storage中,并启用锁定。配置方式是在backend块中指定bucket和namespace:

terraform {
  backend "oci" {
    bucket    = "tf-state-prod"
    namespace = "mytenancy"
    key       = "network/terraform.tfstate"
    region    = "ap-singapore-1"
  }
}

状态文件里包含资源ID、敏感属性(如数据库密码的初始值),因此bucket必须设置为私有,并配合IAM策略限制访问。另一个常见做法是使用OCI资源管理器,它天然把状态托管在服务端,不需要自己维护backend配置。

不要手动编辑terraform.tfstate。如果状态与真实资源不一致,应使用terraform import或terraform state rm等命令修正,而不是直接改文件。

三、模块化组织:从单文件到可复用单元

当资源数量超过几十个,把所有内容写在一个main.tf里会变得难以维护。模块化的思路是把一组相关资源封装成一个可调用的单元,通过输入变量暴露差异点,通过输出值暴露下游需要引用的ID。

以“标准三层网络”为例,可以定义一个modules/network模块,输入是compartment_id、vcn_cidr、子网列表,输出是vcn_id和各子网id。根目录的main.tf只需要调用:

module "prod_network" {
  source         = "./modules/network"
  compartment_id = var.compartment_ocid
  vcn_cidr       = "10.20.0.0/16"
  subnets = {
    app = "10.20.10.0/24"
    db  = "10.20.20.0/24"
  }
}

模块化的收益在环境复制时最明显:dev、staging、prod三个环境可以复用同一个模块,只替换变量文件。但要注意,模块不是越细越好。如果一个模块只封装一个资源,反而增加了调用复杂度。合理的粒度是“一组生命周期一致、被一起创建和销毁的资源”。

四、OCI资源管理器:托管式Terraform

OCI资源管理器把Terraform的执行环境托管在OCI内部。你上传配置或指向Git仓库,资源管理器负责运行plan和apply,状态由服务管理。它适合以下场景:

  • 团队中没有专人维护Terraform执行环境;
  • 需要通过OCI控制台或API触发部署,而不是本地CLI;
  • 希望把状态、日志、权限统一收敛到OCI IAM体系内。

资源管理器的Stack概念对应一个Terraform工作目录,Job对应一次plan或apply。它支持从GitHub、GitLab、OCI DevOps拉取配置,也支持直接上传zip。对于平台工程团队,一个常见模式是:用资源管理器管理基础网络和共享服务,用本地Terraform或CI流水线管理应用层资源。

Terraform 与 OCI 资源管理器协作模型 本地 / CI Terraform main.tf / variables.tf 声明期望状态 terraform plan 差异计算 terraform apply 执行变更 State 文件 资源ID映射 modules/network 可复用单元 OCI 资源管理器 Stack 配置 + 变量 + 状态 Job: Plan 托管执行 Job: Apply 托管执行 服务端 State 自动锁定 Git / Zip 源 版本化配置 OCI 真实资源 VCN 10.20.0.0/16 Subnet: app 10.20.10.0/24 Subnet: db 10.20.20.0/24 Compute / LB 应用层资源 IAM 策略 访问控制 上传/同步 API调用 状态同步 / 导入 依赖

五、从入门到可用的关键判断

学完这一节,可以用三个问题检验自己是否真正入门:第一,能否说清楚terraform plan输出的“+、-、~”分别代表什么,以及为什么plan结果和apply结果有时不完全一致;第二,能否在OCI Object Storage中配置好backend并验证状态锁定生效;第三,能否把一段单文件配置拆成模块,并在另一个compartment中复用。

实际项目中,最常见的错误不是语法写错,而是状态管理失控:有人手动在控制台改了安全列表,Terraform下次plan就会试图改回去;有人把state文件提交到了Git,导致敏感信息泄露。这些问题的根源都在于没有把“状态”当作一等公民来对待。Terraform的声明式模型要求你接受一个前提:所有变更都通过代码走,控制台只用于查看和排障。这个纪律建立起来之后,OCI上的资源交付才真正具备可重复性和可审计性。

建议在第一个练习中只创建VCN和两个子网,先跑通plan/apply/destroy的完整循环,再逐步加入计算实例和负载均衡。每增加一类资源,都先确认plan输出符合预期,再执行apply。