3. Terraform on OCI快速上手

本节把 Terraform 从“听说过”推进到“能在 OCI 上真正跑起来”。目标很明确:在本地安装 Terraform,配置 OCI Provider 的认证信息,用 HCL 写出一个虚拟云网络(VCN)和一台计算实例,执行 terraform apply 后能在 OCI 控制台看到资源,最后用 terraform destroy 清理干净。整个过程围绕三个核心对象展开:Terraform CLI、OCI Provider、以及由 VCN 与计算实例组成的最小资源栈。

一、安装 Terraform 与准备 OCI 认证

Terraform 是单个二进制文件,安装方式取决于操作系统。以 Linux/macOS 为例,推荐使用 HashiCorp 官方仓库或直接下载压缩包。安装完成后用版本命令确认:

# 以 Linux x86_64 为例,下载并解压到 PATH
wget https://releases.hashicorp.com/terraform/1.9.5/terraform_1.9.5_linux_amd64.zip
unzip terraform_1.9.5_linux_amd64.zip -d /usr/local/bin/
terraform -version
# 预期输出:Terraform v1.9.5

OCI Provider 需要认证信息才能调用 OCI API。最常用的方式是 API Key 认证,需要准备四类信息:tenancy OCID、user OCID、fingerprint、private key 文件路径。这些信息来自 OCI 控制台“用户设置 → API 密钥”中生成的密钥对。把公钥上传到 OCI 后,控制台会给出 fingerprint,私钥保存在本地,例如 ~/.oci/oci_api_key.pem。此外还需要 region,例如 ap-singapore-1。

认证信息属于敏感数据,不要直接写进 .tf 文件提交到 Git。推荐使用环境变量或 ~/.oci/config 文件,Terraform 会自动读取。

二、Provider 配置与变量组织

新建一个工作目录,例如 oci-demo,在其中创建 provider.tf。Provider 块声明使用 oracle/oci,并指定版本约束,避免不同环境拉到不兼容版本:

terraform {
  required_providers {
    oci = {
      source  = "oracle/oci"
      version = "~> 6.0"
    }
  }
}

provider "oci" {
  tenancy_ocid     = var.tenancy_ocid
  user_ocid        = var.user_ocid
  fingerprint      = var.fingerprint
  private_key_path = var.private_key_path
  region           = var.region
}

把易变参数抽到 variables.tf,例如 compartment_ocid、region、ssh_public_key。这样同一份代码可以在不同 compartment 或 region 复用。变量值可以写在 terraform.tfvars 中,但该文件同样不应提交敏感内容。

三、编写 VCN 与计算实例资源定义

VCN 是 OCI 中的软件定义网络,计算实例必须落在某个子网中。最小可用网络需要:VCN、Internet Gateway、路由表、子网。下面用 network.tf 定义这些资源,CIDR 使用示例值 10.0.0.0/16,子网使用 10.0.1.0/24:

resource "oci_core_vcn" "demo_vcn" {
  compartment_id = var.compartment_ocid
  cidr_block     = "10.0.0.0/16"
  display_name   = "demo-vcn"
  dns_label      = "demovcn"
}

resource "oci_core_internet_gateway" "demo_igw" {
  compartment_id = var.compartment_ocid
  vcn_id         = oci_core_vcn.demo_vcn.id
  display_name   = "demo-igw"
  enabled        = true
}

resource "oci_core_route_table" "demo_rt" {
  compartment_id = var.compartment_ocid
  vcn_id         = oci_core_vcn.demo_vcn.id
  display_name   = "demo-rt"

  route_rules {
    destination       = "0.0.0.0/0"
    network_entity_id = oci_core_internet_gateway.demo_igw.id
  }
}

resource "oci_core_subnet" "demo_subnet" {
  compartment_id    = var.compartment_ocid
  vcn_id            = oci_core_vcn.demo_vcn.id
  cidr_block        = "10.0.1.0/24"
  display_name      = "demo-subnet"
  dns_label         = "demosub"
  route_table_id    = oci_core_route_table.demo_rt.id
  security_list_ids = [oci_core_vcn.demo_vcn.default_security_list_id]
}

接着在 compute.tf 中定义计算实例。需要指定可用域、shape、镜像、子网和 SSH 公钥。镜像通过数据源查询最新 Oracle Linux 8:

data "oci_core_images" "ol8" {
  compartment_id           = var.compartment_ocid
  operating_system         = "Oracle Linux"
  operating_system_version = "8"
  shape                    = "VM.Standard.E4.Flex"
  sort_by                  = "TIMECREATED"
  sort_order               = "DESC"
}

resource "oci_core_instance" "demo_vm" {
  compartment_id      = var.compartment_ocid
  availability_domain = data.oci_identity_availability_domains.ads.availability_domains[0].name
  shape               = "VM.Standard.E4.Flex"
  display_name        = "demo-vm"

  shape_config {
    ocpus         = 1
    memory_in_gbs = 8
  }

  source_details {
    source_type = "image"
    source_id   = data.oci_core_images.ol8.images[0].id
  }

  create_vnic_details {
    subnet_id        = oci_core_subnet.demo_subnet.id
    assign_public_ip = true
  }

  metadata = {
    ssh_authorized_keys = var.ssh_public_key
  }
}

注意 availability_domain 需要先通过 data "oci_identity_availability_domains" 查询,否则无法确定具体 AD 名称。这是初学者最容易漏掉的一步。

Terraform on OCI 最小资源栈与依赖关系 Terraform CLI init / plan / apply OCI Provider tenancy / user / key OCI API REST 调用 VCN 10.0.0.0/16 Internet Gateway demo-igw Route Table 0.0.0.0/0 → IGW Subnet 10.0.1.0/24 demo-subnet Security List Compute Instance VM.Standard.E4.Flex 1 OCPU / 8 GB Oracle Linux 8 Public IP + SSH Key 挂载 创建 Outputs public_ip / instance_id terraform.tfstate 资源 ID 映射与状态追踪

四、初始化、计划与部署

资源定义写完后,执行标准三步。第一步初始化,下载 Provider 插件并生成锁文件:

terraform init
# 输出中会看到 Installing oracle/oci v6.x.x...

第二步用 plan 预览将要创建的资源。这一步不会真正调用创建接口,但会读取现有状态并计算差异。重点关注输出末尾的 Plan: 5 to add, 0 to change, 0 to destroy,确认数量与预期一致:

terraform plan -out=tfplan
# Plan: 5 to add, 0 to change, 0 to destroy.

第三步应用。使用 -out 保存的计划文件可以保证 apply 的内容与 plan 完全一致:

terraform apply tfplan
# Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
# Outputs:
# public_ip = "203.0.113.45"

部署完成后,在 OCI 控制台进入对应 compartment,可以看到 demo-vcn、demo-subnet、demo-vm 等资源。用输出的公网 IP 尝试 SSH:

ssh opc@203.0.113.45
# 登录成功即说明网络路由、安全列表和实例均正常

五、结果判断与清理

判断部署是否成功,可以看三个层面:Terraform 输出是否显示 Apply complete;OCI 控制台资源列表是否出现对应 display_name;SSH 是否能连通。如果 SSH 超时,优先检查子网是否关联了正确的路由表、安全列表是否放行 22 端口、实例是否分配了公网 IP。这三个点覆盖了绝大多数首次部署失败的原因。

实验结束后务必执行 terraform destroy,否则计算实例会持续计费。destroy 会依据 state 文件逆序删除资源,VCN 和子网也会一并清理。
terraform destroy
# Destroy complete! Resources: 5 destroyed.