LimitRange và ResourceQuota

K
Kai··6 min read

Bài 22 và 32 lo tài nguyên ở tầng pod và node. Còn một tầng nữa, tầng tổ chức: khi một cluster phục vụ nhiều team, làm sao ngăn team A xin 64Gi RAM cho một container, hay tạo 10.000 pod nuốt sạch cụm? Câu trả lời là hai object chính sách gắn vào namespace (Bài 28): LimitRange đặt luật cho từng pod/container, ResourceQuota đặt luật cho tổng cả namespace. Đây là viên gạch cuối Part VI, và là nền cho việc chia cụm đa-team.

Cả hai chạy được là nhờ hai admission controller LimitRangerResourceQuota — chúng nằm trong bộ mặc định của API server, nên dù Bài 7 ta chỉ thêm NodeRestriction, hai cái này vẫn bật (các test dưới đây chứng minh điều đó).

LimitRange: luật cho từng pod trong namespace

Tài liệu: "A LimitRange is a policy to constrain the resource allocations (limits and requests) that you can specify for each applicable object kind (such as Pod or PersistentVolumeClaim) in a namespace." Nó làm bốn việc: ép min/max compute mỗi pod/container, ép min/max storage mỗi PVC, ép tỉ lệ request:limit, và đặt mặc định request/limit rồi tự tiêm vào container không khai. Dựng trong một namespace riêng team-a:

apiVersion: v1
kind: LimitRange
metadata: {name: cpu-mem-limits, namespace: team-a}
spec:
  limits:
  - type: Container
    default:        {cpu: "200m", memory: "128Mi"}   # limit mặc định
    defaultRequest: {cpu: "100m", memory: "64Mi"}     # request mặc định
    max:            {cpu: "500m", memory: "256Mi"}    # trần mỗi container
    min:            {cpu: "50m",  memory: "32Mi"}     # sàn mỗi container

Tiêm default cho pod không khai

Tài liệu mô tả admission: "the LimitRange admission controller applies default request and limit values for all Pods (and their containers) that do not set compute resource requirements." Tạo một pod trần (không resources) trong namespace đó:

kubectl apply -f pod-nolimits.yaml      # pod 'nolimits', không khai resources
kubectl get pod nolimits -n team-a -o jsonpath='requests={.spec.containers[0].resources.requests} limits={.spec.containers[0].resources.limits}{"\n"}'
requests={"cpu":"100m","memory":"64Mi"} limits={"cpu":"200m","memory":"128Mi"}

Pod trần được tiêm đúng defaultRequest/default của LimitRange. Đây là cách ép mọi pod trong namespace request/limit dù lập trình viên quên khai, quan trọng vì pod không request sẽ là BestEffort (Bài 22), bị evict đầu tiên. LimitRange biến "quên khai" thành "có mặc định hợp lý".

Chặn pod vượt max

Việc thứ hai là canh min/max. Tạo pod xin cpu: 800m trong khi max: 500m:

kubectl apply -f pod-toobig.yaml        # requests/limits cpu = 800m
Error from server (Forbidden): error when creating "STDIN": pods "toobig" is forbidden:
maximum cpu usage per Container is 500m, but limit is 800m

403 Forbidden, đúng như tài liệu: "your request to the API server will fail with an HTTP status code 403 Forbidden and a message explaining the constraint." Pod bị chặn ngay lúc tạo (admission), không bao giờ chạy. Lưu ý: "LimitRange validations occur only at Pod admission stage, not on running Pods". Sửa LimitRange không ảnh hưởng pod đang chạy, chỉ áp cho pod tạo sau đó.

ResourceQuota: luật cho tổng namespace

LimitRange canh từng pod; nhưng 1000 pod hợp lệ vẫn nuốt sạch cụm. ResourceQuota canh tổng. Tài liệu: "A ResourceQuota object provides constraints that limit aggregate resource consumption per namespace. A ResourceQuota can also limit the quantity of objects that can be created in a namespace by API kind."

apiVersion: v1
kind: ResourceQuota
metadata: {name: team-a-quota, namespace: team-a}
spec:
  hard:
    requests.cpu: "400m"       # tổng request CPU mọi pod
    requests.memory: 512Mi
    limits.cpu: "1"            # tổng limit CPU
    limits.memory: 1Gi
    pods: "3"                  # tối đa 3 pod
    count/configmaps: "2"      # tối đa 2 ConfigMap

