📑

モジュールを意識したTerraform構成でColabのテンプレートを作成してみた

に公開

今回は、昨日公開したColab Enterpriseのテンプレート作成のTerraformをモジュールを意識した構成で実装してみます。今までの記事ではルートディレクトリだけでmain.tfvariables.tfを管理していましたが、実際に利用するときはモジュールを意識した構成が必要になるので、今回からその構成を意識して実装します。

https://zenn.dev/akasan/articles/2966eb1a9f9a38

ディレクトリ構成

今回は以下の構成でディレクトリを編成しました。

main.tf
variables.tf
modules/
  colab-template/
     main.tf
     variables.tf
   vpc
     main.tf 
     variables.tf
     outputs.tf

コード実装

今回はルートディレクトリでproviderの定義やプロジェクトIDとリージョンの設定をします。モジュール内ではそれ以外の変数の定義とリソースの定義をするようにしてみます。

ルートディレクトリ

まずはルートディレクトリのファイルから編集します。最初にvariables.tfにてプロジェクトIDとリージョンを設定します。

variables.tf
variable "project_id" {
  description = "The Google Cloud project ID"
  type        = string
}

variable "region" {
  description = "The Google Cloud region"
  type        = string
  default     = "asia-northeast1"
}

次にmain.tfにプロバイダの設定とモジュールの参照設定をします。なお、プロジェクトIDとリージョンをルートディレクトリで指定するようにしているため、モジュールに伝えるためにモジュール参照設定ないで両者の指定をしています。

main.tf
provider "google" {
  project = var.project_id
  region  = var.region
}

module "vpc" {
  source = "./modules/vpc"

  project_id = var.project_id
  region = var.region
}

module "colab-template" {
  source = "./modules/colab-template"

  project_id = var.project_id
  region = var.region

  network_id = module.vpc.network_id
  subnetwork_id = module.vpc.subnetwork_id
}

まずはVPCリソースを作成するモジュールを参照しています。次にColab Enterprise テンプレートを作成するモジュールを作成しています。テンプレートの作成にはVPCリソースの情報が必要なので、VPCモジュールの出力からネットワークIDとサブネットワークIDを受け取るようにしています。

VPCモジュールの実装

まずはvariables.tfから実装します。

modules/vpc/variables.tf
variable "project_id" {
  description = "The Google Cloud project ID"
  type        = string
}

variable "region" {
  description = "The Google Cloud region"
  type        = string
}

variable "vpc_network" {
  description = "VPC network"
  type = object({
    network = string
    subnetwork = string
  })
  default = {
    network = "default"
    subnetwork = "default-asia-northeast1"
  }
}

次にリソース定義をmain.tfに実装します。

modules/vpc/main.tf
provider "google" {
  project = var.project_id
  region  = var.region
}

