↓メインコンテンツへスキップ
  1. Blogs/

TerraformでGKEのノードあたり最大Pod数を小さくしすぎるとどうなるか試してみた

2 分
Terraform GoogleCloud GKE
0222-nnn
著者
0222-nnn
猫が好き
目次
Terraform-GoogleCloud - この記事は連載の一部です
パート 29: この記事

概要
#

GKEプライベートクラスタと踏み台の構成では、Pod用セカンダリIPレンジ(pods_cidr)を/16という余裕のあるサイズにしていました。実際の設計では、ノード数とノードあたりの最大Pod数から逆算してサイズを決める必要があります。

「レンジを大きく取れば安全」で終わらせず、ノードあたりの最大Pod数を意図的に小さくして、何が起きるかを実際に確認します。ノードあたりの最大Pod数(default_max_pods_per_node)は作成後に変更できないため、事前の見積もりを誤ると、クラスタを作り直す必要が出てきます。

Terraformの基本操作と、前々回のGKEプライベートクラスタ+踏み台構成は前提としています。

複数ノード・複数ゾーンでの検証は扱いません。11-gke-private-bastionはゾーナル・1ノード構成のままです。services_cidrが作成後に拡張できない点も、実際の再現はせず公式ドキュメントの参照に留めます。

検証すること
#

  • ノードあたりの最大Pod数を小さく(8)設定してクラスタを作成できること
  • その上限だけで、GKEの管理コンポーネント自体がスケジュールできなくなること(apply成功とは別の確認)
  • ユーザーのPodも同じ理由でスケジュールに失敗すること

前提環境
#

  • Google Cloud CLI、Terraform、Docker
  • Billingが有効な検証用Google Cloud Project
  • GKE、Compute Engineを操作できるGoogleアカウント
  • 検証時のバージョン:Terraform 1.14.3、google 7.46.0

使用するTerraformコード
#

新規のterraform-gcp-examplesディレクトリは作りません。今回の検証はレンジサイズと実際のPod数の関係に絞っているため、11-gke-private-bastionにmax_pods_per_node変数を追加し、既存のコードを流用しています。

レンジサイズとPod数の関係
#

