🐡

Oracle Cloud Infrastructure を Terraform 管理できるようにするまで

2025/01/12に公開

schnell と申します. 現在 SaaS 系企業で MLOps を推進するエンジニアとして勤務しています.
本記事では、自主学習の一環として取り組んでいる Oracle Cloud Infrastructure(以下、OCI)の環境を Terraform で管理するための手順について、備忘録として共有いたします.

OCI は AWS や GCP といった有名 IaaS サービスと比べると利用社が少なく地味ですが無料枠が非常に充実しており
例えば、AWSのEC2に相当するコンピューティングリソースでは Arm 版を選択することで RAM 24GB、4 OPCU のインスタンスが無料で利用できます.

このリソースは最大 4 つの VM に分割可能で、無料枠の範疇で RAM 6GB、1 OCPU のインスタンスを4台作成することができ, 簡易的な kubernetes クラスタでさえも運用することが十分に可能なスペックとなっています.

尚、本記事ではおそらく最難関であろう OCI にサインアップするというプロセスについては省略します。

実行環境について

macOS Sonoma で実行確認をしております.

❯ sw_vers
ProductName:            macOS
ProductVersion:         14.6.1
BuildVersion:           23G93

terraform は 1.10.4 を利用します.

❯ terraform -v
Terraform v1.10.4
on darwin_arm64

API キーの追加

プログラム (terraform) から OCI にアクセスするために API キーの登録を行います.
以下のコマンドをを実行することで、API キーとして登録する RSA キーを作成して、公開鍵をクリップボードにコピーできます.

mkdir ~/.oci && cd ~/.oci
openssl genrsa -out oci_rsa.pem 2048
chmod 600 oci_rsa.pem
openssl rsa -pubout -in oci_rsa.pem -out oci_rsa_pub.pem
cat oci_rsa_pub.pem | pbcopy

次にコンソールにログインして API キーの登録を行います.

こちらのリンクに飛んでいただき、「API キーの追加」->「公開キーの貼付け」を選択して先ほどクリップボードにコピーした公開鍵を追加してください.

以上で API キーの追加は完了です.

terraform の oci provider 設定に必要な情報を集める

OCI のコンソールから以下の情報を集めます

  1. Tenancy OCID
    ここから取得してください.
    ocid1.tenancy.oc1 から始まります.

  2. User OCID
    ここから取得してください.
    ocid1.user.oc1 から始まります.

  3. Fingerprint
    ここから取得してください.
    xx:xx:xx...xx のようなフォーマットです.

  4. Region
    東京リージョンなら ap-tokyo-1、大阪リージョンなら ap-osaka-1 です.

terraform ファイルを実行する環境から以下の情報を取得します.

  1. Private Key Path
    先ほど作成した秘密鍵のパスをメモしてください、記事の通りに実行した場合は ~/.oci/oci_rsa.pem となります.

terraform のプロバイダー設定

先ほど取得した情報を使って oci provider の設定をします.
設定情報を保存する ~/.oci/config を作成して以下のように記載してください

~/.oci/config
[DEFAULT]
tenancy_ocid="<tenancy-ocid>"
user_ocid="<user-ocid>"
private_key_path="<rsa-private-key-path>"
fingerprint="<fingerprint>"
region="<region-identifier>"

次に以下のような hcl コードを作成してください.

provider.tf
terraform {
  required_providers {
    oci = {
      source = "oracle/oci"
    }
  }
}

provider "oci" {
  config_file_profile = "DEFAULT"
}

terraform init コマンドを実行して以下のように表示されたら設定完了です.

❯ terraform init
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of oracle/oci from the dependency lock file
- Using previously-installed oracle/oci v4.123.0

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

可用性ドメインを出力してみる

可用性ドメインを取得するためのシンプルな hcl を作成しました.

terraform {
  required_providers {
    oci = {
      source = "oracle/oci"
    }
  }
}

provider "oci" {
  config_file_profile = "DEFAULT"
}

locals {
  oci_config_content = file("~/.oci/config")
  tenancy_ocid       = regex("(?m)^tenancy\\s*=\\s*(.*)$", local.oci_config_content)[0]
}

data "oci_identity_availability_domains" "ads" {
  compartment_id = local.tenancy_ocid
}

output "names" {
  value = [for ad in data.oci_identity_availability_domains.ads.availability_domains : ad.name]
}
実行結果
❯ terraform apply
data.oci_identity_availability_domains.ads: Reading...
data.oci_identity_availability_domains.ads: Read complete after 0s [id=IdentityAvailabilityDomainsDataSource-1575237231]

