Skip to content

Infrastructure Debugging: Node & Control Plane

Tại sao Infrastructure Layer Khó Debug

Khi Pod fail hoặc Service unreachable, developer thường nghĩ vấn đề là ở application. Nhưng một số failure modes chỉ có thể giải thích ở infrastructure layer:

  • Pod không được schedule không phải vì resource thiếu mà vì scheduler không reachable
  • Pod restart không phải vì crash mà vì node có DiskPressure và kubelet evict
  • API calls bị timeout không phải vì code sai mà vì etcd đang compacting và API server backpressure

Hai layer quan trọng cần hiểu: Node layer (kubelet, node conditions, containerd) và Control plane layer (API server, etcd, scheduler, controller manager).


Node Layer: Internal Model của Kubelet

Kubelet là gì và làm gì

Kubelet là agent chạy trên mỗi node, là thành phần duy nhất trực tiếp quản lý containers. Trách nhiệm chính:

  1. Watch Pod assignments: Kubelet watch API server để biết Pod nào được assigned cho node của nó
  2. Lifecycle management: Start containers theo PodSpec, apply resource limits (cgroups), mount volumes
  3. Health reporting: Cập nhật Node status (conditions) vào API server định kỳ
  4. Garbage collection: Clean up container images và dead containers

Heartbeat Mechanism: kube-node-lease

Mỗi node có một Lease object trong namespace kube-node-lease. Kubelet renew Lease này định kỳ (mặc định 10 giây). Controller Manager check các Lease này — nếu một Lease không được renew trong node-monitor-grace-period (mặc định 40 giây), node đó được mark là Unknown.

Đây là cơ chế quan trọng: Lease renewal tốn ít resource hơn cập nhật Node object đầy đủ. Kubernetes tách biệt "node vẫn alive" (Lease) với "node status details" (Node object).

bash
# Xem Lease của node
kubectl get lease -n kube-node-lease
kubectl get lease NODE_NAME -n kube-node-lease -o yaml
# Spec.renewTime cho biết lần cuối kubelet renew

Nếu renewTime ngừng cập nhật → kubelet trên node đó đã bị hang hoặc crash.

Node Conditions: Báo Cáo Sức Khỏe

Kubelet cập nhật Node .status.conditions — mỗi condition biểu diễn một aspect của node health:

ConditionTrue = Vấn đềNgưỡng phát hiện
MemoryPressureNode thiếu memory khả dụngEviction threshold (mặc định: memory.available < 100Mi)
DiskPressureNode thiếu disknodefs.available < 10% hoặc imagefs.available < 15%
PIDPressureQuá nhiều processespid.available < 1000
ReadyNode KHÔNG readyKubelet không renew hoặc có condition trên
NetworkUnavailableCNI chưa setup networkCNI plugin báo lỗi
bash
kubectl describe node NODE_NAME | grep -A20 "Conditions:"
# Status: True/False/Unknown
# Message: Mô tả cụ thể
# Reason: Machine-readable reason

Quan trọng: Khi MemoryPressure hoặc DiskPressureTrue, kubelet bắt đầu eviction — lần lượt terminate Pods để giải phóng resource. Thứ tự eviction ưu tiên:

  1. Pods không có resource requests (Best Effort QoS class)
  2. Pods có resource usage vượt requests (Burstable QoS class)
  3. Pods có resource usage dưới requests (Guaranteed QoS class, ít bị evict nhất)

PLEG — Pod Lifecycle Event Generator

PLEG là component trong kubelet watch container runtime (containerd) để phát hiện container state changes. PLEG poll container runtime mỗi 1 giây và generate events khi container start/stop.

PLEG stall: Nếu PLEG không thể poll container runtime (container runtime bị overloaded hoặc hang), kubelet log warning PLEG is not healthy. Hậu quả:

  • Kubelet không biết container nào đang running
  • Node condition có thể set thành NotReady
  • Liveness probe không được kiểm tra
  • Pods trên node đó có thể bị reschedule
