このシリーズでは、コードとしてのインスフラストラクチャ (IaC) を導入する方法について説明していきます。開発者の皆さんに IaC のコンセプトをしっかりご理解いただけるよう、チュートリアルやコード サンプルを交えて作成しました。各記事では以下のトピックについて取り上げます。

IaC の概要

IaC は、最新の継続的インテグレーション パイプラインに欠かせないパーツであり、コンピューターで読み取ることができる定義ファイルを通じてクラウドや IT リソースを管理、プロビジョニングするプロセスのことです。IaC では、コード内にコンピューター リソースを静的に定義、宣言することにより、最新の DevOps ツールを使用してこれらのリソースを作成、管理、破棄することができます。

今回の記事では、HashiCorp の Terraform を使用してインフラストラクチャ リソースのプロビジョニング、デプロイ、破棄を行う方法について説明していきます。その前に、使用するクラウドプロバイダーやサービス (Google CloudTerraform Cloud) のアカウントを作成しておきましょう。以下の前提条件を満Kubernetes Engine (GKE) クラスタを作成していきます。

前提条件

事前に以下の手順を完了しておく必要があります。

今回は、learn_iac リポジトリpart01 ディレクトリ内のコードを使用します。その前に、GCP 認証情報の作成方法と Terraform について簡単に確認しておきましょう。

GCP プロジェクト認証情報の作成

IoC ツールを使用して管理操作を行うには、GCP 認証情報を作成する必要があります。

  • サービス アカウントキーの作成ページ を開きます。
  • 既定のサービス アカウントを選択するか、新しいものを作成します。
  • キータイプとして JSON を選択します。
  • [作成]をクリックします。
  • JSON ファイルを ~/.config/gcloud/ ディレクトリに保存します。ファイル名は任意の名前に変更できます。

HashiCorp Terraform

HashiCorp Terraform は、インフラストラクチャの作成、変更、バージョン管理を安全かつ効果的に行うためのオープンソース ツールです。Terraform を使用すると、既存のサービスプロバイダーに加え、カスタムの自社ソリューションも管理できます。

Terraform では、単一のアプリケーションまたはデータセンター全体を実行するために必要なコンポーネントを設定ファイルに記述します。また、目的のステートに達するために実行する内容が記載された実行プランを生成し、これを実行して、記述どおりのインフラストラクチャを構築します。設定が変更された場合、Terraform は変更内容を特定し、増分の実行プランを生成します。この実行プランを適用すると、インフラストラクチャ リソースが更新されます。

Terraform で管理できるインフラストラクチャには、コンピューティング インスタンス、ストレージ、ネットワーキングのような低レベルのコンポーネントだけでなく、DNS エントリや SaaS 機能のような高レベルのコンポーネントも含まれます。

Terraform プロバイダー

Terraform コードを使用して、GCP にリソースをプロビジョニングしてみましょう。本シリーズのパート 2 で使用する GKE クラスタを定義し、作成するための Terraform コードを記述する必要があります。Terraform を使用すれば、物理マシン、VM、ネットワーク スイッチ、コンテナなどのインフラストラクチャ リソースを作成、管理、更新することができます。Terraform では、ほとんどすべてのインフラストラクチャ タイプをリソースとして表現できます。

プロバイダーは、API のインタラクションを把握し、リソースを見えるようにする責任を負っています。一般に、プロバイダーとは、IaaS (Alibba Cloud、AWS、GCP、Microsoft Azure、OpenStack など)、PaaS (Heroku など)、SaaS サービス (Terraform Cloud、DNSimple、Cloudflare など) のいずれかです。

GKE クラスタを新規作成するにあたって、GCP とのやり取りには GCP プロバイダーを使用する必要があります。プロバイダーの定義と構成が完了すると、GCP 上で Terraform リソースを作成し、管理できるようになります。

Terraform リソース

リソースは、Terraform 言語の中で最も重要な要素です。リソースブロックごとに 1 つ以上のインフラストラクチャ オブジェクト (仮想ネットワーク、コンピューティング インスタンス、DNS レコードのような高レベルのコンポーネントなど) が記載されています。リソースブロックには、リソースのタイプ (例: google_container_cluster) とローカル名 (例: “web”) が宣言されます。ローカル名は、同じ Terraform モジュール内の他の場所からこのリソースを参照するときに使用します。モジュールのスコープ外では意味を持ちません。

Terraform コード

ここまでの説明で、Terraform のプロバイダーリソースについての理解が深まったと思います。いくつかのコードを例に、さらに詳しく見ていきましょう。Terraform コードは複数のディレクトリで管理されます。CLI ツールを使用するので、コードが格納されているルート ディレクトリ内でコマンドを実行する必要があります。本シリーズのパート 1 で使用する Terraform コードは、こちらpart01/iac_gke_cluster ディレクトリで確認できます。このディレクトリ内には、以下のファイルが格納されています。

  • providers.tf
  • variables.tf
  • main.tf
  • output.tf

