💨

GCPのTerraform チュートリアルをやってみた

2023/06/07に公開

はじめに

はじめまして。今年の1月にsweeepに入社した香下です。

Googleが提供しているGCPのTerraformのチュートリアル(https://cloud.google.com/docs/terraform/get-started-with-terraform?hl=ja)をやってみたので、学びの記録としてアウトプットしてみようと思います。

そもそもTerraformとは何なのか

そこからかい!と言われそうですが、なにぶんインフラ周りは不勉強なので、
とりあえずchatGPTに聞いてみました。

Terraformは、クラウドインフラストラクチャをコードで管理するためのオープンソースのツールです。つまり、インフラストラクチャの構築や変更をコードとして記述し、実行することができます。

伝統的には、インフラストラクチャを構築するためには手動で手順を実行する必要がありました。しかし、手動の作業では環境の一貫性や再現性が確保しづらく、手間やエラーのリスクも高まります。

Terraformを使用することで、以下のようなメリットがあります:

  1. インフラストラクチャのコード化: Terraformでは、HCL(HashiCorp Configuration Language)という専用の言語を使用して、インフラストラクチャの構成をコードとして記述します。これにより、インフラストラクチャの設定やリソースの関係性をコードで表現できます。

  2. インフラストラクチャのバージョン管理: Terraformのコードは通常のソースコードと同様にバージョン管理システムで管理できます。これにより、変更履歴の追跡やバージョン間の比較、バージョンの切り替えなどが容易になります。

  3. クロスプラットフォーム対応: Terraformは、主要なクラウドプロバイダ(AWS、Azure、Google Cloudなど)やオンプレミスのインフラストラクチャ(VMware、OpenStackなど)をサポートしています。同じTerraformコードを使用して異なるプロバイダ間でインフラストラクチャを管理できるため、ハイブリッドクラウド環境での利用が容易です。

  4. インフラストラクチャのプランニングと適用: Terraformは、実際のインフラストラクチャの状態と目的の状態を比較し、必要な変更を計画(プラン)として提供します。プランを確認した後、変更を適用(apply)することで、インフラストラクチャを自動的に構築または変更できます。

Terraformを使うことで、インフラストラクチャの作成や変更を効率化し、一貫性のある環境を確保できます。また、チーム全体で共有されるコードにより、コラボレーションやベストプラクティスの共有が容易になります。

ありがとうChatGPT、よくわかりました。

というわけでチュートリアルを進めていきたいと思います。

チュートリアル

Compute Engine VM を作成する

https://cloud.google.com/docs/terraform/get-started-with-terraform?hl=ja#create-vm)

main.tfというファイルを作成し、その中にリソースの構成情報を書いていきます。
一つのtfファイルに全ての構成情報を定義することもできてしまうのですが、実際に運用で用いられる際には分離して管理することが一般的だそうです。ググってみたらどのようなファイル構成・ディレクトリ構成のベストプラクティスの記事がいっぱい出てきたのでこのファイル分割についてはかなり奥が深そう。

VPCネットワークとサブネットの定義

まずはVPCネットワークとサブネットをファイルに追記します。
このVPCネットワークがないとCompute Engine VMに接続することができないのでVMを作成する際にはセットで作成する必要がある、ということでしょうか?(雑な理解)
VPC ネットワークについての正確な説明はこちら(https://cloud.google.com/vpc/docs/vpc?hl=ja

main.tfファイルに以下のようにリソースのVPCネットワークとサブネットの情報を書きます。

resource "google_compute_network" "vpc_network" {
  name                    = "my-custom-mode-network"
  auto_create_subnetworks = false
  mtu                     = 1460
}

resource "google_compute_subnetwork" "default" {
  name          = "my-custom-subnet"
  ip_cidr_range = "10.0.1.0/24"
  region        = "us-west1"
  network       = google_compute_network.vpc_network.id
}

ComputeEngine VMの定義

同じようにComputeEngine VMのリソースもmain.tfに定義します。以下の通り

resource "google_compute_instance" "default" {
  name         = "flask-vm"
  machine_type = "f1-micro"
  zone         = "us-west1-a"
  tags         = ["ssh"]

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-11"
    }
  }

  # Install Flask
  metadata_startup_script = "sudo apt-get update; sudo apt-get install -yq build-essential python3-pip rsync; pip install flask"

  network_interface {
    subnetwork = google_compute_subnetwork.default.id

    access_config {
      # Include this section to give the VM an external IP address
    }
  }
}

Terraformの構成初期化

次にTerraformの構成を初期化します。
以下のコマンドを叩くだけ。

terraform init

「構成の初期化」というのがどういうことなのかまだよく理解できていないのですが、プラグインのインストールなどが実行されるそうです。
コマンドの詳細な説明についてはこちら
https://developer.hashicorp.com/terraform/cli/commands/init

構成を適用する

