👻

Google Cloud/Terraform GKEクラスタの構築を試してみた

2022/04/25に公開

TL;DR

GKEクラスタをTerraformを使用して作ってみようと思いましたので、動作モードの違いによって作成してみたTerraformコード、調べた内容について書きたいと思います。

準備段階で考えた内容

きっかけ

実は資格取得を目指した際、GKEに対して理解を深めるためにハンズオントレーニングを受けたのですが、その時にGKEクラスタの構築とサンプルマニフェストを使用したPodのとIngressサービスのデプロイまで実施したことがあります。
ただ、ハンズオントレーニングの際はgcloudコマンドでGKEクラスタを作成しましたので、今回Terraformでのコードを使用してデプロイした場合、どのような感じになるのかを試してみました。
また、これまではGKEクラスタの動作モードを従来のStandardモードでのみ作成してきましたが、2021年2月に新たにAutopilotモードがリリースされたので、この未経験のAutopilot動作モードでも試してみて、動作モードによる違いを明確にしたいと思いました。

gcloudコマンドの再確認

GKEクラスタはgloudコマンドでも作成が可能ですが、改めてGoogle Cloud公式サイトのコマンドリファレンスを確認したところ、設定できるパラメータが想像以上に多かったので、この際、自分なりに重要と考えるパラメータをピックアップして、整理します。

gcloud container clusters create 【クラスタ名】 [パラメータ]

https://cloud.google.com/sdk/gcloud/reference/container/clusters/create

GKEクラスタの設定パラメータ

改めて整理してみました。

従来のStandardモードではコントロールプレーンはGKEにて管理してくれますが、ノードはユーザ自身で管理する必要があります。
https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture?hl=ja

一方、Autopilotモードでは、コントロールプレーンのみならず、ノードもGKEが管理してくれます。
※Autopilotモードで、設定できるパラメータが少なくなることは明らかですね。
https://cloud.google.com/kubernetes-engine/docs/concepts/autopilot-architecture?hl=ja

ピックアップしたパラメータ

ロケーションタイプ

コントロールプレーンとノードの配置に関するもので、可用性要件や予算に応じて決めますが、本番環境では単一ゾーン障害による影響を考えると、できれば「リージョンクラスタ」構成を採用することが望ましいのではないかと思いました。
今回の検証では、1つのリージョン「asia-northeast1」の3つのゾーン「asia-northeast1-a、asia-northeast1-b、asia-northeast1-c」にコントロールプレーンとノードの配置する「リージョンクラスタ」を構成してみます。

但し、Autopilotモードの場合は、ロケーションタイプを選ぶことはできず、リージョンクラスタで構成されます。

コントロールプレーンのアップグレード方針

環境毎、アップグレードポリシーによるところではありますが、本番環境での安定性を優先すると、リリースチャンネルは更新頻度が数か月に一回の「Stable」を選択することが望ましいのではないかと思いました。
また、バージョンは指定できた方が良いので、今回の検証では「1.21.10-gke.2000」を選択することにします。

但し、Autopilotモードの場合は、バージョンの指定は不可でした。

ノードのアップグレード方針

標準のアップグレード方式となっている「サージアップグレード」を採用し、アップグレード時の挙動を決めれるので、本番環境を想定して、以下の方針で進めます。
1ノードずつ順番にアップグレードを実行していく形です。

  • max-surge-upgrade
    アップグレード中に追加可能なNode数を指定することができ、今回は検証用の小規模構成なので、デフォルトの「1」とします。
  • max-unavailable-upgrade
    アップグレード中に使用不可になるNode数を指定することができ、今回は検証用の小規模構成なので、デフォルトの「0」とします。

ノードプールサイズと自動スケーリング

GKEクラスタ上で稼働するPod数、各Podが捌ける処理内容などによるところではありますが、リージョンクラスタ構成のメリットを活かし、テストとして以下の方針で設定します。
通常時は3ノード構成で、スケールアウトしたら、最大6ノードまで増える構成です。

  • 初期ノードとして、ゾーンごとにノード数を「1」に設定
  • 自動スケーリングの設定として、ゾーンごとに最小ノード数を「1」、最大ノード数を「2」に設定

ネットワーキング

Google Cloud推奨設定と、リソースは極力インタネットから直接アクセスできるサブネットに配置しないというこれまでの自身のシステム構築経験に沿って、以下で決めてみました。

  • 自身で作成したVPCネットワークとサブネット内にノードを配置するプライベートクラスタを構成し、外部からコントロールプレーンにアクセスできないようにする。
    -コントロールプレーン自体はGoogle Cloudが管理するVPC内に配置されて、自身で作成したVPCネットワークとVPCピアリング接続されるので、そのコントロールプレーンのVPCネットワークのネットワークアドレスを指定する。
  • ネットワークモードはGoogle Cloud推奨の「VPCネイティブクラスタ」モードを選択し、セカンダリIPアドレスの範囲(PodのIPアドレス範囲、サービスのIPアドレス範囲)を明示的に指定し、GKEクラスタ内部で使用する。

