Skip to content

Resource Requests — CPU/Memory Sizing Trên Windows

Vì Sao Quan Trọng

Copy nguyên resources.requests/resources.limits từ một Deployment Linux sang Windows và deploy thẳng là sai lầm phổ biến nhất khi team mới migrate workload lên Windows container. Baseline overhead của Windows OS-level process cao hơn Linux đáng kể, và cơ chế enforce resource limit ở kernel level khác hoàn toàn (Job Objects thay vì cgroups) — dẫn tới hành vi throttling/OOM khác biệt mà nếu không hiểu rõ, sizing sẽ luôn sai theo hướng thiếu resource.

Job Objects: Cơ Chế Enforce Resource Limit Trên Windows

Trên Linux, kubelet dùng cgroups (v1 hoặc v2) để enforce CPU/memory limit cho container — kernel throttle CPU qua CFS bandwidth control, và kill process khi memory vượt cgroup limit (OOM killer ở cgroup scope).

Trên Windows, không có cgroups. Thay vào đó, containerd dùng Job Objects — một Windows kernel primitive được thiết kế từ lâu để group process và áp constraint chung:

Linux:    cgroup (CPU quota, memory.max) → kernel scheduler + OOM killer
Windows:  Job Object (CpuRate, JobMemoryLimit) → Windows kernel scheduler

Về nguyên lý mục tiêu tương tự (giới hạn resource cho group process), nhưng implementation detail và edge-case behavior khác nhau:

  • CPU limit: Job Object CPU rate control hoạt động theo rate-based throttling trong một time window (tương tự CFS quota/period, nhưng công thức nội bộ khác) — không nên assume behavior throttling identical 1:1 giữa hai platform khi tuning ở mức chi tiết cao
  • Memory limit: Job Object JobMemoryLimit enforce ở commit memory level — khi container vượt limit, Windows sẽ terminate container (tương tự OOM kill), nhưng không có "OOM score" tinh vi như Linux; behavior gần với hard kill hơn graceful degrade

Baseline Memory Overhead: Vì Sao Windows Pod Cần Request Cao Hơn

Một container Windows tối giản (chạy process đơn giản trên Nano Server) đã tiêu tốn bộ nhớ nhiều hơn container Linux tương đương đáng kể, vì:

  1. OS services chạy trong container namespace — dù container hóa, một số Windows subsystem (registry hive riêng, service control manager con) vẫn cần khởi tạo per-container
  2. .NET Runtime overhead (nếu app dùng .NET/.NET Framework) — CLR khởi tạo, JIT compilation cache, GC heap baseline — cộng thêm vài chục tới hàng trăm MB tùy runtime

Nguyên Tắc Sizing Thực Tế

Loại workloadLinux baseline (tham khảo)Windows baseline (tham khảo)
App tối giản (echo server)~10–20 MB~100–150 MB
.NET Core/.NET 5+ trên Nano Server~50–80 MB~150–250 MB
.NET Framework trên Server Core + IISKhông áp dụng~350–500 MB+

Số liệu trên chỉ mang tính tham khảo định hướng — luôn benchmark thực tế trên workload cụ thể trước khi finalize sizing, vì overhead phụ thuộc nhiều vào .NET runtime version, GC mode, và cấu hình app.

Nguyên tắc thực dụng: khi migrate service từ Linux sang Windows container, đặt baseline memory request tối thiểu gấp 2–3 lần giá trị tương ứng trên Linux, sau đó tinh chỉnh dựa trên observed working set thực tế qua ít nhất một tuần traffic production-like.

CPU Request: Millicore Semantics Vẫn Giữ Nguyên

Điểm này ít gây nhầm lẫn hơn — Kubernetes CPU request/limit vẫn dùng đơn vị millicore giống Linux (500m = nửa vCPU), và kubelet trên Windows vẫn map correctly sang Job Object CPU rate. Điều cần lưu ý:

  • Không set CPU limit quá sát với request cho .NET workload — JIT compilation và GC pause có burst CPU ngắn, throttle quá gắt gây latency spike (P99 tail latency) rõ rệt hơn so với native Linux binary
  • Nếu chạy multi-threaded .NET app, đảm bảo limits.cpu đủ lớn để Garbage Collector server mode (nếu enable) không bị throttle liên tục — GC server mode spawn thread theo số logical CPU visible, throttle CPU aggressive có thể làm GC pause kéo dài bất thường

Không Có Swap — Giống Linux Nhưng Vì Lý Do Khác

GKE Linux node tắt swap theo default (tránh latency không dự đoán được khi kernel swap page). Windows container trên GKE cũng không có swap cho container workload, nhưng vì lý do khác — Windows container namespace không hỗ trợ per-container swap accounting tương thích với Kubernetes memory model. Hệ quả thực tế giống nhau: memory limit là hard ceiling, vượt quá sẽ bị kill, không có "graceful degrade qua swap" ở cả hai platform.

Requests vs Limits: QoS Class Vẫn Áp Dụng

Kubernetes QoS class (Guaranteed/Burstable/BestEffort) hoạt động độc lập với OS — logic vẫn dựa trên so sánh requestslimits khai báo trong Pod spec, không đổi giữa Linux/Windows. Điều khác biệt là hệ quả khi vi phạm:

  • Node bị memory pressure trên Windows → kubelet eviction logic vẫn dùng cùng thuật toán ranking theo QoS class như Linux (evict BestEffort trước, Guaranteed sau cùng)
  • Nhưng tốc độ phản ứng của Windows node dưới memory pressure có thể khác — do cách Windows kernel report available memory (memory manager, standby list) khác Linux MemAvailable — nên threshold --eviction-hard cấu hình cho node pool Windows nên test riêng, không nên copy nguyên giá trị đã tune cho Linux node pool.

Checklist Sizing Cho Windows Pod

  1. Benchmark baseline memory footprint container ở idle state trước khi set request
  2. Set memory request có buffer, không set sát idle baseline (tính cả GC heap growth, JIT cache)
  3. Tránh CPU limit quá sát request cho workload có burst pattern (GC, JIT, startup)
  4. Test riêng eviction threshold cho node pool Windows, không tái sử dụng giá trị đã tune từ Linux
  5. Theo dõi actual working set qua ít nhất một chu kỳ traffic thật trước khi finalize limit

Tham Khảo