3. OCI核心网络的自助化配置
在OCI中,网络是所有计算、数据库、负载均衡与容器资源的“地基”。手工在控制台点选创建VCN、子网、网关和安全规则,短期看似直观,但一旦需要复制到测试、预生产、生产三套环境,或需要按合规要求审计“谁在什么时候改了哪条路由”,手工方式就会迅速失控。网络即代码(Network as Code)的核心,是把网络拓扑的每一个对象都写成可版本化、可评审、可重复执行的Terraform声明,让环境差异只体现在变量文件里,而不是散落在控制台的操作历史中。
一、先理解对象之间的依赖顺序
OCI的VCN不是“一个网络”,而是一组相互引用的资源集合。Terraform之所以适合它,正是因为Terraform会依据引用关系自动推导创建顺序。理解下面这条依赖链,是写出正确配置的前提:
- VCN:定义整体地址空间,例如
10.0.0.0/16,是所有子网和网关的容器。 - 网关:Internet Gateway(IGW)承载公网进出;NAT Gateway 让私有子网主动访问公网但不被外部主动连接;Service Gateway 让私有子网访问OCI对象存储等服务而不走公网。
- 子网:从VCN CIDR中切分,分为公共子网(可关联IGW)和私有子网(关联NAT或Service Gateway)。
- 路由表:决定子网流量“下一跳去哪”,必须引用已存在的网关。
- 安全列表与NSG:安全列表作用于整个子网,NSG作用于具体VNIC,二者共同决定“谁能进来、谁能出去”。
因此,Terraform配置中网关和VCN必须先声明,路由表引用网关,子网再引用路由表和安全列表。下面这张图展示了本节要构建的典型双栈网络拓扑与依赖关系:
二、用变量把“环境差异”抽离出来
直接写死CIDR和名称,等于把配置锁死在一个环境。推荐做法是把环境相关参数放进 terraform.tfvars,主配置只保留结构。例如:
# variables.tf
variable "compartment_id" { type = string }
variable "vcn_cidr" { type = string default = "10.0.0.0/16" }
variable "env" { type = string default = "prod" }
# terraform.tfvars(生产)
compartment_id = "ocid1.compartment.oc1..aaaa..."
vcn_cidr = "10.0.0.0/16"
env = "prod"
# terraform.tfvars(测试)
compartment_id = "ocid1.compartment.oc1..bbbb..."
vcn_cidr = "10.1.0.0/16"
env = "test"
这样同一份 main.tf 通过 -var-file 就能生成两套互不冲突的网络,CIDR不重叠,后续做对等连接或DRG互联时不会踩坑。
三、核心资源声明示例
下面给出VCN、Internet Gateway、NAT Gateway、路由表和两个子网的最小可用声明。注意 route_rules 中 network_entity_id 直接引用网关资源,Terraform会据此建立依赖:
resource "oci_core_vcn" "prod" {
compartment_id = var.compartment_id
cidr_blocks = [var.vcn_cidr]
display_name = "${var.env}-vcn"
dns_label = "${var.env}vcn"
}
resource "oci_core_internet_gateway" "igw" {
compartment_id = var.compartment_id
vcn_id = oci_core_vcn.prod.id
display_name = "${var.env}-igw"
enabled = true
}
resource "oci_core_nat_gateway" "nat" {
compartment_id = var.compartment_id
vcn_id = oci_core_vcn.prod.id
display_name = "${var.env}-nat"
}
resource "oci_core_route_table" "public_rt" {
compartment_id = var.compartment_id
vcn_id = oci_core_vcn.prod.id
display_name = "${var.env}-public-rt"
route_rules {
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = oci_core_internet_gateway.igw.id
}
}
resource "oci_core_route_table" "private_rt" {
compartment_id = var.compartment_id
vcn_id = oci_core_vcn.prod.id
display_name = "${var.env}-private-rt"
route_rules {
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = oci_core_nat_gateway.nat.id
}
}
resource "oci_core_subnet" "public" {
compartment_id = var.compartment_id
vcn_id = oci_core_vcn.prod.id
cidr_block = cidrsubnet(var.vcn_cidr, 8, 1) # 10.0.1.0/24
display_name = "${var.env}-public-subnet"
route_table_id = oci_core_route_table.public_rt.id
security_list_ids = [oci_core_security_list.public_sl.id]
dns_label = "pub"
}
resource "oci_core_subnet" "private_app" {
compartment_id = var.compartment_id
vcn_id = oci_core_vcn.prod.id
cidr_block = cidrsubnet(var.vcn_cidr, 8, 2) # 10.0.2.0/24
display_name = "${var.env}-app-subnet"
route_table_id = oci_core_route_table.private_rt.id
security_list_ids = [oci_core_security_list.private_sl.id]
dns_label = "app"
}
这里用 cidrsubnet(var.vcn_cidr, 8, 1) 从VCN CIDR自动切出 /24 子网,比手写 10.0.1.0/24 更安全——当VCN换成 10.1.0.0/16 时,子网会自动变成 10.1.1.0/24,不会出现子网落在VCN之外的错误。
四、安全列表与NSG的分工
安全列表是子网级的有状态防火墙,适合定义“这个子网整体允许哪些端口”。例如公共子网只放行22、80、443:
resource "oci_core_security_list" "public_sl" {
compartment_id = var.compartment_id
vcn_id = oci_core_vcn.prod.id
display_name = "${var.env}-public-sl"
ingress_security_rules {
protocol = "6" # TCP
source = "0.0.0.0/0"
tcp_options { min = 22 max = 22 }
}
ingress_security_rules {
protocol = "6"
source = "0.0.0.0/0"
tcp_options { min = 443 max = 443 }
}
egress_security_rules {
protocol = "all"
destination = "0.0.0.0/0"
}
}
而NSG更适合“按角色授权”。例如应用子网只接受来自负载均衡NSG的8080流量,数据子网只接受来自应用NSG的1521流量。NSG的规则引用的是另一个NSG的OCID,而不是IP段,这样即使实例IP变化,规则依然有效。实践中建议:子网级用安全列表做粗粒度兜底,实例级用NSG做细粒度角色授权,两者叠加,取允许的并集。
egress 0.0.0.0/0 或指向NAT的规则。五、执行与验证
配置写完后,标准执行链是:
terraform init # 下载 oci provider
terraform validate # 语法与引用检查
terraform plan -var-file=prod.tfvars -out=prod.plan
terraform apply prod.plan
验证不能只看 apply 是否成功。建议按以下顺序确认:
- 在OCI控制台进入VCN详情,确认子网CIDR、路由表关联、安全列表关联与代码一致。
- 启动一台公共子网实例,SSH登录后
curl ifconfig.me,应返回公网IP,说明IGW和公共路由生效。 - 启动一台私有子网实例,执行
curl https://objectstorage.<region>.oraclecloud.com,若能返回响应,说明NAT或Service Gateway路径通。 - 从公网直接SSH私有子网实例应超时,说明私有子网没有公网入口,安全边界正确。
terraform plan 显示要重建子网,通常是因为修改了 cidr_block 或 dns_label,这两项在OCI中不可原地更新。把上述配置纳入Git仓库后,网络变更就变成了Pull Request:评审人可以看到CIDR、路由和安全规则的每一行差异,CI可以自动跑 terraform plan 并把结果贴到PR评论中。这才是“网络即代码”真正带来的价值——不是少点几次鼠标,而是让网络拓扑具备可审计、可回滚、可复制的工程属性。