上記のファイルはこれから作成する GCP インフラストラクチャを表しています。Terraform はこれらのファイルを処理するわけです。すべての Terraform コードを 1 つのファイルに収めることもできますが、構文のボリュームが大きくなると管理が難しくなる傾向があるため、多くの Terraform 開発者が要素ごとに別々のファイルを作成しています。それでは、各ファイルの内容と重要な要素について簡単に確認していきましょう。

解説: providers.tf

provider.tf ファイルでは、使用するクラウド プロバイダーを定義します。この例では、google_container_cluster プロバイダーを使用するため、provider.tf ファイルは以下のような内容になります。

provider "google" {
  # version     = "2.7.0"
  credentials = file(var.credentials)
  project     = var.project
  region      = var.region
}

上記のコード ブロックでは、クロージャ { } ブロック内に複数のパラメーターが記述されています。credentials ブロックには、先ほど作成した GCP 認証情報の JSON ファイルのファイル パスを指定します。ここでパラメーターの値の先頭に var というプレフィックスが付いている点に注目してください。var プレフィックスは、Terraform モジュールのパラメーターとなる Terraform 入力変数の使い方を定義するものです。これにより、モジュールのソース コードを変更せずにモジュールをカスタマイズすることや、複数の構成でモジュールを共有することが可能になります。構成のルート モジュールに変数を宣言した場合、CLI オプションと環境変数を使用して値を設定できます。子モジュールに変数を宣言した場合、呼び出しモジュールがモジュール ブロック内の値を渡します。

解説: variables.tf

variables.tf ファイルには、この Terraform プロジェクトで使用するすべての入力変数を指定します。

variable "project" {
  default = "cicd-workshops"
}

variable "region" {
  default = "us-east1"
}

variable "zone" {
  default = "us-east1-d"
}

variable "cluster" {
  default = "cicd-workshops"
}

variable "credentials" {
  default = "~/.ssh/cicd_demo_gcp_creds.json"
}

variable "kubernetes_min_ver" {
  default = "latest"
}

variable "kubernetes_max_ver" {
  default = "latest"
}

上記のファイルに定義されている変数は、このプロジェクト全体を通じて使用されます。これらすべての変数には default 値がありますが、Terraform コードの実行時に CLI を使って定義することで変更可能です。これらの変数を使用することでコードに求められる柔軟性が確保され、変数コードを再利用できるようになります。

解説: main.tf

main.tf ファイルでは、GKE クラスタ パラメーターを一括定義できます。

terraform {
  required_version = "~>0.12"
  backend "remote" {
    organization = "datapunks"
    workspaces {
      name = "iac_gke_cluster"
    }
  }
}

resource "google_container_cluster" "primary" {
  name               = var.cluster
  location           = var.zone
  initial_node_count = 3

  master_auth {
    username = ""
    password = ""

    client_certificate_config {
      issue_client_certificate = false
    }
  }

  node_config {
    machine_type = var.machine_type
    oauth_scopes = [
      "https://www.googleapis.com/auth/logging.write",
      "https://www.googleapis.com/auth/monitoring",
    ]

    metadata = {
      disable-legacy-endpoints = "true"
    }

    labels = {
      app = var.app_name
    }

    tags = ["app", var.app_name]
  }

  timeouts {
    create = "30m"
    update = "40m"
  }
}

main.tf ファイルの要素を 1 つずつ解説していきたいと思います。terraform ブロックから始めましょう。このブロックでは Terraform バックエンドのタイプを指定します。Terraform の「バックエンド」によって、ステートを読み込む方法や、apply などのオペレーションの実行方法が決定されます。この抽象化により、非ローカルのファイル ステート ストレージやリモート実行が使用可能になります。このコードブロックでは、Terraform Cloud を使用する remote バックエンドを使用します。このバックエンドは、事前の準備で作成した iac_gke_cluster ワークスペースに接続しています。

terraform {
  required_version = "~>0.12"
  backend "remote" {
    organization = "datapunks"
    workspaces {
      name = "iac_gke_cluster"
    }
  }
}

次のコード ブロックでは、これから作成する GKE クラスタを定義します。variables.tf に定義されているいくつかの変数も使用します。resource ブロックには、GCP で GKE クラスタのプロビジョニングと構成に使用されるパラメーターが多数含まれています。ここで重要なパラメーターは namelocation、そして initial_node_count です。initial_node_count では、新しいクラスタを構成するコンピューティング リソースまたは仮想マシンの初期合計数を指定します。このクラスタでは 3 つのコンピューティング ノードから始めます。

resource "google_container_cluster" "primary" {
  name               = var.cluster
  location           = var.zone
  initial_node_count = 3

  master_auth {
    username = ""
    password = ""

    client_certificate_config {
      issue_client_certificate = false
    }
  }

  node_config {
    machine_type = var.machine_type
    oauth_scopes = [
      "https://www.googleapis.com/auth/logging.write",
      "https://www.googleapis.com/auth/monitoring",
    ]

    metadata = {
      disable-legacy-endpoints = "true"
    }

    labels = {
      app = var.app_name
    }

    tags = ["app", var.app_name]
  }

  timeouts {
    create = "30m"
    update = "40m"
  }
}

