GKE node pool zablokowany w stanie PROVISIONING: wyczerpanie limitów, zakresy IP i dostępność typów maszyn
Rozwiązanie problemu z GKE node pool zablokowanym w stanie PROVISIONING z powodu wyczerpania limitów, niewystarczających zakresów IP dla podów lub niedostępności wymaganego typu maszyny w skonfigurowanej strefie.
PROVISIONING lub nowe nody są NotReady. Pody pozostają w stanie Pending, autoscaler próbuje skalować w górę, ale żadne nowe nody się nie pojawiają. Przyczyna to niemal zawsze ograniczenie zasobów poza samym Kubernetes - quota GCP, wyczerpanie adresów IP lub dostępność typu maszyny.
Objawy
Node pool pokazuje status PROVISIONING przez dłuższy czas. Pody pozostają w stanie Pending z błędami planowania:
# Node pool zablokowany w stanie PROVISIONING
gcloud container node-pools describe default-pool \
--cluster my-cluster \
--zone europe-west1-b \
--format="value(status)"
# Wynik: PROVISIONING
# Nody w stanie NotReady
kubectl get nodes
# NAME STATUS AGE VERSION
# gke-my-cluster-default-pool-abc123 NotReady 15m v1.29.4-gke.1234
# Pody zablokowane w Pending
kubectl get pods -A | grep Pending
# default my-app-7b9f4d-xk2lm 0/1 Pending 0 12m
# Sprawdź dlaczego pody nie są planowane
kubectl describe pod my-app-7b9f4d-xk2lm | grep -A5 Events
# Events:
# Warning FailedScheduling pod/my-app-7b9f4d-xk2lm
# 0/3 nodes are available: 3 Insufficient cpu.
# Logi cluster autoscalera
kubectl -n kube-system logs -l k8s-app=cluster-autoscaler --tail=50 | grep -i "scale up"
# Could not scale up node pool: RESOURCE_POOL_EXHAUSTED
Cluster autoscaler chce dodać nody, ale nie może ich utworzyć z powodu ograniczeń na poziomie infrastruktury.
Przyczyna
Tworzenie nodów GKE kończy się niepowodzeniem z trzech głównych przyczyn infrastrukturalnych:
- Wyczerpanie limitów regionu (quota GCP). GCP wymusza limity per region na CPU, instancje, adresy IP i akceleratory GPU. Po osiągnięciu limitu Compute Engine odmawia tworzenia nowych VM. To najczęstsza przyczyna w projektach z wieloma klastrami lub takimi, które rosły organicznie.
- Niewystarczający zakres IP dla podów. GKE używa wtórnych zakresów IP dla podów. Każdy nod rezerwuje domyślnie blok /24 (256 adresów IP). Jeśli zakres wtórny jest za mały, GKE nie może przypisać CIDR podów do nowego noda i tworzenie się blokuje.
- Niedostępność typu maszyny w strefie. Nie wszystkie typy maszyn są dostępne we wszystkich strefach. Jeśli żądasz
n2-standard-32w strefie, gdzie ta rodzina jest wyprzedana, tworzenie czeka w nieskończoność. Dotyczy to szczególnie instancji GPU i dużych typów maszyn.
Rozwiązanie
Krok 1: Sprawdź wyczerpanie limitów
# Wylistuj wszystkie quota dla regionu z aktualnym zużyciem
gcloud compute regions describe europe-west1 \
--format="table(quotas.metric,quotas.limit,quotas.usage)" \
| grep -E "CPUS|INSTANCES|IN_USE_ADDRESSES|SSD_TOTAL_GB"
# Przykładowy wynik pokazujący wyczerpanie limitów CPU:
# CPUS 24.0 24.0 <-- na limicie
# INSTANCES 100.0 15.0
# IN_USE_ADDRESSES 23.0 20.0
# SSD_TOTAL_GB 2048.0 1500.0
# Sprawdź czy konkretne quota blokuje operacje
gcloud compute regions describe europe-west1 \
--format="json(quotas)" | jq '.quotas[] | select(.usage >= .limit)'
Jeśli limit jest wyczerpany, poproś o podwyższenie:
# Poproś o podwyższenie limitu przez gcloud
gcloud compute regions describe europe-west1 --format="json(quotas)"
# Następnie poproś o podwyższenie w Console:
# IAM & Admin > Quotas > Filtruj po regionie/metryce > Edit Quotas
# Lub użyj gcloud:
gcloud alpha services quota update \
--consumer=projects/my-project \
--service=compute.googleapis.com \
--metric=compute.googleapis.com/cpus \
--unit=1/{project}/{region} \
--dimensions=region=europe-west1 \
--value=48
Krok 2: Sprawdź wyczerpanie zakresu IP dla podów
# Sprawdź zakresy wtórne na podsieci
gcloud compute networks subnets describe my-subnet \
--region=europe-west1 \
--format="table(secondaryIpRanges.rangeName,secondaryIpRanges.ipCidrRange)"
# Przykładowy wynik:
# RANGE_NAME IP_CIDR_RANGE
# pods 10.4.0.0/20 <-- Tylko 16 bloków /24 = max 16 nodów
# services 10.8.0.0/20
# Sprawdź ile nodów zużywa CIDR podów
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.podCIDR}{"\n"}{end}'
Jeśli zakres podów jest pełny, rozszerz go lub utwórz nowy node pool z większym zakresem:
# Opcja 1: Utwórz nowy node pool z inną podsiecią mającą większy zakres podów
gcloud container node-pools create expanded-pool \
--cluster=my-cluster \
--zone=europe-west1-b \
--machine-type=e2-standard-4 \
--num-nodes=3 \
--max-pods-per-node=64 \
--enable-autoscaling \
--min-nodes=1 \
--max-nodes=10
# Opcja 2: Zmniejsz max-pods-per-node by używać mniejszych bloków CIDR per nod
# To pozwala na więcej nodów w tym samym zakresie wtórnym
gcloud container node-pools create efficient-pool \
--cluster=my-cluster \
--zone=europe-west1-b \
--machine-type=e2-standard-4 \
--max-pods-per-node=32 \
--num-nodes=3
Krok 3: Obsługa niedostępności typu maszyny
# Sprawdź jakie typy maszyn są dostępne w strefie
gcloud compute machine-types list \
--zones=europe-west1-b \
--filter="name:n2-standard" \
--format="table(name,zone,guestCpus,memoryMb)"
# Jeśli typ maszyny jest niedostępny, sprawdź inne strefy
gcloud compute machine-types list \
--filter="name=n2-standard-8" \
--format="table(name,zone)"
Użyj wielostrefowych node pool z zapasowymi typami maszyn:
# Utwórz regionalny (wielostrefowy) node pool z zapasowym typem maszyny
gcloud container node-pools create resilient-pool \
--cluster=my-cluster \
--region=europe-west1 \
--machine-type=n2-standard-8 \
--enable-autoscaling \
--min-nodes=1 \
--max-nodes=10 \
--num-nodes=3
# Dla jeszcze większej odporności użyj spot VM
gcloud container node-pools create spot-pool \
--cluster=my-cluster \
--region=europe-west1 \
--machine-type=n2-standard-8 \
--spot \
--enable-autoscaling \
--min-nodes=0 \
--max-nodes=20 \
--node-labels=workload-type=batch
Krok 4: Skonfiguruj Node Auto-Provisioning (NAP) dla automatycznego przełączania
# Włącz node auto-provisioning z limitami zasobów
gcloud container clusters update my-cluster \
--zone=europe-west1-b \
--enable-autoprovisioning \
--min-cpu=1 \
--max-cpu=100 \
--min-memory=1 \
--max-memory=400 \
--autoprovisioning-locations=europe-west1-b,europe-west1-c,europe-west1-d
NAP pozwala GKE automatycznie tworzyć node pool z innymi typami maszyn, gdy preferowany typ jest niedostępny.
Weryfikacja
Po zastosowaniu poprawek potwierdź, że nody mogą być tworzone:
# 1. Zweryfikuj, że node pool jest RUNNING (nie PROVISIONING)
gcloud container node-pools describe default-pool \
--cluster my-cluster \
--zone europe-west1-b \
--format="value(status)"
# Oczekiwane: RUNNING
# 2. Zweryfikuj, że wszystkie nody są Ready
kubectl get nodes
# Oczekiwane: wszystkie STATUS = Ready
# 3. Zweryfikuj, że pody w stanie Pending zostały zaplanowane
kubectl get pods -A | grep Pending
# Oczekiwane: brak wyników
# 4. Zweryfikuj, że autoscaler może tworzyć nowe nody (wywołaj skalowanie)
kubectl run test-scale --image=nginx --requests='cpu=500m' --replicas=5
kubectl get pods -w
# Oczekiwane: pody są planowane w ciągu 2-3 minut wraz z pojawianiem się nowych nodów
# Sprzątanie po teście
kubectl delete deployment test-scale
# 5. Sprawdź status cluster autoscalera
kubectl -n kube-system describe configmap cluster-autoscaler-status | grep -A5 "Health"
# Oczekiwane: Healthy
Klaster GKE, który nie może się skalować, to tykająca bomba. Gdy ruch wzrasta lub wdrożenia wymagają więcej zasobów, pody pozostają w stanie Pending i aplikacja staje się niedostępna. Podwyższenie limitu wymaga minut na zgłoszenie, ale godzin na zatwierdzenie - planuj więc z wyprzedzeniem. Utrzymuj co najmniej 20% zapasu na limit CPU i IP w regionach produkcyjnych i zawsze używaj wielostrefowych node pool, by przetrwać problemy z pojemnością w pojedynczej strefie.
Utrzymujesz GKE na produkcji?
Umów bezpłatną 30-minutową rozmowę. Projektujemy klastry GKE z prawidłowym autoskalowaniem, planowaniem IP i wielostrefową odpornością, byś nigdy nie wpadł w ścianę braku zasobów.