Phase 1:ネットワーク(VCN)¶
目標¶
- Terraform で最小構成のネットワークを作り、
init → plan → apply → destroyの流れに慣れる - state(状態ファイル)とは何かを理解する:Terraform は「どのリソースを自分が作ったか」をどうやって把握しているのか
ネットワーク・リソース(VCN、ゲートウェイ(Gateway)、ルート表(Route Table)など)はすべて無料なので、練習に最適です。
構成¶
flowchart LR
Internet((Internet)) <--> IGW[インターネット・ゲートウェイ]
subgraph VCN["VCN 10.0.0.0/16"]
IGW --- RT["ルート表<br/>0.0.0.0/0 → IGW"]
RT --- SN["パブリック・サブネット 10.0.1.0/24"]
SL["セキュリティ・リスト<br/>SSH: my_ip / VCN 内部<br/>ICMP 3/4"] --- SN
end
AWS との対応¶
| OCI | AWS | 補足 |
|---|---|---|
| VCN(仮想クラウド・ネットワーク) | VPC | |
| インターネット・ゲートウェイ(Internet Gateway) | Internet Gateway | |
| ルート表(Route Table) | Route Table | |
| セキュリティ・リスト(Security List) | NACL(ただしデフォルトはステートフル) | サブネット(Subnet)単位のファイアウォール |
| NSG(ネットワーク・セキュリティ・グループ) | Security Group | VNIC 単位のファイアウォール。Phase 4 から使用 |
| コンパートメント(Compartment) | (近いもの)Resource Group / アカウント分割 | 権限と費用の境界 |
ポイント¶
参照 = 依存関係¶
他のリソースの属性を参照すると、Terraform は VCN を先に作ってから IGW を作ると判断します。削除時は逆順です。順序を自分で書く必要はありません。
セキュリティ・リストの書き方¶
protocolはプロトコル番号で指定:"6"= TCP、"1"= ICMP、"all"= すべて- SSH はローカル PC の IP だけ許可(
"${var.my_ip}/32") - ICMP type 3 code 4(Path MTU Discovery)は許可しておく必要があります。ないと大きなパケットで問題が起きます。OCI のデフォルトのセキュリティ・リストにもこのルールがあります
タグ¶
すべてのリソースに同じ freeform_tags を付けておくと、Terraform 管理のリソースだとコンソール(GUI)でひと目でわかります。
state とは¶
terraform applyのあとにterraform.tfstateが生成されます。「コード上のリソース ↔ OCI 上の実際の OCID」の対応を記録したものですplanのとき、Terraform は コード ↔ state ↔ 実際のリソース の 3 つを比較します- state をなくすと、Terraform は作成済みのリソースを認識できなくなり、もう 1 セット作ろうとします
- ディレクトリ(ルート・モジュール、root module)ごとに state が 1 つ →
01-networkと02-computeは別々に apply します。作るときは 01 → 02、削除するときは 02 → 01
| plan の記号 | 意味 |
|---|---|
+ |
新規作成 |
~ |
インプレース変更(OCID は変わらない) |
-/+ |
削除して再作成(# forces replacement、OCID が変わる) |
- |
削除 |
補足
リソースがまだ存在しない段階では、コードをどう変えても plan は + しか表示しません。~ や -/+ はリソースを作成したあとにはじめて現れます。
この段階では state をローカルに置いています。複数人での作業や紛失対策の問題は、Phase 3 でリモート state(remote state)を使って解決します。
実際の操作¶
1. まず plan、それから apply¶
cd 01-network
terraform init
terraform plan # → Plan: 5 to add, 0 to change, 0 to destroy.
terraform apply # もう一度 plan が表示され、yes を入力すると実行
plan を見るときのポイント:
- まず最終行を見る
Plan: X to add, Y to change, Z to destroy。想定外の change や destroy があれば、いったん止まります (known after apply):OCID などの値はリソースができるまでわかりません- 出力が長いとき:
terraform plan -no-color > plan.txtで保存してゆっくり確認します
apply の結果は 5 added、作成順は次のとおりでした。
順序は Terraform が参照関係から自動で決めます。削除時はちょうど逆になります。
2. リソースがない状態でコードを変える¶
apply の前に project_name を変えて plan すると、記号は + のまま、display_name の値だけが変わりました。
→ state が空で比較対象がないため、どう変えても「新規作成」になります。~ や -/+ はリソースを作成したあとにしか現れません。
3. ドリフト(drift):コンソールでリソースを手動変更する¶
- コンソールでセキュリティ・リストに Ingress ルールを手動で 1 つ追加(
0.0.0.0/0、TCP 80) -
terraform plan:~ resource "oci_core_security_list" "public" { - ingress_security_rules { - protocol = "6" -> null - source = "0.0.0.0/0" -> null ... } # (3 unchanged blocks hidden) } Plan: 0 to add, 1 to change, 0 to destroy.~= インプレース変更、- ingress_security_rules= コンソールで追加したルールを削除しようとしている旧値 -> null= この値が削除される- Terraform は plan のときにまず実際の状態を読み込み(refresh)、コードと比較します。コードにこのルールがないので、実環境をコードどおりに戻そうとします
-
対処はどちらか一方です。
方法 やり方 今回 手動変更を取り消す terraform apply✅ こちらを選択 → 再度 plan すると No changes変更をコードに取り込む ルールを main.tfに書く → plan がNo changesになる(apply 不要)
IaC の基本原則
変更はすべてコードで行い、コンソールで手動変更はしません。手動で変えても、次の apply でコードの内容に戻されます。
4. destroy と再作成¶
terraform destroy # 5 to destroy であることを確認してから yes を入力
terraform state list # → 空
terraform apply # 作り直す(Phase 2 の VM をこのサブネットに置くため)
作り直すと OCID はすべて変わります。Terraform が管理しているのは「コード上のアドレス ↔ OCID」の対応関係で、OCID 自体は毎回新しくなります。
コード全体¶
01-network/main.tf
# ------------------------------------------------------------
# Phase 1:ネットワークのみ作成(VCN と関連ネットワーク・リソースはすべて無料)
# VCN ─ Internet Gateway
# └─ Public Subnet ─ Route Table(0.0.0.0/0 → IGW)
# └─ Security List(SSH はローカル PC の IP のみ許可)
# ------------------------------------------------------------
locals {
# 全リソース共通のタグ。コンソール(GUI)で絞り込み、Terraform 管理のリソースか確認しやすくする
common_tags = {
project = "tf-lab"
managed_by = "terraform"
}
}
# VCN(仮想クラウド・ネットワーク):AWS の VPC に相当
resource "oci_core_vcn" "main" {
compartment_id = var.compartment_ocid
cidr_blocks = [var.vcn_cidr]
display_name = "${var.project_name}-vcn"
dns_label = var.project_name # 英数字のみ、先頭は英字、15 文字以内
freeform_tags = local.common_tags
}
# インターネット・ゲートウェイ(Internet Gateway):パブリック・サブネット(Public Subnet)からインターネットへの出入口
resource "oci_core_internet_gateway" "igw" {
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.main.id # 他リソースの属性を参照 → Terraform が作成順序を自動判断
display_name = "${var.project_name}-igw"
enabled = true
freeform_tags = local.common_tags
}
# ルート表(Route Table):デフォルト・ルートを IGW に向ける
resource "oci_core_route_table" "public" {
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.main.id
display_name = "${var.project_name}-rt-public"
route_rules {
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = oci_core_internet_gateway.igw.id
}
freeform_tags = local.common_tags
}
# セキュリティ・リスト(Security List):サブネット(Subnet)単位のファイアウォール
resource "oci_core_security_list" "public" {
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.main.id
display_name = "${var.project_name}-sl-public"
# アウトバウンド:すべて許可
egress_security_rules {
destination = "0.0.0.0/0"
protocol = "all"
}
# インバウンド:SSH はローカル PC の IP のみ許可(protocol はプロトコル番号で指定:6=TCP, 1=ICMP)
ingress_security_rules {
source = "${var.my_ip}/32"
protocol = "6"
tcp_options {
min = 22
max = 22
}
}
# インバウンド:SSH を VCN 内部から許可(Cloud Shell の Private Network はこの VCN 内のプライベート IP を取得する)
ingress_security_rules {
source = var.vcn_cidr
protocol = "6"
description = "SSH from inside VCN (Cloud Shell private network)"
tcp_options {
min = 22
max = 22
}
}
# インバウンド:ICMP type 3 code 4(Path MTU Discovery に必要。OCI のデフォルト・セキュリティ・リストにも含まれる)
ingress_security_rules {
source = "0.0.0.0/0"
protocol = "1"
icmp_options {
type = 3
code = 4
}
}
freeform_tags = local.common_tags
}
# パブリック・サブネット:リージョナル・サブネット(Regional Subnet。可用性ドメイン(Availability Domain)を指定せず、AD をまたいで使用可能)
resource "oci_core_subnet" "public" {
compartment_id = var.compartment_ocid
vcn_id = oci_core_vcn.main.id
cidr_block = var.public_subnet_cidr
display_name = "${var.project_name}-subnet-public"
dns_label = "public"
route_table_id = oci_core_route_table.public.id
security_list_ids = [oci_core_security_list.public.id]
prohibit_public_ip_on_vnic = false # false = パブリック IP の割当てを許可(パブリック・サブネット)
freeform_tags = local.common_tags
}