resource "google_compute_network" "my_network" {
  name = "colab-test-default"
  auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "my_subnetwork" {
  name   = "colab-test-default"
  network = google_compute_network.my_network.id
  region = var.region
  ip_cidr_range = "10.0.1.0/24"
}

最後に、ネットワークIDとサブネットワークIDをoutputs.tfに実装します。outputs.tfに実装することでColab Enterpriseテンプレートモジュールへの入力のための参照を作成します。

modules/vpc/outputs.tf
output "network_id" {
  description = "The ID of the created VPC network."
  value       = google_compute_network.my_network.id
}

output "subnetwork_id" {
  description = "The ID of the created VPC subnetwork."
  value       = google_compute_subnetwork.my_subnetwork.id
}

Colab Enterpriseテンプレートモジュールの実装

それでは次にモジュールを実装していきます。基本的に昨日公開した内容と同じですが、VPCネットワークの参照部分が異なります。network_idsubnetwork_idはVPCモジュールからの出力を与えるためデフォルト値を省略しています。

まずはvariables.tfを実装します。

modules/colab-template/variables.tf
variable "project_id" {
  description = "The Google Cloud project ID"
  type        = string
}

variable "region" {
  description = "The Google Cloud region"
  type        = string
}

# Colab Enterprise variables
variable "colab_enterprise_display_name" {
  description = "Colab Enterprise display name"
  type        = string
  default     = "sample-template"
}

variable "colab_enterprise_machine_type" {
  description = "Colab Enterprise machine type"
  type        = string
  default     = "n2-standard-4"
}

variable "data_persistent_disk_spec" {
  description = "Data persistent disk spec"
  type = object({
    disk_type    = string
    disk_size_gb = number
  })
  default = {
    disk_type    = "pd-standard"
    disk_size_gb = 100
  }
}

variable "network_id" {
  description = "Network ID"
  type        = string
}

variable "subnetwork_id" {
  description = "Subnetwork ID"
  type        = string
}

variable "idle_timeout" {
  description = "Idle timeout"
  type        = string
  default     = "600s"
}

次にmain.tfを実装します。VPCに関する記述は別モジュールに切り出しているため、こちらの実装では省略されています。

modules/colab-template/main.tf
provider "google" {
  project = var.project_id
  region  = var.region
}

resource "google_compute_network" "my_network" {
  name = "colab-test-default"
  auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "my_subnetwork" {
  name   = "colab-test-default"
  network = google_compute_network.my_network.id
  region = var.region
  ip_cidr_range = "10.0.1.0/24"
}

resource "google_colab_runtime_template" "runtime-template" {
  name         = "colab-runtime-template"
  display_name = var.colab_enterprise_display_name
  location     = var.region

  machine_spec {
    machine_type = var.colab_enterprise_machine_type
  }

  data_persistent_disk_spec {
    disk_type    = var.data_persistent_disk_spec.disk_type
    disk_size_gb = var.data_persistent_disk_spec.disk_size_gb
  }
  network_spec {
    enable_internet_access = true
    network = var.network_id
    subnetwork = var.subnetwork_id
  }

  idle_shutdown_config {
    idle_timeout = var.idle_timeout
  }

  euc_config {
    euc_disabled = false
  }
}

リソース作成

それではterraform planを実行してリソース作成プランが適切に作成されるか確認してみます。以下が結果になります。

terraform plan

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.colab-template.google_colab_runtime_template.runtime-template will be created
  + resource "google_colab_runtime_template" "runtime-template" {
      + display_name     = "sample-template"
      + effective_labels = {
          + "goog-terraform-provisioned" = "true"
        }
      + id               = (known after apply)
      + labels           = (known after apply)
      + location         = "asia-northeast1"
      + name             = "colab-runtime-template"
      + project          = "project_id"
      + terraform_labels = {
          + "goog-terraform-provisioned" = "true"
        }

      + data_persistent_disk_spec {
          + disk_size_gb = "100"
          + disk_type    = "pd-standard"
        }

      + euc_config {
          + euc_disabled = false
        }

      + idle_shutdown_config {
          + idle_timeout = "600s"
        }

      + machine_spec {
          + accelerator_count = (known after apply)
          + machine_type      = "n2-standard-4"
        }

      + network_spec {
          + enable_internet_access = true
          + network                = (known after apply)
          + subnetwork             = (known after apply)
        }

      + shielded_vm_config (known after apply)

      + software_config (known after apply)
    }

  # module.colab-template.google_compute_network.my_network will be created
  + resource "google_compute_network" "my_network" {
      + auto_create_subnetworks                   = false
      + bgp_always_compare_med                    = (known after apply)
      + bgp_best_path_selection_mode              = (known after apply)
      + bgp_inter_region_cost                     = (known after apply)
      + delete_default_routes_on_create           = false
      + gateway_ipv4                              = (known after apply)
      + id                                        = (known after apply)
      + internal_ipv6_range                       = (known after apply)
      + mtu                                       = (known after apply)
      + name                                      = "colab-test-default"
      + network_firewall_policy_enforcement_order = "AFTER_CLASSIC_FIREWALL"
      + network_id                                = (known after apply)
      + numeric_id                                = (known after apply)
      + project                                   = "project_id"
      + routing_mode                              = (known after apply)
      + self_link                                 = (known after apply)
    }

  # module.colab-template.google_compute_subnetwork.my_subnetwork will be created
  + resource "google_compute_subnetwork" "my_subnetwork" {
      + creation_timestamp         = (known after apply)
      + external_ipv6_prefix       = (known after apply)
      + fingerprint                = (known after apply)
      + gateway_address            = (known after apply)
      + id                         = (known after apply)
      + internal_ipv6_prefix       = (known after apply)
      + ip_cidr_range              = "10.0.1.0/24"
      + ipv6_cidr_range            = (known after apply)
      + ipv6_gce_endpoint          = (known after apply)
      + name                       = "colab-test-default"
      + network                    = (known after apply)
      + private_ip_google_access   = (known after apply)
      + private_ipv6_google_access = (known after apply)
      + project                    = "project_id"
      + purpose                    = (known after apply)
      + region                     = "asia-northeast1"
      + self_link                  = (known after apply)
      + stack_type                 = (known after apply)
      + state                      = (known after apply)
      + subnetwork_id              = (known after apply)

      + secondary_ip_range (known after apply)
    }

  # module.vpc.google_compute_network.my_network will be created
  + resource "google_compute_network" "my_network" {
      + auto_create_subnetworks                   = false
      + bgp_always_compare_med                    = (known after apply)
      + bgp_best_path_selection_mode              = (known after apply)
      + bgp_inter_region_cost                     = (known after apply)
      + delete_default_routes_on_create           = false
      + gateway_ipv4                              = (known after apply)
      + id                                        = (known after apply)
      + internal_ipv6_range                       = (known after apply)
      + mtu                                       = (known after apply)
      + name                                      = "colab-test-default"
      + network_firewall_policy_enforcement_order = "AFTER_CLASSIC_FIREWALL"
      + network_id                                = (known after apply)
      + numeric_id                                = (known after apply)
      + project                                   = "project_id"
      + routing_mode                              = (known after apply)
      + self_link                                 = (known after apply)
    }

  # module.vpc.google_compute_subnetwork.my_subnetwork will be created
  + resource "google_compute_subnetwork" "my_subnetwork" {
      + creation_timestamp         = (known after apply)
      + external_ipv6_prefix       = (known after apply)
      + fingerprint                = (known after apply)
      + gateway_address            = (known after apply)
      + id                         = (known after apply)
      + internal_ipv6_prefix       = (known after apply)
      + ip_cidr_range              = "10.0.1.0/24"
      + ipv6_cidr_range            = (known after apply)
      + ipv6_gce_endpoint          = (known after apply)
      + name                       = "colab-test-default"
      + network                    = (known after apply)
      + private_ip_google_access   = (known after apply)
      + private_ipv6_google_access = (known after apply)
      + project                    = "project_id"
      + purpose                    = (known after apply)
      + region                     = "asia-northeast1"
      + self_link                  = (known after apply)
      + stack_type                 = (known after apply)
      + state                      = (known after apply)
      + subnetwork_id              = (known after apply)

      + secondary_ip_range (known after apply)
    }

Plan: 5 to add, 0 to change, 0 to destroy.
─────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't
guarantee to take exactly these actions if you run "terraform apply" now.

結果を確認したところ、昨日作成したplan結果と同じでした。なお、実行結果も昨日の結果と同じものが再現でいていることを確認しました。

まとめ

今回は、モジュール構成を意識したTerraformの書き方を試してみました。今までは単独のサービスのみ扱っていたためモジュールを意識してなかったですが、今後大規模なサービスを作るとなるとモジュール化が必須になるので、今回調べてよかったです。ぜひ皆さんもモジュール化したことない方は試してみてください!

Discussion