Control Plane Debugging — API, etcd, Scheduler, Webhooks
Tại sao quan trọng ở Production
Control plane adalah "brain" của cluster. Kalau control plane slow atau broken:
- All Kubernetes API calls slow (kubectl commands hang)
- Scheduler berhenti place pods baru (Pending pods)
- New deployments rollout hang
- ConfigMap/Secret updates tidak propagate
- Webhooks timeout → admission reject valid requests
Operator sering tidak realize ini control plane problem karena mereka fokus pada pod/node. Tapi kubectl hang atau api-server not responding adalah giveaway.
Internal Model: Control Plane Architecture
Control Plane Components in GKE
GKE managed control plane (bukan self-managed like kubeadm):
GCP Managed Control Plane (Highly Available):
├── API Server (multiple replicas)
├── etcd (persistent data store)
├── Scheduler
├── Controller Manager
└── Cloud Auditing LayerGCP manage HA, so usually not totally down. Tapi bisa slow.
API Server: Request Path
Ketika kubectl run atau application make API call:
1. kubectl call → API Server HTTP/2 endpoint
2. API Server receive request
3. Authentication (check certificate/token)
4. Authorization (check RBAC)
5. Admission Control (validating/mutating webhooks)
6. Request processing (create object, update state)
7. etcd write (persist object)
8. Webhooks fire (post-admission)
9. Return responseJika slow anywhere dalam path → entire request slow.
etcd: Persistent State
etcd adalah key-value store. Setiap Kubernetes object (Pod, Service, etc) store di etcd:
/kubernetes.io/namespaces/default
/kubernetes.io/pods/default/my-pod
/kubernetes.io/services/default/my-service
...etcd performance critical karena:
- Every object write must persist (synchronous)
- Slow etcd write → all object create/update slow
- Scheduler read pod/node info dari etcd → slow etcd → slow scheduling
Scheduler: Pod Placement Decision
Scheduler watch unscheduled pods, try place on nodes:
1. Watch for Pending pods
2. For each pod:
a. Filter nodes (node selector, affinity, resource fit)
b. Score nodes (spreading, preferred affinity)
c. Bind pod to best-scoring node
3. If bind fail → pod stay PendingSlow scheduler bisa from:
- High scheduler latency (take long to filter/score)
- etcd slow (cannot read pod/node info fast)
- Many pending pods (overload scheduler)
Admission Webhooks: Validation & Mutation
Validating Webhook:
├── Check request (e.g., image must from approved registry)
└── If fail → reject request (HTTP 400/403)
Mutating Webhook:
├── Modify request (e.g., inject sidecar)
└── Return modified request → continue processingWebhooks call external service (you deploy it). Kalau webhook slow atau timeout:
- Slow to create/update resources
- Eventually timeout → request fail → resource not created
Debugging API Server Latency
Symptom: kubectl Slow
# kubectl take >30s to return result
time kubectl get pods
# real 0m35.234s
# Compare with direct API call
time curl https://<api-server>/api/v1/pods
# Also slow? → API server problem
# Fast? → kubectl authentication overheadStep 1: Check API Server Availability
# Check if API server responding at all
kubectl version
# If timeout/error → API server problem
# Check component health
kubectl get componentstatuses
# Deprecated in 1.19+, but might still work
# Or check control plane dashboard in GCP consoleStep 2: Identify Bottleneck
# Enable API server request auditing (GKE can do this)
# Then check logs for request latency
# Or manually check:
# Option A: Check etcd performance
# Option B: Check scheduler latency
# Option C: Check admission webhook latencyStep 3: Check etcd Performance
etcd expensive operation: list all objects, watch objects.
# Via Control Plane Logs (GCP Cloud Logging)
gcloud logging read \
'resource.type="k8s_cluster" AND jsonPayload.component="etcd"' \
--limit 50
# Look untuk slow operations:
# "operation-duration" > 1 second
# "apply-index-duration" > 1 second
# From API server logs, check etcd calls:
gcloud logging read \
'resource.type="k8s_cluster" AND jsonPayload.component="kube-apiserver"' \
--limit 50
# Search untuk "etcd" latency messagesIf etcd slow:
Check etcd member status:
bash# Can't direct query etcd (GCP managed), but can check via API server logs # API server health endpoint might report etcd latencyCheck database size:
bash# Via GCP console → GKE cluster → storage quota # etcd default storage: ~1GB per 100k objects # If exceed quota → evict old data or increase quotaCheck for large objects:
bash# Get all objects sizes kubectl api-resources --no-headers=true | awk '{print $1}' | while read r; do echo "$r: $(kubectl get $r -A --no-headers 2>/dev/null | wc -l)" done # Find large CRDs kubectl get crds --sort-by=.status.storedVersions
Step 4: Check Scheduler Latency
Scheduler slow = new pods pending longer.
# Check scheduler logs
gcloud logging read \
'resource.type="k8s_cluster" AND jsonPayload.component="scheduler"' \
--limit 50
# Look untuk "operation-latency" or "schedule-attempt-duration"
# Normal: <100ms
# Slow: >1sIf scheduler slow:
Check pending pods count:
bashkubectl get pods --field-selector=status.phase=Pending --all-namespaces # Many pending? → scheduler overloadCheck node count:
bashkubectl get nodes # Many nodes (>500)? → scheduler filter/score slowerCheck complex affinity rules:
bashkubectl get pods -o yaml | grep -A10 "affinity:" # Complex pod/node affinity → scheduler slower
Solution:
- Scale scheduler replicas (not typical in GKE managed, but possible)
- Simplify pod affinity rules
- Use node labels for better filtering
Step 5: Check Admission Webhook Latency
Admission webhook timeout common cause of create/update hang.
# Check ValidatingWebhookConfiguration
kubectl get validatingwebhookconfigurations
kubectl describe validatingwebhookconfigurations <name>
# Check MutatingWebhookConfiguration
kubectl get mutatingwebhookconfigurations
# Check timeout setting
kubectl get validatingwebhookconfigurations -o yaml | grep -i timeout
# Default: 10 seconds
# If webhook take >10s → timeout → request failDebug slow webhook:
Check webhook logs:
bash# Webhook is user-deployed pod kubectl logs -f -l app=webhookCheck webhook service latency:
bash# From pod in cluster kubectl exec <pod> -- time curl https://<webhook-service>/validateCheck if webhook backing service healthy:
bashkubectl get endpoints <webhook-service> # Should have addresses (backends) kubectl get pods -l app=webhook # Should be running
If webhook too slow:
Increase timeout (in webhookconfiguration):
yamltimeoutSeconds: 30 # Increase from 10Optimize webhook code (less processing)
Scale webhook replicas
Use failure policy:
yamlfailurePolicy: Ignore # Or Fail # Ignore: if webhook fail, let request through # Fail: if webhook fail, reject request
Debugging Scheduler Failures
Symptom: Pods Stuck Pending Despite Free Resources
kubectl get pods
# STATUS: Pending, no "reason" visible
kubectl describe pod <name>
# Events section might show "Unschedulable" or "Failed to bind"Root Causes
1. Scheduler not running
- Check scheduler pod: kubectl get pods -n kube-system | grep scheduler
2. Scheduler cannot read pod/node info
- Check scheduler logs for API errors
3. Scheduler cannot bind pod to node
- Check kubelet on target node
4. Pod affinity not satisfiable
- Pod require specific node label → label missing
- Pod require co-location with other pod → pod not scheduled yetDebugging Affinity Issues
# Check pod affinity rules
kubectl get pod <name> -o yaml | grep -A20 "affinity:"
# Example: nodeAffinity
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: gpu
operator: In
values:
- "true"
# Check if node exist with this label
kubectl get nodes --show-labels | grep gpu
# If no node with gpu=true → pod cannot schedule
# Fix: either
# 1. Add label to node: kubectl label nodes <node> gpu=true
# 2. Remove/relax affinity requirementDebugging Pod Affinity (Inter-Pod)
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- database
topologyKey: kubernetes.io/hostname
# Meaning: schedule on same node as pod with app=databaseDebug:
# Check if matching pod exist
kubectl get pods -l app=database
# If no pods → new pod cannot schedule (chicken-egg problem)
# Solution: deploy database pod first, then depend podsDebugging Webhook Failures
Symptom: create/update Resources Fail
kubectl apply -f my-resource.yaml
# Error: admission webhook validation failed
# or webhook timeoutCheck Webhook Configuration
# List all webhooks
kubectl get validatingwebhookconfigurations
kubectl get mutatingwebhookconfigurations
# Describe webhook
kubectl describe validatingwebhookconfigurations <name>
# Check:
# - clientConfig.service (webhook service address)
# - namespaceSelector (which namespaces apply)
# - objectSelector (which objects)
# - timeoutSeconds
# - failurePolicyDebug Webhook Connectivity
# Check webhook service exist
kubectl get service <webhook-service> -n <webhook-namespace>
# Check endpoints (backend pods)
kubectl get endpoints <webhook-service> -n <webhook-namespace>
# Should have IPs
# Test connect to webhook (from API server pod would be ideal, but hard to exec)
# Instead, check webhook pod logs
kubectl logs -l app=webhook -n <webhook-namespace>
# Look untuk request entries and response statusWebhook Timeout Issue
# If webhook pod slow or crash:
# 1. Check pod status
kubectl get pods -l app=webhook
# NotReady or CrashLoopBackOff?
# 2. Check pod logs
kubectl logs <webhook-pod>
# 3. Check webhook latency
# Add timing in webhook code, or use APM tool
# 4. Solution: increase timeoutSeconds, or fix slow webhookGKE-Specific Control Plane Debugging
GCP Cluster Control Plane Status
# Check control plane health via gcloud
gcloud container clusters describe <cluster-name> \
--format='value(status,statusMessage)'
# Check control plane add-ons
gcloud container clusters describe <cluster-name> \
--format='value(addonsConfig)'
# Check master authorized networks (might block API access)
gcloud container clusters describe <cluster-name> \
--format='value(masterAuthorizedNetworksConfig.cidrBlocks[*].cidrBlock)'Cloud Monitoring for Control Plane
GCP provide metrics for managed control plane:
# Check API server request latency
gcloud monitoring timeseries list \
--filter='metric.type="kubernetes.io/apiserver/request_duration_seconds"' \
--format='table(metric.labels, points[0].value.double_value)'
# Check etcd latency
gcloud monitoring timeseries list \
--filter='metric.type="kubernetes.io/etcd/server/has/leader"' \
--format='table(points[0].value.double_value)'Cloud Audit Logs
GCP automatically audit Kubernetes API requests:
# Check who created/modified resources
gcloud logging read \
'resource.type="k8s_cluster" AND operation.response.@type="CreateStatus"' \
--limit 10 \
--format='table(timestamp, operation.first(protoPayload.request.name))'
# Check which resources modified at specific time
gcloud logging read \
'timestamp>="2025-06-24T10:00:00Z" AND resource.type="k8s_cluster"' \
--limit 50Operational Practices
1. Monitor Control Plane Health
# Prometheus/Cloud Monitoring alerts:
# - kube_apiserver_request_duration_seconds_bucket{le="1"} < 0.95 # 95% requests <1s
# - etcd_disk_backend_commit_duration_seconds_bucket{le="0.1"} > 0.9 # 90% commits <100ms
# - scheduler_schedule_attempts_total{result="unschedulable"} rate increase2. Webhook Best Practices
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: my-webhook
webhooks:
- name: validation.example.com
clientConfig:
service:
name: webhook
namespace: webhooks
path: "/validate"
caBundle: LS0tLS1... # Base64 CA cert
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: ["apps"]
apiVersions: ["v1"]
resources: ["deployments"]
timeoutSeconds: 5
failurePolicy: Fail
admissionReviewVersions: ["v1"]
sideEffects: None
namespaceSelector:
matchLabels:
webhook: enabled # Only apply to labeled namespacesBest practices:
- Set appropriate timeout (not too long, not too short)
- Use namespace selector (don't apply globally)
- Keep webhook simple (fast to execute)
- Scale webhook replicas
- Use structured logging untuk debug
3. Manage etcd Size
# Prevent unbounded growth:
# 1. Set object quotas per namespace
resourcequota:
hard:
pods: "100"
configmaps: "50"
# 2. Periodic cleanup of old objects
# CronJob to delete old Jobs, Pods, Logs
# 3. Monitor etcd size
kubectl describe nodes -l cloud.google.com/gke-nodepool=...
# Under "Allocated resources"
# Watch storage trends