1クール続けるブログ

とりあえず1クール続けるエンジニアの備忘録

TerraformでGKEクラスタ構築

業務ではdeployment managerというGCPから公式が提供している、Google Cloudリソースの作成と管理を自動化するインフラストラクチャデプロイサービスを利用して、Kubernetesクラスターやノードプールの作成を行っています。

ですが、GCPの環境構築においてはTerraformを利用している企業さんがかなり多いという認識をしております。 せっかくなのでプライベートでクラスターを作成する際には、Terraformを利用してみたいと思います。

作成したい構成

結構ケチケチしている性分なので、なるべく安くしたいところ…!

  • Google Kubernetes Engineのゾーンクラスタ
    • デフォルトで作成されるのがゾーンクラスタで、1アカウントにつき1つはAlwaysFree枠で無料になる
    • 実際に本番利用するときには、リージョンクラスタにしてMasterの冗長性を確保した方が良い、EKSのデフォルトはリージョンクラスタのはず
  • n1-standard1のノードプール
    • f1-microは30日間分無料なのだけれどスペックが満たせない

無料トライアルでもらえる$300のクレジットを利用して、はみ出したGCEとロードバランサの料金を払いたいと思います。
…待って。Stackdriverの料金もここに乗っかりそう🤔 Stackdriverのログも保存させないように設定を変更させておかないと!と思っていたのですが、50GBまでは無料なので一旦無視します。

TerraformでProvisionする準備

参考

learn.hashicorp.com

CLIをインストール

Download Terraform - Terraform by HashiCorp にアクセスして、アーキテクチャ/OSを選択し、CLIのリンクアドレスをコピーする。

$ wget https://releases.hashicorp.com/terraform/0.12.25/terraform_0.12.25_darwin_amd64.zip
$ unzip terraform_0.12.25_darwin_amd64.zip                                                                                                 
$ vi $HOME/.zshrc    # CLIのバイナリにPATHを通しておく
export PATH=$HOME/.terraform/bin:$PATH

$ terraform    # 叩けるか確認                                                                                                                                       
Usage: terraform [-version] [-help] <command> [args] ...(省略)

GCPをセットアップ

試しにTerraformでリソースを作成してみる

下記のように、ファイルを作成する。

// リソースの作成と管理を担当するproviderを設定する
// 複数のクラウドベンダなどを利用する場合には、複数のproviderブロックが存在しうる
provider "google" {
  version = "3.5.0"

  credentials = file("<クレデンシャルキーのファイル>.json")

  project = "<PROJECT_ID>"
  region  = "us-central1"
  zone    = "us-central1-c"
}

// インフラストラクチャ内にあるリソースを定義
// ブロック開始前の構成は、<リソースタイプ> <リソース名> となっていて、
// リソースタイプ google_compute_network の接頭辞がproviderを表す。
// <リソースタイプ>.<リソース名> はリソースIDとして扱われ、他のリソースから参照できる
resource "google_compute_network" "vpc_network" {
  name = "terraform-network"
}

下記のようにターミナルで操作を進めていく。
新しい設定の場合もしくはバージョン管理化にあるファイルをチェックアウトした後には、terraform initを実行する。このコマンドは、ローカルの設定やデータを初期化する。これは、main.tfのあるディレクトリで実行する。
terraform planでこの設定を適用した場合に、どのような操作が行われるかが確認できる。kubectlにおける--dry-runオプションのようなもの。
terraform applyで実際にリソースの作成/変更/削除が行われる。

$ terraform init                                                                  

Initializing the backend...

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

Terraform has been successfully initialized!
...(省略)

$ terraform plan                                                                  
...(省略)
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # google_compute_network.vpc_network will be created
  + resource "google_compute_network" "vpc_network" {
      + auto_create_subnetworks         = true
      + delete_default_routes_on_create = false
      + gateway_ipv4                    = (known after apply)
      + id                              = (known after apply)
      + ipv4_range                      = (known after apply)
      + name                            = "terraform-network"
      + project                         = (known after apply)
      + routing_mode                    = (known after apply)
      + self_link                       = (known after apply)
    }

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

