Kubernetes Resource Quota và LimitRange: Chặn Namespace Ăn Hết CPU/RAM Trên Production

Kubernetes Resource Quota LimitRange là bộ kiểm soát nền tảng mà gần như cluster production nào cũng nên có trước khi cho nhiều team cùng deploy workload. Nếu không đặt quota, một namespace có thể tạo quá nhiều Pod, request quá nhiều CPU/RAM, hoặc chạy container không giới hạn memory rồi làm node rơi vào tình trạng pressure, evict Pod của team khác và biến một lỗi nhỏ thành sự cố toàn cluster.

Bài viết này đi theo hướng thực chiến cho sysadmin/SRE: hiểu cơ chế scheduler, requests/limits, ResourceQuota, LimitRange; dựng lab; áp dụng YAML; kiểm tra output; xử lý lỗi thường gặp; và xây checklist nghiệm thu trước khi bật chính sách trên production.

1. Bối cảnh production: vì sao cluster cần quota?

Hãy tưởng tượng một Kubernetes cluster dùng chung cho nhiều team:

  • Team backend chạy API Java cần nhiều RAM.
  • Team data chạy batch job ngắn nhưng ăn CPU mạnh.
  • Team frontend chạy vài Pod nginx nhỏ.
  • Team platform vận hành ingress, monitoring, logging.

Nếu không có giới hạn, một deployment sai cấu hình như replica 200 hoặc container memory leak không limit có thể chiếm tài nguyên node, làm Pod khác bị Pending, OOMKilled hoặc Evicted. Trong môi trường cloud, autoscaler có thể tiếp tục mở node mới và tạo chi phí ngoài dự kiến.

2. Requests và limits: nền móng trước khi nói về quota

CPU request/limit

resources.requests.cpu là lượng CPU scheduler dùng để quyết định Pod có đủ chỗ trên node hay không. resources.limits.cpu là trần CPU container được dùng. CPU limit thường gây throttling khi vượt ngưỡng, không kill container.

resources:
  requests:
    cpu: "250m"
  limits:
    cpu: "500m"

250m nghĩa là 0.25 vCPU. Nếu Pod request 250m, một node còn 1000m allocatable về lý thuyết có thể nhận 4 Pod như vậy, chưa tính tài nguyên hệ thống.

Memory request/limit

Memory request dùng cho scheduling. Memory limit là trần cứng; vượt quá thường dẫn đến OOMKilled.

resources:
  requests:
    memory: "256Mi"
  limits:
    memory: "512Mi"

Với memory, đặt limit quá thấp làm ứng dụng chết liên tục; đặt quá cao hoặc không đặt limit có thể làm node bị memory pressure.

3. ResourceQuota là gì?

ResourceQuota là object áp chính sách tổng tài nguyên cho một namespace. Nó có thể giới hạn:

  • Tổng CPU/memory requests và limits.
  • Số lượng Pod, Service, ConfigMap, Secret, PVC.
  • Tổng storage request của PVC.
  • Quota theo storage class hoặc scope.

Ví dụ namespace team-a chỉ được request tối đa 4 CPU và 8Gi RAM:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-a-compute-quota
  namespace: team-a
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    pods: "30"
    services: "10"
    configmaps: "50"
    secrets: "50"

4. LimitRange là gì?

LimitRange đặt default request/limit và min/max cho từng container hoặc PVC trong namespace. Nó giải quyết vấn đề phổ biến: developer quên khai báo resources.

apiVersion: v1
kind: LimitRange
metadata:
  name: team-a-default-limits
  namespace: team-a
spec:
  limits:
  - type: Container
    defaultRequest:
      cpu: "100m"
      memory: "128Mi"
    default:
      cpu: "500m"
      memory: "512Mi"
    min:
      cpu: "50m"
      memory: "64Mi"
    max:
      cpu: "2"
      memory: "2Gi"

Khi Pod không khai báo resources, admission controller sẽ tự thêm default. Khi Pod khai báo vượt max hoặc dưới min, request tạo Pod sẽ bị từ chối.

5. Lab: tạo namespace và bật quota

Lab này chạy được trên kind, minikube hoặc cluster thật có quyền admin. Tạo namespace:

kubectl create namespace team-a
kubectl get ns team-a

Output mẫu:

NAME     STATUS   AGE
team-a   Active   3s

Lưu file team-a-policy.yaml:

cat > team-a-policy.yaml <<'EOF'
apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-a-compute-quota
  namespace: team-a
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
    pods: "5"
---
apiVersion: v1
kind: LimitRange
metadata:
  name: team-a-default-limits
  namespace: team-a