bash
# Tìm PLEG unhealthy trong kubelet logs
gcloud logging read \
  'resource.type="k8s_node"
   AND resource.labels.node_name="NODE_NAME"
   AND textPayload:"PLEG is not healthy"' \
  --freshness=2h

PLEG stall thường là triệu chứng của container runtime (containerd) bị quá tải, thường do excessive pod churn (liên tục create/delete pods) hoặc containerd bị OOM.

Debugging Node NotReady

Khi node có status NotReady:

bash
# Bước 1: Xem conditions cụ thể
kubectl describe node NODE_NAME | grep -A30 "Conditions:"

# Bước 2: Xem recent events của node
kubectl get events --field-selector involvedObject.name=NODE_NAME \
  --sort-by='.lastTimestamp'

# Bước 3: Check kubelet logs (quan trọng nhất)
gcloud logging read \
  'resource.type="k8s_node"
   AND resource.labels.node_name="NODE_NAME"
   AND (textPayload:"NotReady" OR textPayload:"kubelet" OR textPayload:"error")' \
  --freshness=2h \
  --format=json | jq '.[] | {time: .timestamp, msg: .textPayload}' | head -50

# Bước 4: Check containerd status trên node (nếu SSH được)
# systemctl status containerd
# journalctl -u containerd -n 100 --no-pager

Node Resource Allocation vs Capacity

Hiểu lầm phổ biến: so sánh Pod requests với node capacity. Thực ra, kubelet reserve một phần resource cho OS và Kubernetes system:

Allocatable = Capacity - kube-reserved - system-reserved - eviction-threshold

Với GKE node điển hình 4 vCPU, 15GB RAM:

  • Kubernetes/system reserve khoảng 940m CPU, ~3.5GB RAM
  • Eviction threshold ~100MB RAM
  • Allocatable ~ 3060m CPU, ~11.4GB RAM
bash
kubectl describe node NODE_NAME | grep -A10 "Capacity:" 
kubectl describe node NODE_NAME | grep -A10 "Allocatable:"
kubectl describe node NODE_NAME | grep -A20 "Allocated resources:"

Allocated resources cho thấy tổng requests của tất cả Pods đang chạy. Nếu Requests gần 100% Allocatable → node sắp hết chỗ để schedule Pod mới.


Node-Problem-Detector (NPD)

GKE chạy node-problem-detector như một DaemonSet, phát hiện các vấn đề system-level:

  • Kernel panic / OOM kill messages trong /dev/kmsg
  • NTP out of sync
  • Docker/containerd daemon hung
  • Network interface errors

NPD ghi vấn đề vào Node Conditions và Events. Xem:

bash
kubectl describe node NODE_NAME | grep -A5 "node-problem-detector"

Control Plane Layer: Internal Model

GKE Managed Control Plane: Giới Hạn Visibility

Trong GKE, control plane (API server, etcd, scheduler, controller manager) chạy trên infrastructure do Google quản lý — bạn không có SSH access vào control plane nodes. Điều này có nghĩa:

Bạn CÓ THỂ:

  • Observe API server metrics qua GKE Control Plane Health dashboard
  • Đọc control plane logs nếu đã bật GKE Control Plane Logging
  • Gián tiếp detect etcd performance qua API request latency

Bạn KHÔNG THỂ:

  • SSH vào API server node
  • Chạy etcdctl trực tiếp
  • Restart controller manager

Đây là trade-off của managed Kubernetes: bạn mất visibility thấp nhưng được đảm bảo availability từ Google.

API Server Request Lifecycle

Mỗi API request (kubectl, Kubernetes internal) đi qua pipeline:

Client → Authentication → Authorization (RBAC) → Admission Webhooks → API server handler
         → Object validation → etcd write/read → Response

Latency breakdown quan trọng:

  1. Authentication: Thường < 1ms (JWT validation) hoặc < 10ms (OIDC/external)
  2. Authorization: RBAC check trong memory, < 1ms
  3. Admission webhooks: Đây là nguồn latency variable nhất. Mỗi webhook là một external HTTP call. Nếu webhook server slow hoặc unavailable → API call timeout
  4. etcd roundtrip: Mỗi write operation là một etcd transaction. Latency phụ thuộc vào disk I/O và etcd leader election state
  5. Serialization: Encode object thành JSON/protobuf