解説: output.tf

Terraform では出力値と呼ばれるコンセプトが採用されています。Terraform モジュールが返した値は、子モジュールに出力され、そのリソース属性のサブセットが親モジュールに公開されるか、terraform apply の実行後に CLI 出力に特定の値が出力されます。以下の output.tf ブロックは、値を出力して、クラスタ名やクラスタ エンドポイントといった値のほか、sensitive パラメーターで指定した機密データを読み出します。


output "cluster" {
  value = google_container_cluster.primary.name
}

output "host" {
  value     = google_container_cluster.primary.endpoint
  sensitive = true
}

output "cluster_ca_certificate" {
  value     = base64decode(google_container_cluster.primary.master_auth.0.cluster_ca_certificate)
  sensitive = true
}

output "username" {
  value     = google_container_cluster.primary.master_auth.0.username
  sensitive = true
}

output "password" {
  value     = google_container_cluster.primary.master_auth.0.password
  sensitive = true
}

Terraform の初期化

Terraform のプロジェクトと構文について理解したところで、今度は Terraform を使用して GKE クラスタのプロビジョニングを行います。まずは part01/iac_gke_cluster ディレクトリに移動しましょう。

cd part01/iac_gke_cluster

part01/iac_gke_cluster ディレクトリで以下のコマンドを実行します。

terrform init

今回の例では以下の内容が出力されます。

root@d9ce721293e2:~/project/terraform/gcp/compute# terraform init

Initializing the backend...

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "google" (hashicorp/google) 3.10.0...

* provider.google: version = "~> 3.10"

Terraform has been successfully initialized!

Terraform でのプレビュー

Terraform には、Terraform コードをドライ実行して、実際には何も実行することなくバリデーションできる terraform plan というコマンドがあります。このコマンドでは、Terraform が既存のインフラストラクチャに対して実行するすべてのアクションと変更をグラフ化することも可能です。ターミナルで以下を実行します。

terraform plan

今回の例では以下の内容が出力されます。

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # google_container_cluster.primary will be created
  + resource "google_container_cluster" "primary" {
      + additional_zones            = (known after apply)
      + cluster_ipv4_cidr           = (known after apply)
      + default_max_pods_per_node   = (known after apply)
      + enable_binary_authorization = false
      + enable_intranode_visibility = (known after apply)
      + enable_kubernetes_alpha     = false
      + enable_legacy_abac          = false
      + enable_shielded_nodes       = false
      + enable_tpu                  = (known after apply)
      + endpoint                    = (known after apply)
      + id                          = (known after apply)
      + initial_node_count          = 3
      + instance_group_urls         = (known after apply)
      + label_fingerprint           = (known after apply)
      + location                    = "us-east1-d"
  }....
Plan: 1 to add, 0 to change, 0 to destroy.  

Terraform は main.tf ファイル内のコードに基づいて GCP リソースを新規作成します。

Terraform の適用

インフラストラクチャを新規作成してアプリケーションをデプロイする準備が整ったので、ターミナルで以下のコマンドを実行します。

terraform apply

コマンドを確認するメッセージが表示されたら、yes と入力して Enter キーを押します。

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

Terraform により、GCP 上に新しい GKE クラスタが作成されます。

: クラスタの完成には 3 ~ 5 分かかります。バックエンド システムがプロビジョニングを行ってからクラスタをオンラインにするため、瞬時には処理されません。

クラスタが完成すると、以下のような内容が出力されます。

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

Outputs:

cluster = cicd-workshops
cluster_ca_certificate = <sensitive>
host = <sensitive>
password = <sensitive>
username = <sensitive>

GKE クラスタが新規作成され、Outputs の結果が表示されます。機密情報としてマークされていると、出力値が <sensitive> タグによってマスキングされ、結果に表示されません。このように、機密データは必要に応じて保護することができます。

Terraform のリソースの破棄

GKE クラスタを正常に作成できたところで、今度は terraform destroy コマンドを実行して、このチュートリアルで作成したアセットを破棄してみましょう。アセットはそのまま稼働させても構いません。しかし、GCP 上でアセットを実行するにはコストが発生し、そのコストに対する責任も負わなくてはなりません。Google から無料トライアルの登録ユーザーに提供されている 300 ドル分のクレジットは、アセットを実行し続ければあっという間に尽きてしまいます。もちろん、ご事情に合わせて判断していただければと思いますが、terraform destroy を実行すれば実行中のすべてのアセットを破棄できます。

このコマンドを実行して GKE クラスタを破棄しましょう。

terraform destroy

まとめ

お疲れさまでした。本シリーズのパート 1 はこれで以上です。今回は IaC と Terraform を使用して Kubernetes クラスタをプロビジョニングし、GCP にデプロイする方法をご説明しました。

次回のパート 2 では、アプリケーション用の Docker イメージを作成し、リポジトリにプッシュした後、Terraform を使用してこのイメージをコンテナとして GKE にデプロイする方法について取り上げます。

さらに詳しく知りたい方は以下の資料を参照してください。