spec:
  limits:
  - type: Container
    defaultRequest:
      cpu: "100m"
      memory: "128Mi"
    default:
      cpu: "300m"
      memory: "256Mi"
    min:
      cpu: "50m"
      memory: "64Mi"
    max:
      cpu: "1"
      memory: "1Gi"
EOF
kubectl apply -f team-a-policy.yaml

Kiểm tra:

kubectl describe quota -n team-a
kubectl describe limitrange -n team-a

Output mẫu của quota:

Name:            team-a-compute-quota
Namespace:       team-a
Resource         Used  Hard
--------         ----  ----
limits.cpu       0     2
limits.memory    0     2Gi
pods             0     5
requests.cpu     0     1
requests.memory  0     1Gi

6. Test Pod không khai báo resources

Tạo Pod nginx không khai báo resources:

kubectl -n team-a run nginx-default --image=nginx:1.27
kubectl -n team-a get pod nginx-default -o yaml | grep -A8 resources:

Bạn sẽ thấy Kubernetes đã thêm default từ LimitRange:

resources:
  limits:
    cpu: 300m
    memory: 256Mi
  requests:
    cpu: 100m
    memory: 128Mi

Đây là lý do LimitRange rất hữu ích: nó giảm rủi ro workload “vô chủ” về tài nguyên.

7. Test Pod vượt quota

Tạo deployment request quá nhiều CPU:

cat > too-big.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: too-big
  namespace: team-a
spec:
  replicas: 2
  selector:
    matchLabels:
      app: too-big
  template:
    metadata:
      labels:
        app: too-big
    spec:
      containers:
      - name: nginx
        image: nginx:1.27
        resources:
          requests:
            cpu: "800m"
            memory: "256Mi"
          limits:
            cpu: "1"
            memory: "512Mi"
EOF
kubectl apply -f too-big.yaml

ReplicaSet có thể không tạo đủ Pod vì tổng request CPU vượt quota 1 CPU. Kiểm tra event:

kubectl -n team-a describe rs -l app=too-big

Output thường gặp:

Error creating: pods "too-big-..." is forbidden: exceeded quota: team-a-compute-quota, requested: requests.cpu=800m, used: requests.cpu=800m, limited: requests.cpu=1

Thông điệp này cho biết quota hoạt động đúng: Pod bị chặn trước khi làm cluster quá tải.

8. Test container vượt LimitRange max

Nếu container request memory lớn hơn max 1Gi:

cat > over-limitrange.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: over-limitrange
  namespace: team-a
spec:
  containers:
  - name: nginx
    image: nginx:1.27
    resources:
      requests:
        cpu: "100m"
        memory: "2Gi"
      limits:
        cpu: "500m"
        memory: "2Gi"
EOF
kubectl apply -f over-limitrange.yaml

Output mẫu:

Error from server (Forbidden): error when creating "over-limitrange.yaml": pods "over-limitrange" is forbidden: maximum memory usage per Container is 1Gi, but limit is 2Gi

9. Thiết kế quota cho production như thế nào?

Bắt đầu bằng quan sát, không đoán mò

Trước khi áp quota cứng, hãy đo mức sử dụng thật:

kubectl top pod -A --containers
kubectl top node
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,CPU_REQ:.spec.containers[*].resources.requests.cpu,MEM_REQ:.spec.containers[*].resources.requests.memory'

Nếu có Prometheus, nên xem p95/p99 CPU và memory working set trong 7–30 ngày. Quota nên dựa trên nhu cầu thực tế cộng biên độ tăng trưởng, không chỉ dựa trên số replica hiện tại.

Phân tầng namespace

  • dev: quota nhỏ, limit chặt, ưu tiên tiết kiệm chi phí.
  • staging: gần production nhưng replica thấp hơn.
  • production: quota đủ cho peak và rolling update.
  • platform: dành cho monitoring, ingress, logging; không dùng chung quota với app team.

Lưu ý rolling update: nếu deployment có maxSurge: 25%, trong lúc deploy số Pod có thể cao hơn bình thường. Quota phải đủ cho giai đoạn chuyển tiếp, không chỉ trạng thái steady-state.

10. Kết hợp với PriorityClass và autoscaler

ResourceQuota không thay thế Cluster Autoscaler hay Karpenter. Quota trả lời câu hỏi “namespace được phép dùng bao nhiêu”, còn autoscaler trả lời “cluster có nên thêm node không”. Nếu quota quá thấp, Pod bị chặn ngay cả khi cluster còn khả năng scale. Nếu quota quá cao, autoscaler có thể mở node liên tục và làm chi phí tăng mạnh.

