15
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Terraformを使ってGKEクラスタを作成する

Last updated at Posted at 2019-05-05

概要

あえてGoogle Cloud SDKを使わずにTerraformを使ってGKEクラスタを作成したので、そのときのメモ書きです。

前提

OS: macOS 10.14.4
Terraformバージョン: v0.11.13

事前知識

Terraformとは

クラウドプロバイダ上(AWSやGCPなど)のインフラ構成を管理・自動化できるツール。
例えばGCP上にインスタンスを作成したい場合、インスタンスの種類や数などの設定をファイルに定義しておいてそれを実行すれば自動でインスタンスを作成できます。
IaaSだけでなくPaaSやCaaSなども可能。

Kubernetesとは

公式サイトより。

Kubernetes (K8s)は、デプロイやスケーリングを自動化したり、コンテナ化されたアプリケーションを管理したりするための、オープンソースのシステムです。

Kubernetesは奥が深いので詳しい説明はここでは割愛。

GKEとは

Google Kubernetes Engine。
GCP上でKubernetes環境を利用できるようにしたマネージド型のサービス。
Web UI上から簡単に操作するだけでKubernetes環境を立ち上げることができます。
今回はその辺の操作をTerraformを使って自動化します。

今回作成するGKEクラスタ構成

料金を安く済ませることを軸に以下のように設定しました。

設定項目 設定値
マシンタイプ f1-micro
プリエンプティブノード数 2
通常ノード数 1
ゾーン us-west1-a

以下の記事を参考に算出。

これだけだとなぜこの設定値にしたのか不明なので少し解説。

マシンタイプ

マシンタイプは安く済ませるためと、後述するGCPのAlways Free枠を使って1台インスタンスを稼働させるために最低ランクのf1-microにしました。

ノードについて

今回は通常ノードとプリエンプティブノードの2種類を使ってクラスタを構成しました。
なぜ通常ノードだけではないかというと、プリエンプティブノードの料金が圧倒的に安いからです。
そもそもプリエンプティブノードとは、GCEの余ったリソースを使って立てられるインスタンスであり、他のタスクがリソースを必要とする場合は強制的にシャットダウンさせられます。そのため、使い勝手はあまり良くないです。
しかし、Kubernetesとして稼働させればKubernetesのオートヒーリング機能によって自動で復旧することができるので、そこまで不便に感じずに利用することができます。
ただし、全てプリエンプティブなノードにしてしまうと、全ノードが落ちたときに復旧できなくなるので、1台は常に稼働している通常ノードを確保しています。
そして、f1-microであればGCPのAlways Free枠で常に1台無料で利用できるため、通常ノードの1台をこれにあてています。
したがって今回はこのようなプリエンプティブなノードと通常ノードでの構成となりました。

ゾーン

GCPのAlways Free枠を利用するためにus-west1-aを指定します。

実際にやってみた

プロジェクトの作成

GCP上でプロジェクトを作成します。
そして発行されるプロジェクトIDを控えます。

Terraformのインストール

homebrewでインストールできるのでインストールします。

$ brew install terraform

設定ファイルの作成

GCP上にGKEクラスタを作成する設定内容をファイルに書いていきます。
Terraformでは*.tfという拡張子ファイルに設定を記述していきます。

プロバイダの設定

provider "google" {
  project = "<プロジェクトID>"
  region  = "us-west1"
  zone    = "us-west1-a"
}

設定を適用するクラウドプロバイダの設定を記述します。
今回はGCPの設定を書きます。
ここで設定する項目はデフォルトの設定値となるため、後述するリソースに設定されていれば書かなくても問題はないです。

設定項目 内容
project 適用するデフォルトのプロジェクトID
region 適用するデフォルトのリージョン
zone 適用するデフォルトのゾーン

変数の設定

後述するリソースの設定で共通の設定値が出てくるので先に変数化しておきます。
Terraformではvariableブロックを使って変数を定義します。

variable "location" {
  type = "string"
  default = "us-west1-a"
}

変数を呼び出すときはvar.<変数名>という形で呼び出すことができます。

リソースの設定

クラスタ、通常ノード、プリエンプティブノードの3つの設定を書きます。

クラスタ

google_container_clusterリソースで設定します。

resource "google_container_cluster" "primary" {
  name     = "my-gke-cluster"
  location = "${var.location}"

  remove_default_node_pool = true
  initial_node_count = 1

  master_auth {
    username = ""
    password = ""
  }
}
設定項目 内容 必須 or 任意
name クラスタ名 string 必須
location マスタクラスタのゾーンもしくはリージョンを指定
ゾーンの場合は1ゾーンのみでクラスタが作成される
リージョンの場合はその中の複数ゾーン跨る形でクラスタが作成される
string 任意
remove_default_node_pool trueの場合クラスタ作成時にデフォルトのノードプールを削除する bool 任意
initial_node_count デフォルトのノードプールに作成されるノード数
remove_default_node_poolをtrueにした場合は1以上を設定する必要がある
number 任意
master_auth.username Kubernetes masterにアクセスするときのbasic認証に使用するユーザ名を指定する string 任意
master_auth.password Kubernetes masterにアクセスするときのbasic認証に使用するパスワードを指定する string 任意