Khi API latency cao, cần xác định ở bước nào:

bash
# Xem API server metrics (nếu bật control plane metrics)
# Cloud Monitoring → Metric: kubernetes.io/api_server/request_latencies

# Kiểm tra webhook latency
kubectl get validatingwebhookconfiguration
kubectl get mutatingwebhookconfiguration
# Nếu có nhiều webhooks → có thể là bottleneck

Admission Webhooks: Nguồn Gốc Của API Timeouts

Admission webhooks là một trong những nguyên nhân phổ biến nhất của API latency tăng đột biến. Khi deploy một CRD controller, service mesh (Istio), hoặc policy engine (OPA Gatekeeper), các webhook được register.

Failure mode:

  • Webhook server bị overloaded hoặc crash
  • API server call webhook → timeout (mặc định 10 giây)
  • Mọi kubectl apply, kubectl create bị delay 10 giây
  • Hàng nghìn requests queue → API server backpressure
bash
# Kiểm tra webhook config
kubectl get validatingwebhookconfiguration -o json | jq '.items[].webhooks[] | {name: .name, failurePolicy: .failurePolicy, timeoutSeconds: .timeoutSeconds}'

# Webhook với failurePolicy: Fail + không có healthy backend = API server hung

Nếu webhook có failurePolicy: Fail, API server phải nhận response từ webhook trước khi proceed. Nếu webhook server down → API call fail. Đây là thiết kế đúng cho security-critical webhooks (block bad resources) nhưng nguy hiểm cho availability.

etcd: Internal Storage Engine

etcd là distributed key-value store, single source of truth của tất cả Kubernetes cluster state. GKE lưu etcd trên SSD-backed persistent storage, chạy một cluster etcd per GKE cluster.

etcd request flow:

  1. API server gửi gRPC request đến etcd leader
  2. etcd leader đề xuất write lên Raft log
  3. Majority của etcd members phải confirm (trong GKE: usually 3 members)
  4. Sau khi committed, data được apply vào state machine
  5. Response trả về API server

Raft consensus có implications:

  • Latency phụ thuộc vào network latency giữa etcd members (cross-zone trong GKE)
  • Nếu leader election xảy ra, có downtime ngắn (vài giây) trong khi new leader elected
  • Throughput giới hạn bởi Raft commit frequency

etcd compaction và defragmentation:

etcd giữ revision history của mọi thay đổi. Theo thời gian, lịch sử này tích lũy và tốn storage. compact xóa revisions cũ. defrag giải phóng disk space từ compact.

Trong GKE, Google quản lý compaction tự động. Nhưng bạn có thể observe ảnh hưởng:

  • Trong lúc compact: etcd lock data temporarily → API latency spike
  • Sau defrag: disk space giảm đáng kể
bash
# Xem etcd metrics qua GKE Control Plane Health Dashboard
# Cloud Monitoring → GKE Control Plane → etcd metrics:
# - etcd_server_proposals_pending: số proposals chờ commit (nên gần 0)
# - etcd_disk_backend_commit_duration: disk write latency (nên < 25ms)
# - etcd_server_leader_changes: số lần leader thay đổi (nên thấp)

Scheduler Debugging

Kubernetes Scheduler quyết định Pod nào vào node nào. Failure modes:

Scheduling throughput: Với cluster lớn (nghìn nodes, nghìn pods), scheduler có thể bị bottleneck. Scheduler process Pods một lần (không phải parallel scheduling per Pod trong một batch). Nếu nhiều Pods cần schedule cùng lúc → latency cao.

Scheduler unschedulable backoff: Nếu Pod không thể schedule, scheduler không retry ngay. Nó retry sau 30 giây (backoff tăng dần đến 5 phút). Điều này có nghĩa: nếu resource thiếu được giải phóng (Pod khác bị xóa), Pod pending có thể không được schedule ngay mà phải chờ hết backoff period.