Với workload quan trọng, có thể dùng PriorityClass để Pod production quan trọng được ưu tiên hơn. Tuy nhiên, đừng dùng priority để che lấp việc thiếu quota; hai cơ chế này giải quyết hai lớp vấn đề khác nhau.

11. Monitoring quota bằng kubectl và Prometheus

Kiểm tra nhanh quota theo namespace:

kubectl get resourcequota -A
kubectl describe quota -n team-a

Nếu dùng kube-state-metrics, có thể theo dõi các metric như:

  • kube_resourcequota
  • kube_pod_container_resource_requests
  • kube_pod_container_resource_limits

PromQL tham khảo để xem tỷ lệ CPU request đã dùng so với quota:

sum by (namespace) (kube_resourcequota{resource="requests.cpu",type="used"})
/
sum by (namespace) (kube_resourcequota{resource="requests.cpu",type="hard"})

Nên cảnh báo khi namespace dùng trên 80% quota trong thời gian dài, vì deploy kế tiếp có thể thất bại.

12. Troubleshooting lỗi thường gặp

Pod bị Forbidden vì quota

pods "api-xxx" is forbidden: exceeded quota: team-a-compute-quota

Cách xử lý:

  1. Chạy kubectl describe quota -n team-a để xem resource nào chạm trần.
  2. Kiểm tra Deployment có tăng replica hoặc request bất thường không.
  3. Nếu hợp lý, mở quota qua GitOps/pull request; nếu không, sửa request/replica.

Pod Pending dù quota còn

Quota còn không có nghĩa node còn chỗ. Kiểm tra scheduler event:

kubectl -n team-a describe pod POD_NAME

Nếu thấy Insufficient cpu hoặc Insufficient memory, vấn đề nằm ở capacity node/autoscaler, không phải ResourceQuota.

Ứng dụng OOMKilled sau khi áp LimitRange

kubectl -n team-a get pod
kubectl -n team-a describe pod api-xxx | grep -i oom -A3

Có thể default memory limit quá thấp. Hãy đo memory thật bằng metrics, tăng limit cho workload cụ thể, rồi cập nhật chart/manifest thay vì chỉ tăng default toàn namespace.

Helm upgrade thất bại sau khi thêm quota

Helm có thể tạo Job hook, Pod tạm hoặc replica surge làm vượt quota. Kiểm tra hook và strategy:

helm -n team-a get manifest RELEASE_NAME | grep -n "resources:" -A8
kubectl -n team-a get events --sort-by=.lastTimestamp | tail -30

13. Checklist nghiệm thu trước khi bật production

  • Mỗi namespace có owner rõ ràng và mục đích rõ ràng.
  • Requests/limits của workload chính đã được đo từ metrics thực tế.
  • Quota tính cả rolling update, cronjob, migration job và autoscaling peak.
  • LimitRange có default hợp lý nhưng không bóp chết ứng dụng lớn.
  • Monitoring cảnh báo khi quota dùng trên 80%.
  • Runbook xử lý lỗi exceeded quota đã được viết và chia sẻ cho developer.
  • Thay đổi quota đi qua GitOps hoặc quy trình review, không sửa tay tùy tiện.
  • Namespace platform/monitoring không bị quota app team ảnh hưởng.
  • Đã test rollback manifest quota trong môi trường staging.

14. Bài tập lab cuối bài

  1. Tạo namespace team-lab với quota CPU request 500m, memory request 512Mi và tối đa 3 Pod.
  2. Tạo LimitRange default request 100m/128Mi, default limit 300m/256Mi.
  3. Deploy nginx 2 replicas và kiểm tra quota used.
  4. Tăng replicas lên 10, ghi lại lỗi event.
  5. Sửa deployment để request thấp hơn hoặc tăng quota, sau đó xác minh rollout thành công.

Kết luận

Kubernetes Resource Quota LimitRange không phải cấu hình “cho đẹp” mà là lớp bảo vệ quan trọng khi nhiều team dùng chung cluster. ResourceQuota giúp giới hạn tổng tài nguyên theo namespace, LimitRange giúp đặt mặc định và biên min/max cho container. Khi kết hợp với monitoring, GitOps, autoscaler và runbook rõ ràng, bạn sẽ giảm đáng kể rủi ro một workload lỗi kéo sập tài nguyên của toàn môi trường production.

Tác giả: Mạnh Hoàng

Tôi là Hoàng Mạnh, người sáng lập blog SysadminSkills.com. Tôi viết về quản trị hệ thống, bảo mật máy chủ, DevOps và cách ứng dụng AI để tự động hóa công việc IT. Blog này là nơi tôi chia sẻ những gì đã học được từ thực tế – đơn giản, ngắn gọn và áp dụng được ngay.