Vài mẹo cấu hình tài nguyên cho .NET App trên Kubernetes
Khi đưa ứng dụng ASP.NET Core lên Kubernetes, nếu không hiểu rõ cách .NET Garbage Collector (GC) tương tác với Linux cgroups, bạn rất dễ gặp phải hiện tượng OOMKilled (Exit Code 137) hoặc ứng dụng bị nghẽn CPU định kỳ.
Dưới đây là một số thiết lập quan trọng mà tôi rút ra được sau nhiều lần triển khai hệ thống thực tế.
1. Cấu hình Server GC vs Workstation GC
Mặc định trên môi trường Server/Docker, .NET sẽ bật Server GC. Server GC sẽ tạo ra một GC Heap riêng cho mỗi CPU Core được phát hiện.
Tuy nhiên, nếu Kubernetes Node của bạn có 32 Cores nhưng bạn chỉ gán resources.limits.cpu: "1000m" (1 Core), .NET mặc định vẫn có thể nhìn thấy cả 32 Cores và tạo 32 Heaps, gây ngốn RAM không cần thiết!
Hãy cấu hình trong file .csproj hoặc biến môi trường:
<PropertyGroup>
<!-- Bật Server GC nhưng giới hạn số lượng Heap tương ứng với limit -->
<ServerGarbageCollection>true</ServerGarbageCollection>
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
</PropertyGroup>
Hoặc qua biến môi trường trong Kubernetes Deployment:
env:
- name: DOTNET_gcServer
value: "1"
- name: DOTNET_GCHeapCount
value: "2"
2. Thiết lập Liveness & Readiness Probes chuẩn xác
Đừng dùng chung một endpoint cho cả Liveness và Readiness Probe.
- Liveness Probe: Chỉ kiểm tra xem tiến trình App có còn sống hay không. Nếu Liveness fail, K8s sẽ restart Pod.
- Readiness Probe: Kiểm tra xem App đã sẵn sàng nhận request chưa (kết nối DB, Redis, RabbitMQ sẵn sàng).
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 2
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
Kết luận
Hiểu đúng cách .NET hoạt động bên trong Container sẽ giúp bạn tiết kiệm đáng kể chi phí hạ tầng Cloud và đảm bảo ứng dụng vận hành ổn định 99.99% thời gian.