概要 #
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数は作成後に変更できないため、見積もりを誤ると新しいノードプール(クラスタ)を作り直すことになる
参考資料 #
- Google Cloud: GKE networking overview / IP address ranges
- Terraform Registry: google_container_cluster