上記の場合、外部からコントロールプレーンにアクセスできない(kubectlコマンドが発行できない)ので、踏み台用インスタンスをノードと同じサブネットに配置し、ssh接続などでアクセスできるようにする必要があります。
ちなみに、ノードからインタネットにアクセスできるようにするためには、Cloud NATを構成する必要があり、Google Cloud APIにアクセスできるようにするためには、 限定公開のGoogleアクセスの利用を実施すれば良いことがわかりました。

構成イメージは以下のようになります。

メンテナンスポリシー

システムのオフピーク時にメンテナンスを実施できるように、メンテナンス時間枠(曜日、時間帯、頻度)を指定します。
コンソールで確認したところ、以下の注意表記がありました。

32日間のローリングウィンドウ内で少なくとも48 時間はメンテナンスが可能な状態にする必要があります。 メンテナンスに4 時間以上連続する時間を用意してください。

言い換えると、1回あたりのメンテナンス時間を最短の4時間に指定した場合、1週間の中で最低3日間はメンテナンス曜日を設定しないといけないことと理解しました。
今週末からメンテナンスを開始し、毎週週末の夜間帯にメンテナンス時間枠を設定しました。

  • 金曜日のAM2:00(日本時間)開始
  • 土曜日のAM2:00(日本時間)開始
  • 日曜日のAM2:00(日本時間)開始

https://cloud.google.com/kubernetes-engine/docs/concepts/maintenance-windows-and-exclusions?hl=ja

モニタリング、ロギング

障害発生時の調査や動作確認時に利用する(よっぽどのことでない限り、モニタリングとログは無効化しない)ので、Google Cloudの推奨設定に従い、有効にします。
Standardモードでは、モニタリングとロギングを無効化したり、ソースを選択できたりしますが、Autopilotモードでは無効化できないことを知りました。

https://cloud.google.com/stackdriver/docs/solutions/gke/installing?hl=ja

ノードとPodへの権限付与

今回はクラスタを作成するところまでですが、本番環境ではノード、PodからCloud StorageなどのGoogle Cloudサービスへのアクセスが必要になってくるケースが多々あると思っています。
そこで、以下で設定を進めてみます。

  • ノードには適切な権限を付与したサービスアカウントをアタッチ
  • Podから指定したGoogle Cloudサービスにアクセスできるように、Workload Identiyを有効化

調べたところ、Autopilotモードでは、Workload Identiyはデフォルトで有効でした。
また、Workload Identityを有効化するだけではなく、適切な権限を付与したサービスアカウントとKubernetesのサービスアカウントを関連付ける必要があることを知りました。

https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity

動作モードの違いによるパラメータ比較

先述したパラメータが各動作モードで手動で明示的に設定可能かどうかを表で比較してみました。
Autopilotモードでは、ノードに関する設定項目がないので、すっきりした感じですね。

Standardモード Autopilotモード
ロケーションタイプ ×
コントロールプレーンのアップグレード方針
ノードのアップグレード方針 ×
ノードプールサイズと自動スケーリング ×
ネットワーキング
メンテナンスポリシー
モニタリング、ロギング ×
ノードとPodへの権限付与 ×

リソースの作成

GKE クラスタ(Standardモード)

Terraformのドキュメントを確認していたところ、ノードプールに関しては、デフォルトノードプールを使用せずに、明示的にノードプールを作成する方式が推奨されていました。
ノードプールを手動で新規作成すると、クラスタに追加したり、削除する場合、クラスタ自体を再作成せずに柔軟に対応できます。

It is recommended that node pools be created and managed as separate resources as in the example above. This allows node pools to be added and removed without recreating the cluster.

https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/container_cluster

コード

# VPC
resource "google_compute_network" "vpc_network" {
  name                    = "btc4043-vpc"
  auto_create_subnetworks = false
  routing_mode            = "REGIONAL"
}

# Subnet1(asia-northeast1)
resource "google_compute_subnetwork" "an1_private1" {
  name          = "btc4043-an1-private1"
  ip_cidr_range = "192.168.1.0/24"
  region        = "asia-northeast1"
  network       = google_compute_network.vpc_network.id
}

# Service account for GKE Cluster node
resource "google_service_account" "sc_gke1" {
  account_id   = "btc4043-sc-gck1"
  display_name = "Service Account for GkE1"
}