通常ノード

google_container_node_poolリソースで設定します。

resource "google_container_node_pool" "primary_nodes" {
  name       = "my-gke-node-pool"
  location   = "${var.location}"
  cluster    = "${google_container_cluster.primary.name}"
  node_count = 1

  management {
    auto_repair = true
  }

  node_config {
    machine_type = "f1-micro"

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

    oauth_scopes = [
      "https://www.googleapis.com/auth/logging.write",
      "https://www.googleapis.com/auth/monitoring",
    ]
  }
}
設定項目 内容 必須 or 任意
name ノードプール名 string 任意
cluster ノードプールを作成するクラスタ名 string 必須
location クラスタが存在するリージョンもしくはゾーンを指定 string 任意
node_count ノード数 number 任意
management.auto_repair trueの場合ノードが自動修復される bool 任意
node_config.machine_type GCEのマシンタイプを指定 string 任意
node_config.metadata クラスタ内のインスタンスに渡すメタデータをkey/value形式で指定する object 任意
node_config.oauth_scopes デフォルトサービスアカウントが利用できるGoogle APIのスコープを書く list 任意

プリエンプティブノード

google_container_node_poolリソースで設定します。
設定項目はほとんど通常ノードと同じですが、唯一node_config.preemtpible項目をtrueに設定しています。

resource "google_container_node_pool" "primary_preemptible_nodes" {
  name       = "my-gke-preemptible-node-pool"
  location   = "${var.location}"
  cluster    = "${google_container_cluster.primary.name}"
  node_count = 2

  management {
    auto_repair = true
  }

  node_config {
    preemptible  = true
    machine_type = "f1-micro"

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

    oauth_scopes = [
      "https://www.googleapis.com/auth/logging.write",
      "https://www.googleapis.com/auth/monitoring",
    ]
  }
}
設定項目 内容 必須 or 任意
node_config.preemptible trueの場合プリエンプティブなノードとなる
デフォルトはfalse
bool 任意

最終的なtfファイル

gke.tf
provider "google" {
  project = "<プロジェクトID>"
  region  = "us-west1"
  zone    = "us-west1-a"
}

variable "location" {
  type = "string"
  default = "us-west1-a"
}

resource "google_container_cluster" "primary" {
  name     = "my-gke-cluster"
  location = "${var.location}"

  remove_default_node_pool = true
  initial_node_count = 1

  master_auth {
    username = ""
    password = ""
  }
}

resource "google_container_node_pool" "primary_nodes" {
  name       = "my-gke-node-pool"
  location   = "${var.location}"
  cluster    = "${google_container_cluster.primary.name}"
  node_count = 1

  management {
    auto_repair = true
  }

  node_config {
    machine_type = "f1-micro"

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

    oauth_scopes = [
      "https://www.googleapis.com/auth/logging.write",
      "https://www.googleapis.com/auth/monitoring",
    ]
  }
}

resource "google_container_node_pool" "primary_preemptible_nodes" {
  name       = "my-gke-preemptible-node-pool"
  location   = "${var.location}"
  cluster    = "${google_container_cluster.primary.name}"
  node_count = 2

  management {
    auto_repair = true
  }

  node_config {
    preemptible  = true
    machine_type = "f1-micro"

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

    oauth_scopes = [
      "https://www.googleapis.com/auth/logging.write",
      "https://www.googleapis.com/auth/monitoring",
    ]
  }
}

サービスアカウントの作成

TerraformがGCP APIを叩く用のサービスアカウントをこちらから作成します。
作成後、JSONファイルをダウンロードし、パスを環境変数にセットします。

.zshrc
export GOOGLE_CLOUD_KEYFILE_JSON={{path}}

初期化

リソースを操作するためのプラグインを導入するために最初だけ初期化をしてあげる必要があります。
tfファイルがある配下で以下コマンドを実行します。

$ terraform init

すると、直下に.terraformディレクトリが作成されます。これが作成されれば初期化は完了です。

実行計画

実際に適用する時のdry run的なものを確認できます。

$ terraform plan