Changes to Outputs:
  + names = [
      + "ewah:AP-OSAKA-1-AD-1",
    ]

You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes


Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

names = [
  "ewah:AP-OSAKA-1-AD-1",
]

(おまけ) nginx サーバーをシンプルな構成でデプロイしてみる

最小限のシンプルな構成で nginx サーバーをデプロイしてみます.
iptables -I INPUT -p TCP --dport 80 -j ACCEPT を実行している点がポイントで、OCI ではデフォルトで OS のファイアウォールが設定されているため明示的に許可する必要があります.
terraform apply すると出力される URL にアクセスすると nignx サーバーにアクセスできることが確認できます.

また、ssh の設定もしているので ssh ubuntu@<ip addr> -i ~/.ssh/<key name> でインスタンスに ssh で接続することもできます.

terraform ファイル
terraform {
  required_providers {
    oci = {
      source = "oracle/oci"
    }
  }
}

provider "oci" {
  config_file_profile = "DEFAULT"
}

locals {
  oci_config_content = file("~/.oci/config")
  tenancy_ocid       = regex("(?m)^tenancy\\s*=\\s*(.*)$", local.oci_config_content)[0]
}

# コンパートメントを作成
resource "oci_identity_compartment" "this" {
  compartment_id = local.tenancy_ocid
  name           = "terraform"
  description    = "Terraform compartment"
}

# 可用性ドメインを取得
data "oci_identity_availability_domains" "this" {
  compartment_id = local.tenancy_ocid
}

# VCN を作成
resource "oci_core_vcn" "this" {
  cidr_block     = "10.0.0.0/16"
  compartment_id = oci_identity_compartment.this.id
  display_name   = "nginx-vcn"
}

# IGW を作成
resource "oci_core_internet_gateway" "this" {
  compartment_id = oci_identity_compartment.this.id
  display_name   = "nginx-igw"
  vcn_id         = oci_core_vcn.this.id
}

# ルートテーブルを作成
resource "oci_core_route_table" "this" {
  compartment_id = oci_identity_compartment.this.id
  vcn_id         = oci_core_vcn.this.id
  display_name   = "nginx-rt"

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

# サブネットを作成
resource "oci_core_subnet" "this" {
  cidr_block        = "10.0.1.0/24"
  compartment_id    = oci_identity_compartment.this.id
  vcn_id            = oci_core_vcn.this.id
  route_table_id    = oci_core_route_table.this.id
  security_list_ids = [oci_core_security_list.nginx_security_list.id]
  display_name      = "nginx-subnet"
}

# セキュリティリストの更新
resource "oci_core_security_list" "nginx_security_list" {
  compartment_id = oci_identity_compartment.this.id
  vcn_id         = oci_core_vcn.this.id
  display_name   = "nginx-security-list"

  egress_security_rules {
    destination = "0.0.0.0/0"
    protocol    = "all"
  }

  ingress_security_rules {
    protocol = "6" # TCP
    source   = "0.0.0.0/0"

    tcp_options {
      max = "22"
      min = "22"
    }
  }

  ingress_security_rules {
    protocol = "6" # TCP
    source   = "0.0.0.0/0"
    tcp_options {
      max = "80"
      min = "80"
    }
  }
}

# Compute インスタンスを作成
resource "oci_core_instance" "nginx_instance" {
  availability_domain = data.oci_identity_availability_domains.this.availability_domains[0].name
  compartment_id      = oci_identity_compartment.this.id
  shape               = "VM.Standard.A1.Flex"

  shape_config {
    ocpus         = 1
    memory_in_gbs = 6
  }

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

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

  metadata = {
    ssh_authorized_keys = file("~/.ssh/oci-nginx.pub") # 公開鍵は事前に作成しておく
    user_data = base64encode(<<-EOF
    #!/bin/bash
    apt-get update
    apt-get install -y nginx
    systemctl start nginx
    systemctl enable nginx
    iptables -I INPUT -p TCP --dport 80 -j ACCEPT
    EOF
    )
  }
}

data "oci_core_images" "ubuntu_image" {
  compartment_id           = oci_identity_compartment.this.id
  operating_system         = "Canonical Ubuntu"
  operating_system_version = "24.04"
  shape                    = "VM.Standard.A1.Flex"
}

output "public_ip" {
  value = "http://${oci_core_instance.nginx_instance.public_ip}"
}

参考記事

GitHubで編集を提案

Discussion