bash
# Xem scheduler events cho pod cụ thể
kubectl get events -n NAMESPACE \
  --field-selector involvedObject.name=POD_NAME,reason=FailedScheduling

# Scheduler logs (cần bật control plane logging)
gcloud logging read \
  'resource.type="k8s_control_plane_component"
   AND resource.labels.component_name="scheduler"
   AND severity>=WARNING' \
  --freshness=2h

Controller Manager Bottlenecks

Controller Manager chạy nhiều controllers: Deployment controller, ReplicaSet controller, Node lifecycle controller, etc. Mỗi controller có work queue.

Reconciliation loop: Mỗi controller watch relevant objects và reconcile desired state với actual state. Nếu cluster có nhiều changes (nhiều rolling updates cùng lúc), work queues grow.

Nhìn thấy qua GKE metrics:

  • controller_runtime_reconcile_time_seconds: Thời gian reconcile mỗi request (histogram)
  • workqueue_depth: Chiều sâu của work queue

Nếu workqueue depth cao và tăng → controller manager không kịp xử lý → Deployments không scale/update nhanh như expected.


Quy Trình Debug Infrastructure Incidents

Khi Node Bị NotReady

1. kubectl describe node NODE_NAME
   → Xem Conditions để biết loại pressure

2. kubectl get events --field-selector involvedObject.name=NODE_NAME
   → Events gần đây

3. Cloud Logging: kubelet logs trên node đó
   → Nguyên nhân cụ thể (OOM, disk full, PLEG stall)

4. Cloud Monitoring: node CPU, memory, disk usage time series
   → Trend trước khi incident

5. Quyết định:
   - Nếu tạm thời (disk full → cleanup): cordon node, drain, clean
   - Nếu nghiêm trọng (hardware failure): cordon, drain, delete node, cluster autoscaler tạo mới
   - Nếu network issue: escalate (GCP support nếu VPC level)

Khi API Server Latency Cao

1. GKE Control Plane Dashboard → API server latency metrics
   → Latency tăng ở loại request nào? (LIST pods? WATCH? Write?)

2. kubectl get validatingwebhookconfiguration
   kubectl get mutatingwebhookconfiguration
   → Có webhook nào failed/slow không?

3. Cloud Monitoring: etcd metrics
   → etcd_disk_backend_commit_duration spikes?
   → etcd_server_leader_changes nhiều?

4. Cloud Logging: control plane component logs
   → API server log error gì?

5. Nếu webhook gây ra:
   → Scale up webhook server
   → Temporary: change failurePolicy từ Fail → Ignore (rủi ro security)
   → Long-term: optimize webhook handler

Giới Hạn Của GKE Managed Control Plane và Cách Làm Việc Với Nó

Do control plane được Google quản lý, một số thông tin không available trực tiếp. Cách tiếp cận:

  1. Enable Control Plane LoggingControl Plane Metrics trong GKE cluster settings để có visibility tốt hơn
  2. Sử dụng GKE Dashboard trong Cloud Console → Observability tab → Control Plane metrics
  3. Khi suspecting control plane issue: Mở Google Cloud Support ticket với:
    • Cluster name và project ID
    • Exact timestamp của incident
    • Symptom description (commands ran, outputs)
    • Cloud Logging query results

Google SRE team có access deeper metrics mà customer không thể see.


Anti-Pattern: Restart Control Plane Components trong Self-Managed Kubernetes

Lưu ý này chủ yếu cho team có self-managed Kubernetes hoặc Anthos, nhưng cũng hữu ích để hiểu design:

Tại sao không restart etcd member tùy tiện:

etcd là distributed consensus system. Nếu bạn restart tất cả etcd members cùng lúc → quorum bị mất → cluster hung. Chỉ restart từng member một, đảm bảo cluster maintain quorum trước khi restart member tiếp theo.

Với GKE, Google quản lý điều này. Nhưng nếu bạn đang debug và thấy "etcd slow", không có action nào bạn có thể take trực tiếp — thay vào đó monitor metrics và contact support nếu vượt ngưỡng.


References