次に構成を検証します。
tfファイルの構文チェックと、作成されるリソースの確認ができます。
以下のコマンドを実行

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:

  # google_compute_instance.default will be created
  + resource "google_compute_instance" "default" {
      + can_ip_forward          = false
      + cpu_platform            = (known after apply)
      + current_status          = (known after apply)
      + deletion_protection     = false
      + guest_accelerator       = (known after apply)
      + id                      = (known after apply)
      + instance_id             = (known after apply)
      + label_fingerprint       = (known after apply)
      + machine_type            = "f1-micro"
      + metadata_fingerprint    = (known after apply)
      + metadata_startup_script = "sudo apt-get update; sudo apt-get install -yq build-essential python3-pip rsync; pip install flask"
      + min_cpu_platform        = (known after apply)
      + name                    = "flask-vm"
      + project                 = (known after apply)
      + self_link               = (known after apply)
      + tags                    = [
          + "ssh",
        ]
      + tags_fingerprint        = (known after apply)
      + zone                    = "us-west1-a"

      + boot_disk {
          + auto_delete                = true
          + device_name                = (known after apply)
          + disk_encryption_key_sha256 = (known after apply)
          + kms_key_self_link          = (known after apply)
          + mode                       = "READ_WRITE"
          + source                     = (known after apply)

          + initialize_params {
              + image  = "debian-cloud/debian-11"
              + labels = (known after apply)
              + size   = (known after apply)
              + type   = (known after apply)
            }
        }

      + network_interface {
          + ipv6_access_type   = (known after apply)
          + name               = (known after apply)
          + network            = (known after apply)
          + network_ip         = (known after apply)
          + stack_type         = (known after apply)
          + subnetwork         = (known after apply)
          + subnetwork_project = (known after apply)

          + access_config {
              + nat_ip       = (known after apply)
              + network_tier = (known after apply)
            }
        }
    }

  # google_compute_network.vpc_network will be created
  + resource "google_compute_network" "vpc_network" {
      + auto_create_subnetworks                   = false
      + delete_default_routes_on_create           = false
      + gateway_ipv4                              = (known after apply)
      + id                                        = (known after apply)
      + internal_ipv6_range                       = (known after apply)
      + mtu                                       = 1460
      + name                                      = "my-custom-mode-network"
      + network_firewall_policy_enforcement_order = "AFTER_CLASSIC_FIREWALL"
      + project                                   = (known after apply)
      + routing_mode                              = (known after apply)
      + self_link                                 = (known after apply)
    }

  # google_compute_subnetwork.default will be created
  + resource "google_compute_subnetwork" "default" {
      + creation_timestamp         = (known after apply)
      + external_ipv6_prefix       = (known after apply)
      + fingerprint                = (known after apply)
      + gateway_address            = (known after apply)
      + id                         = (known after apply)
      + ip_cidr_range              = "10.0.1.0/24"
      + ipv6_cidr_range            = (known after apply)
      + name                       = "my-custom-subnet"
      + network                    = (known after apply)
      + private_ip_google_access   = (known after apply)
      + private_ipv6_google_access = (known after apply)
      + project                    = (known after apply)
      + purpose                    = (known after apply)
      + region                     = "us-west1"
      + secondary_ip_range         = (known after apply)
      + self_link                  = (known after apply)
      + stack_type                 = (known after apply)
    }

Plan: 3 to add, 0 to change, 0 to destroy.

ここまできたらあとは構成を適用します。
以下のコマンドを叩いたらtfファイルに定義したリソースがポンッとできあがります。

terraform apply

Google Cloud でウェブサーバーを実行する

カスタム SSH ファイアウォール ルールを追加する

作成したVMに接続するためにはVPCネットワークにファイアウォールルールを設定する必要があります。この設定もtfファイルに定義して、Terraformで管理することができます。

ファイアウォール ルールの設定は以下の通り。

resource "google_compute_firewall" "ssh" {
  name = "allow-ssh"
  allow {
    ports    = ["22"]
    protocol = "tcp"
  }
  direction     = "INGRESS"
  network       = google_compute_network.vpc_network.id
  priority      = 1000
  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["ssh"]
}

これをterraform applyすればVMにSSH接続できるようになります。

VMにSSH接続

GCPのVMインスタンスページからSSH接続できます。
簡単ですね。

Flask アプリをビルドする

app.pyを作成
以下のような簡単なHello cloudするだけの簡単なアプリをデプロイします。

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_cloud():  
    return 'Hello Cloud!'
    
app.run(host='0.0.0.0')

python3 app.pyでFlaskサーバーを起動。

VMでポート5000を開く

Flaskの開発サーバーはデフォルトで5000ポートを使うので、外部からウェブサーバーにアクセスするにはファイアウォールを設定して5000ポートを開く必要があります。このファイアウォールルールの設定もtfファイルに定義できます

resource "google_compute_firewall" "flask" {
  name    = "flask-app-firewall"
  network = google_compute_network.vpc_network.id

  allow {
    protocol = "tcp"
    ports    = ["5000"]
  }
  source_ranges = ["0.0.0.0/0"]
}

ウェブサーバーの URL の出力変数を追加する

main.tfにTerraformの出力変数を追加して、ウェブサーバーのURLを出力されるようにします。

// A variable for extracting the external IP address of the VM
output "Web-server-URL" {
 value = join("",["http://",google_compute_instance.default.network_interface.0.access_config.0.nat_ip,":5000"])
}

今回はURLの確認のために使っていますが、module間の値の受け渡しにもoutputを使うみたいです。
参考:
https://developer.hashicorp.com/terraform/tutorials/configuration-language/outputs

outputを設定した状態でterraform applyすると設定した値が出力されます。
また、terraform outputコマンドでいつでも設定した値を出力させることができます。

出力されたURLにアクセスすると、「Hello Cloud!」のメッセージを確認することができました。

クリーンアップ

terraform destroy コマンドを実行すると、main.tfに定義したリソースが全て削除されます。これでチュートリアルおしまい!

最後に

かなり初歩的な内容になってしまいましたが、個人的にはいい勉強になりました。
公式のドキュメントやチュートリアルも充実しているので、Terraform & GCPは初心者にも勉強しやすい環境が揃っているのかなと思います。今回を機にもっと学習を進めてみようと思いました。

Discussion