kubectl describe quota cho bảng Used vs Hard — tài nguyên/số object đã dùng so với trần:

kubectl describe quota team-a-quota -n team-a
Resource          Used   Hard
--------          ----   ----
count/configmaps  1      2
limits.cpu        200m   1
limits.memory     128Mi  1Gi
pods              1      3
requests.cpu      100m   400m
requests.memory   64Mi   512Mi

Pod nolimits đã tính vào quota: requests.cpu 100m, pods 1. Để ý sự ăn khớp với LimitRange: vì quota canh requests.cpu/memory, tài liệu cảnh báo "users must specify requests or limits ... otherwise the quota system may reject pod creation", nhưng LimitRange đã tự tiêm default, nên pod trần vẫn hợp lệ. Hai chính sách bổ nhau: LimitRange đảm bảo mọi pod số để quota đếm. (count/configmaps 1 là vì mỗi namespace tự có sẵn ConfigMap kube-root-ca.crt.)

Vượt quota → 403

Tạo pod tới khi chạm trần pods: 3:

# tạo p2, p3 (đạt pods = 3/3), rồi thử p4:
kubectl apply -f pod-p4.yaml
pod/p2 created
pod/p3 created
Error from server (Forbidden): error when creating "STDIN": pods "p4" is forbidden:
exceeded quota: team-a-quota, requested: pods=1, used: pods=3, limited: pods=3

Pod thứ tư bị chặn: "exceeded quota ... requested: pods=1, used: pods=3, limited: pods=3", thông điệp nói rõ xin bao nhiêu, đang dùng bao nhiêu, trần bao nhiêu. Tài liệu: "If creating or updating a resource violates a quota constraint, the control plane rejects that request with HTTP status code 403 Forbidden." Quota chặn cả theo số object (pods, count/configmaps, count/deployments.apps...) lẫn theo tổng tài nguyên (requests.cpu...) — pod thứ tư cũng sẽ bị chặn nếu nó đẩy requests.cpu quá 400m, dù số pod chưa đầy.

Còn một trục nữa đáng nhắc: PID. Tiến trình cũng là tài nguyên hữu hạn của node, một pod fork bom có thể làm cạn PID cả máy. Kubelet hỗ trợ giới hạn PID ở tầng node (--pod-max-pids) và ResourceQuota cũng đếm được count/*; phòng thủ PID đầy đủ thuộc về cấu hình node, nhưng tinh thần giống hệt: đặt trần để một workload không nuốt sạch tài nguyên chung.

🧹 Dọn dẹp

kubectl delete namespace team-a

Xóa namespace dọn sạch mọi thứ bên trong — pod, LimitRange, ResourceQuota, ConfigMap — trong một lệnh (đúng tinh thần namespace là phạm vi gom nhóm của Bài 28, và finalizer kubernetes của Bài 29 lo việc dọn tuần tự). Cụm về lại hai pod CoreDNS. Manifest ở github.com/nghiadaulau/kubernetes-from-scratch, thư mục 33-limitrange-quota.

Tổng kết

Hai chính sách gắn namespace để chia cụm đa-team. LimitRange canh từng pod/container: tiêm defaultRequest/default cho pod trần (ta thấy pod không khai được gắn 100m/64Mi request, 200m/128Mi limit), và chặn vượt min/max bằng 403 Forbidden ngay lúc admission (pod xin 800m > max 500m bị từ chối). ResourceQuota canh tổng namespace: trần cho tổng requests.*/limits.*số object (pods, count/*), hiển thị Used/Hard qua describe quota, và chặn bằng 403 exceeded quota khi tạo vượt (pod thứ tư bị chặn ở pods: 3). Hai cái bổ nhau, LimitRange đảm bảo pod request để ResourceQuota đếm, và cùng với PID limit ở tầng node tạo thành hàng rào để không workload nào nuốt sạch tài nguyên chung.

Hết Part VI. Part VII bước vào scheduling, câu hỏi "pod nào chạy ở node nào". Bài 34 mở đầu với scheduler và scheduling framework: kube-scheduler (ta đã dựng ở Bài 8) thực sự chọn node ra sao qua chuỗi filter rồi score, các plugin như NodeResourcesFit (chính cái dùng Allocatable của Bài 32), trước khi các bài sau đào affinity, taint, topology spread.