$ terraform apply
...(省略)
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

google_compute_network.vpc_network: Creating...(省略)

実際に出来ているのが確認できました!

f:id:jrywm121:20200607081807p:plain

インスタンスの作成もやってみる。下記の設定をmain.tfに付け加える。f1-microなのでAlways Free枠です。お値段はかかりません。

resource "google_compute_instance" "vm_instance" {
  name         = "terraform-instance"
  machine_type = "f1-micro"

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

  network_interface {
    // ここで別のリソースの参照ができる
    network = google_compute_network.vpc_network.name
    access_config {
    }
  }
}

VMインスタンスも作成できていますね

f:id:jrywm121:20200607084935p:plain

最後にお片付けをしておきましょう

$ terraform destroy

Kubernetesクラスタを作成する

www.terraform.io

www.terraform.io

クラスターを作成する上で下記の2つのような方針を考えています。

  • kubectlコマンドで必要となるkubeconfigはgcloud container clusters get-credentials cluster-nameにて取得する
  • terraformのデフォルトはルートベースでのクラスター作成だが、今回はGCPのデフォルトであるVPC-Nativeのクラスターを作成する

実際にファイルを作成してapplyする前に APIの有効化をやっておくべき。

ファイルは下記のように作成した

provider "google" {
  version = "3.5.0"

  credentials = file("<クレデンシャルキーのファイル>.json")

  project = "<PROJECT_ID>"
  region  = "us-central1"
  zone    = "us-central1-c"
}

resource "google_container_cluster" "primary" {
  name = "my-gke-cluster"

  // ゾーンを指定するとゾーンクラスタ、リージョンを指定するとリージョナルクラスタ
  location = "us-central1-c"

  // ノードプールのリソース設定を包含すると密結合になり変更が難しくなる場合がある
  remove_default_node_pool = true
  initial_node_count       = 1 // ↑がtrueのとき使用されないが"1"をセットしておく必要あり

  network    = "default"
  subnetwork = "default"

  // VPC-Nativeの場合には指定
  ip_allocation_policy {
    cluster_ipv4_cidr_block  = "/16" // podのIPアドレス範囲
    services_ipv4_cidr_block = "/22" // ServiceのIPアドレス範囲
  }

  // private_cluster_config も入れたほうが良い

  // usernameとpasswordを空で作ればBasic認証はdisableになる
  // このブロックを指定しなければ、GCPのユーザ名を利用し自動生成する
  master_auth {
    username = ""
    password = ""

    client_certificate_config {
      issue_client_certificate = false
    }
  }
}

resource "google_container_node_pool" "primary_preemptible_nodes" {
  name     = "my-node-pool"
  location = "us-central1-c"
  cluster  = google_container_cluster.primary.name

  // node_countと同時に使用するべきではない
  autoscaling {
    min_node_count = 0
    max_node_count = 1
  }

  management {
    auto_repair  = true  // ノードの自動修復は有効化
    auto_upgrade = false // 自動アップグレードは無効化
  }

  node_config {
    preemptible  = true
    machine_type = "n1-standard-1"

    labels = {
      app = "web"
    }

    // taintsも設定できるが、このフィールドの変更がノード再生成のトリガーと
    // なるので、ここで管理すべきではない

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

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

いざ!Provisionだ!

$ terraform apply
$ gcloud container clusters get-credentials my-gke-cluster
$ kubectl get svc                                                                        
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.112.0.1   <none>        443/TCP   11m

kubectlが通りました!コンソールも確認してみましょう!想定どおりです!

f:id:jrywm121:20200607103350p:plain

感想

普段利用しているdeployment-managerよりもシンプルで分かりやすい印象を受けました。
テンプレート化して変数とか当て込んだりしようとすると構成がもう少し複雑になってしまうのかもしれませんが、各クラウドベンダが同じ記法でインフラストラクチャを構成できるのは魅力ですね。