# GKE cluster(Standard)
resource "google_container_cluster" "gke1" {
  name     = "btc4043-gke1-cluster"
  location = "asia-northeast1"

  remove_default_node_pool = true
  initial_node_count       = 1

  release_channel {
    channel = "STABLE"
  }

  min_master_version = "1.21.10-gke.2000"

  networking_mode = "VPC_NATIVE"
  ip_allocation_policy {
    cluster_ipv4_cidr_block  = "10.10.0.0/16"
    services_ipv4_cidr_block = "10.20.0.0/16"
  }
  
  network    = google_compute_network.vpc_network.self_link
  subnetwork = google_compute_subnetwork.an1_private1.self_link

  private_cluster_config {
    enable_private_nodes = true
    enable_private_endpoint = true
    master_ipv4_cidr_block = "192.168.100.0/28"

    master_global_access_config {
    enabled = false
    }
  }

  master_authorized_networks_config {
  }

  logging_config {
    enable_components = [
        "SYSTEM_COMPONENTS",
        "WORKLOADS"
    ]
  }

  monitoring_config {
    enable_components = [
        "SYSTEM_COMPONENTS",
    ]
  }

  addons_config {
    http_load_balancing {
      disabled = false
    }
  }

  workload_identity_config {
    workload_pool = "btc4043.svc.id.goog"
  }

  maintenance_policy {
    recurring_window {
      start_time = "2022-04-29T17:00:00Z"
      end_time   = "2022-04-29T21:00:00Z"
      recurrence = "FREQ=WEEKLY;BYDAY=FR,SA,SU"
    }
  }

}

# GKE nodepool
resource "google_container_node_pool" "gke1_nodes" {
  name       = "btc4043-node-pool"
  location   = "asia-northeast1"
  cluster    = google_container_cluster.gke1.name
  node_count = 1

  autoscaling {
    min_node_count = 1
    max_node_count = 2
  }

  upgrade_settings {
    max_surge       = 1   
    max_unavailable = 0
  }

  management {
    auto_repair  = true
    auto_upgrade = true
  }

  node_config {
    machine_type = "e2-medium"

    service_account = google_service_account.sc_gke1.email
    oauth_scopes    = [
      "https://www.googleapis.com/auth/cloud-platform"
    ]
  }
}

確認

terraform applyを実行したところ、エラーは発生せずにリソースの作成が完了しました。
作成されたGKEクラスタ(Standardモード)をコンソール画面から確認したところ、ピックアップしたパラメータが正しくセットされていました。

  • クラスタ概要画面

  • クラスタ詳細画面(クラスタの基本と自動化)

  • クラスタ詳細画面(ネットワーキング)

  • クラスタノード画面

GKE クラスタ(Autopilotモード)

「enable_autopilot = true」で、Autopilotモードでの作成を宣言する必要がありました。
また、ノード管理はGKEが行うので、ノードプールのリソース作成が省かれました。

コード

# VPC
resource "google_compute_network" "vpc_network" {
  name                    = "btc4043-vpc"
  auto_create_subnetworks = false
  routing_mode            = "REGIONAL"
}

# Subnet1(asia-northeast1)
resource "google_compute_subnetwork" "an1_private1" {
  name          = "btc4043-an1-private1"
  ip_cidr_range = "192.168.1.0/24"
  region        = "asia-northeast1"
  network       = google_compute_network.vpc_network.id
}

# GKE cluster(Autopilot)
resource "google_container_cluster" "gke2" {
  name     = "btc4043-gke2-cluster"

  enable_autopilot = true
  location = "asia-northeast1"

  release_channel {
    channel = "STABLE"
  }

  networking_mode = "VPC_NATIVE"
  ip_allocation_policy {
    cluster_ipv4_cidr_block  = "10.10.0.0/16"
    services_ipv4_cidr_block = "10.20.0.0/16"
  }

  network    = google_compute_network.vpc_network.self_link
  subnetwork = google_compute_subnetwork.an1_private1.self_link

  private_cluster_config {
    enable_private_nodes = true
    enable_private_endpoint = true
    master_ipv4_cidr_block = "192.168.100.0/28"

    master_global_access_config {
    enabled = false
    }
  }

  master_authorized_networks_config {
  }

  maintenance_policy {
    recurring_window {
      start_time = "2022-04-29T17:00:00Z"
      end_time   = "2022-04-29T21:00:00Z"
      recurrence = "FREQ=WEEKLY;BYDAY=FR,SA,SU"
    }
  }

}

確認

terraform applyを実行したところ、エラーは発生せずにリソースの作成が完了しました。
作成されたGKEクラスタ(Autopilotモード)をコンソール画面から確認したところ、ピックアップしたパラメータが正しくセットされていました。
Autopilotモードの場合、クラスタ概要画面で「モード」カラムが追加されて(値はAutopilot)、「ノード数」カラムの値がブランクになっていました。
また、クラスタの詳細画面では「ノード」タブが無くなり、ノードに関する情報がコンソールから確認できなくなりました。

  • クラスタ概要画面

  • クラスタ詳細画面(クラスタの基本と自動化)

  • クラスタ詳細画面(ネットワーキング)

まとめ

GKEクラスタのそれぞれの動作モードにおいて、自分なりに重要と思われる設定パラメータをピックアップし、Terrraformでリソースを作成してみました。
まだまだキャッチアップできていないパラメータも多々ありますが、今回作成したリソースをベースに他のパラメータも試してみたり、実際にPod、Service/Ingresのデプロイを実施してみようと思います。
また、運用面でのポイント(モニタリングやロギングの活用)なども引続き調査していき、まとめることにします。

Discussion