実行すると以下のように表示されるので、これを見て実行内容が正しいか確認します。

Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

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
      id:                                              <computed>
      additional_zones.#:                              <computed>
      addons_config.#:                                 <computed>
      cluster_autoscaling.#:                           <computed>
      cluster_ipv4_cidr:                               <computed>
      enable_binary_authorization:                     <computed>
      enable_kubernetes_alpha:                         "false"
      enable_legacy_abac:                              "false"
      enable_tpu:                                      <computed>
      endpoint:                                        <computed>
      initial_node_count:                              "1"
      instance_group_urls.#:                           <computed>
      ip_allocation_policy.#:                          <computed>
      location:                                        "us-west1-a"
      logging_service:                                 <computed>
      master_auth.#:                                   "1"
      master_auth.0.client_certificate:                <computed>
      master_auth.0.client_key:                        <computed>
      master_auth.0.cluster_ca_certificate:            <computed>
      master_version:                                  <computed>
      monitoring_service:                              <computed>
      name:                                            "my-gke-cluster"
      network:                                         "default"
      network_policy.#:                                <computed>
      node_config.#:                                   <computed>
      node_locations.#:                                <computed>
      node_pool.#:                                     <computed>
      node_version:                                    <computed>
      project:                                         <computed>
      region:                                          <computed>
      remove_default_node_pool:                        "true"
      zone:                                            <computed>

  + google_container_node_pool.primary_nodes
      id:                                              <computed>
      cluster:                                         "my-gke-cluster"
      initial_node_count:                              <computed>
      instance_group_urls.#:                           <computed>
      location:                                        "us-west1-a"
      management.#:                                    "1"
      management.0.auto_repair:                        "true"
      management.0.auto_upgrade:                       "false"
      max_pods_per_node:                               <computed>
      name:                                            "my-gke-node-pool"
      name_prefix:                                     <computed>
      node_config.#:                                   "1"
      node_config.0.disk_size_gb:                      <computed>
      node_config.0.disk_type:                         <computed>
      node_config.0.guest_accelerator.#:               <computed>
      node_config.0.image_type:                        <computed>
      node_config.0.local_ssd_count:                   <computed>
      node_config.0.machine_type:                      "f1-micro"
      node_config.0.metadata.%:                        "1"
      node_config.0.metadata.disable-legacy-endpoints: "true"
      node_config.0.oauth_scopes.#:                    "2"
      node_config.0.oauth_scopes.1277378754:           "https://www.googleapis.com/auth/monitoring"
      node_config.0.oauth_scopes.172152165:            "https://www.googleapis.com/auth/logging.write"
      node_config.0.preemptible:                       "false"
      node_config.0.service_account:                   <computed>
      node_count:                                      "1"
      project:                                         <computed>
      region:                                          <computed>
      version:                                         <computed>
      zone:                                            <computed>

  + google_container_node_pool.primary_preemptible_nodes
      id:                                              <computed>
      cluster:                                         "my-gke-cluster"
      initial_node_count:                              <computed>
      instance_group_urls.#:                           <computed>
      location:                                        "us-west1-a"
      management.#:                                    "1"
      management.0.auto_repair:                        "true"
      management.0.auto_upgrade:                       "false"
      max_pods_per_node:                               <computed>
      name:                                            "my-gke-preemptible-node-pool"
      name_prefix:                                     <computed>
      node_config.#:                                   "1"
      node_config.0.disk_size_gb:                      <computed>
      node_config.0.disk_type:                         <computed>
      node_config.0.guest_accelerator.#:               <computed>
      node_config.0.image_type:                        <computed>
      node_config.0.local_ssd_count:                   <computed>
      node_config.0.machine_type:                      "f1-micro"
      node_config.0.metadata.%:                        "1"
      node_config.0.metadata.disable-legacy-endpoints: "true"
      node_config.0.oauth_scopes.#:                    "2"
      node_config.0.oauth_scopes.1277378754:           "https://www.googleapis.com/auth/monitoring"
      node_config.0.oauth_scopes.172152165:            "https://www.googleapis.com/auth/logging.write"
      node_config.0.preemptible:                       "true"
      node_config.0.service_account:                   <computed>
      node_count:                                      "2"
      project:                                         <computed>
      region:                                          <computed>
      version:                                         <computed>
      zone:                                            <computed>


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

------------------------------------------------------------------------

実行

実際に適用します。
途中でDo you want to perform these actions?と聞かれるので問題ない場合はyesと答えます。

$ terraform apply

最後に以下のように表示されれば成功です。

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

補足情報

tfstateファイルについて

Terraformは*.tfstateという拡張子のファイルで現在の環境を認識します。
そのため、tfstateファイルは削除すると実際の環境情報と差異が出てしまうので絶対に削除してはいけません
tfstateファイルはクラウドストレージなどリモートで保管することが推奨されています。
GCSで管理するやり方をこちらにまとめてあります。

リソースの破棄

リソースの破棄は以下コマンドで実現できます。

$ terraform destroy

dry run的に確認する場合は以下で実現できます。

$ terraform plan -destroy

tfファイルのフォーマットに関するvimプラグイン

tfファイルをvimで修正する場合、以下プラグインを入れるとフォーマットや色付けがされて便利です。
hashivim/vim-terraform: basic vim/terraform integration

以上

15
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?