flowchart TB
    RANGE["Pod 用セカンダリレンジ
例: /21"] RANGE --> N1["ノード1
/24 を切り出す"] RANGE --> N2["ノード2
/24 を切り出す"] RANGE --> N3["ノード3
..."] N1 --> M["max_pods_per_node で
1ノードあたりの上限が決まる"] M --> P["実際に載る Pod 数"]

ノードごとにレンジが切り出され、その中で max_pods_per_node が上限になります。レンジを大きくしても、この値を超えては載りません。

今回の変更
#

11-gke-private-bastionにmax_pods_per_node変数を追加しました。デフォルトはnullで、指定しなければ既存の動作をそのまま維持します。Standardクラスタでは省略時に110が入ります。

variable "max_pods_per_node" {
  description = "Maximum Pods per node (default_max_pods_per_node). null leaves it unset, which uses the GKE default (110). Immutable after cluster creation -- changing it requires recreating the node pool."
  type        = number
  default     = null
}
resource "google_container_cluster" "primary" {
  # ...
  default_max_pods_per_node = var.max_pods_per_node
}

nullをそのまま渡すとTerraformは属性自体を省略するため、この変数を追加しても既存の11の挙動は変わりません。実際に、max_pods_per_nodeを指定しないplanではリソース数25(変更前と同じ)で差分なしを確認しています。

設定と実行
#

cd Advanced-Examples/11-gke-private-bastion
cp terraform.tfvars.example terraform.tfvars
project_id = "YOUR_PROJECT_ID"
iap_member = "user:YOUR_ACCOUNT"

max_pods_per_nodeをあえて小さい値(8)にしてapplyします。

terraform apply -var="max_pods_per_node=8"
Apply complete! Resources: 25 added, 0 changed, 0 destroyed.

実際に8が設定されたかを確認します。

gcloud container node-pools describe tf-adv-gke-bastion-np \
  --cluster="$(terraform output -raw cluster_name)" --zone="$(terraform output -raw zone)" \
  --project="$(terraform output -raw project_id)" \
  --format="value(maxPodsConstraint.maxPodsPerNode)"
8

GKEの管理コンポーネントだけで上限を使い切る
#

クラスタ作成が成功したことと、実際にPodが動かせることは別です。踏み台からkubectlで、稼働中のPodを確認します。

kubectl get pods -A --field-selector spec.nodeName!= \
  -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,STATUS:.status.phase'
NAMESPACE     NAME                                                             STATUS
kube-system   fluentbit-gke-7bg8x                                              Running
kube-system   gke-metadata-server-qq4gc                                        Running
kube-system   gke-metrics-agent-lq2ck                                          Running
kube-system   konnectivity-agent-948897f8d-kjpw9                               Running
kube-system   kube-proxy-gke-tf-adv-gke-basti-tf-adv-gke-basti-f9e879ca-t3b2   Running
kube-system   netd-wccb8                                                       Running
kube-system   node-local-dns-kbhsm                                             Running
kube-system   pdcsi-node-bxxgb                                                 Running

ちょうど8個です。ノードの許容量を確認すると、この8個で埋まっていることが分かります。

kubectl get node -o jsonpath='{.items[0].status.allocatable.pods}'
8

この時点で、kube-dnsやmetrics-serverなど他の管理コンポーネントはまだ起動していません。理由をPendingになっているPodのイベントで確認します。

kubectl describe pod -n kube-system -l k8s-app=kube-dns
Events:
  Type     Reason             Age    From                Message
  ----     ------             ----   ----                -------
  Warning  FailedScheduling   84s (x9 over 3m35s)   default-scheduler   0/1 nodes are available: 1 Too many pods. no new claims to deallocate, preemption: 0/1 nodes are available: 1 Too many pods.

Too many pods。ユーザーのアプリケーションはまだ1つもデプロイしていません。GKE自身が動かす管理コンポーネント(ログ収集のfluentbit-gke、コントロールプレーンとの接続を担うkonnectivity-agent、kube-proxy、CNIのnetd、DNSキャッシュのnode-local-dns、永続ディスクドライバのpdcsi-node、メタデータサーバ、メトリクスエージェント)だけで、上限の8をすでに使い切っていました。

ユーザーPodでも同じ現象を確認する
#

念のため、シンプルなテスト用Podでも同じ結果になることを確認します。

kubectl run maxpods-test --image=busybox:1.36 --restart=Never -- sleep 3600
kubectl get pod maxpods-test
NAME           READY   STATUS    RESTARTS   AGE
maxpods-test   0/1     Pending   0          11s
kubectl describe pod maxpods-test
Events:
  Type     Reason             Age   From                Message
  ----     ------             ----  ----                -------
  Warning  FailedScheduling   10s   default-scheduler   0/1 nodes are available: 1 Too many pods. no new claims to deallocate, preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.

管理コンポーネントのPodと全く同じ理由で、ユーザーPodもスケジュールされませんでした。

後片付け
#

kubectl delete pod maxpods-test --ignore-not-found=true
terraform destroy
Destroy complete! Resources: 25 destroyed.

GKE Clusterと踏み台VMは利用中に料金が発生します。検証後は必ずdestroyします。

まとめ
#

  • ノードあたりの最大Pod数を小さくしすぎると、ユーザーのアプリケーションどころか、GKE自身が動かす管理コンポーネントすらスケジュールできなくなる
  • 今回の環境では、ログ収集・DNSキャッシュ・CNI・永続ディスクドライバ等の標準コンポーネントだけで8個を使い切った。これらはkubectl get pods -Aで最初から見えているが、意識していないと見落としやすい
  • 失敗の原因はkubectl describe podのイベント(Too many pods)で確認できる。applyが成功した後に、実際にワークロードが動くところまで確認して初めて設計の妥当性が分かる
  • ノードあたりの最大Pod数は作成後に変更できないため、見積もりを誤ると新しいノードプール(クラスタ)を作り直すことになる

参考資料
#

次回
#

VPC Peering越しのGKE Pod→VM通信を、ip-masq-agentで復旧させます。

Terraform-GoogleCloud - この記事は連載の一部です
パート 29: この記事

関連記事

Peering越しのPod→VM通信が落ちる原因を、ルートだと思い込んでいた話
4 分
Terraform GoogleCloud GKE VPC
TerraformでGKE Dataplane V2を有効化し、NetworkPolicyでPod間通信を遮断してみた
2 分
Terraform GoogleCloud GKE NetworkPolicy
TerraformでGKEのPodをInternal HTTP LB(NEG)でポート別に振り分けてみた
3 分
Terraform GoogleCloud GKE LoadBalancing
TerraformでGKEのコントロールプレーンをプライベート+パブリックの両方から接続してみた
2 分
Terraform GoogleCloud GKE
TerraformでGKEプライベートクラスタからMemorystore for Redis Clusterへ接続してみた
3 分
Terraform GoogleCloud GKE Redis
TerraformでGKEプライベートクラスタからMemorystore for Redisへ接続してみた
2 分
Terraform GoogleCloud GKE Redis