Có một kiểu bực bội rất riêng khi gỡ lỗi hệ thống nhiều tầng: bạn bấm một nút, nó báo "xong", nhưng thứ bạn muốn lại không xảy ra — và lời giải thích nằm ở một nơi bạn không hề nhìn tới. Giống gửi một lá đơn qua bưu điện: quầy nhận đóng dấu "đã tiếp nhận", bạn yên tâm ra về, còn lá đơn thì bị phòng xử lý bên trong trả lại vì thiếu chữ ký — và cái phiếu báo lỗi ấy rơi vào một ngăn kéo bạn chẳng bao giờ mở. ResourceQuota của Kubernetes tạo ra đúng cái bẫy đó. Namespace chia không gian tên (phần 59: nó không chia không gian mạng); ResourceQuota biến nó thành ranh giới thật về tài nguyên. Bài này đo xem nó chặn được gì, và chặn ở đâu.
Quota bắt buộc mọi pod phải khai requests
Namespace có ResourceQuota với requests.cpu và requests.memory, rồi tạo deployment không khai gì:
deployment.apps/a created <- TẠO ĐƯỢC
số pod: 0 <- KHÔNG có pod nào
Error creating: pods "a-..." is forbidden: failed quota:
must specify requests.cpu for: nginx
Quota chặn ở tầng pod, không chặn ở tầng Deployment. Đây là chỗ gây bối rối nhất: kubectl create deployment báo thành công, kubectl get deployment hiện 0/1, và không nói vì sao. Lý do nằm trong event của ReplicaSet:
kubectl -n <ns> get events --field-selector reason=FailedCreate
Tác dụng phụ này thực ra rất hữu ích: có quota về CPU hoặc RAM nghĩa là không pod nào trong namespace được phép thiếu requests — tức không còn BestEffort nào (phần 55).
LimitRange khai hộ
apiVersion: v1
kind: LimitRange
spec:
limits:
- type: Container
default: {cpu: 200m, memory: 256Mi}
defaultRequest: {cpu: 100m, memory: 128Mi}
Sau khi thêm, cùng deployment đó chạy được:
{"requests":{"cpu":"100m","memory":"128Mi"},
"limits":{"cpu":"200m","memory":"256Mi"}}
ResourceQuota và LimitRange đi với nhau: quota đòi phải khai, LimitRange khai hộ. Thiếu cái thứ hai là mọi deployment cũ trong namespace đều gãy ngay khi bạn bật quota. Đây là thứ tự đúng khi áp quota lên namespace đang chạy: LimitRange trước, ResourceQuota sau.
Bốn loại giới hạn, bốn cách chặn
pods: 5 — scale --replicas=8 cho ra đúng 5 pod. Deployment không báo lỗi, chỉ đơn giản không đủ bản.
count/deployments.apps: 2
tạo deployment b -> created
tạo deployment c -> forbidden: exceeded quota
Lỗi hiện ngay trên kubectl, khác hẳn trường hợp pod.
persistentvolumeclaims: 1 — p1 tạo được, p2 bị từ chối ngay.
requests.cpu / requests.memory — cộng dồn của mọi pod:
đã dùng: requests.cpu=500m requests.memory=640Mi pods=5
Nguyên tắc chung rút ra:
Giới hạn trên object báo lỗi ngay cho người gõ lệnh. Giới hạn trên pod báo lỗi trong event của ReplicaSet — chỗ ít ai nhìn.
Nên với namespace dùng chung, đặt cả hai loại: count/deployments.apps cho phản hồi nhanh, và requests.cpu cho giới hạn thật.
Quota tính theo requests, không theo mức dùng
requests.cpu: 2 nghĩa là tổng requests của mọi pod không vượt 2 nhân — không liên quan tới CPU thật sự dùng. Đây là hệ quả trực tiếp của phần 47: bộ lập lịch cũng cộng requests. Một namespace khai requests thừa sẽ chạm quota trong khi CPU thật gần như rỗi. Đó là lý do VPA ở chế độ Off (phần 67) rất hữu ích với môi trường có quota: nó cho bạn con số requests gần đúng, và đó là tiền thật.
Xoá quota là bỏ giới hạn ngay lập tức
xoá ResourceQuota -> tạo deployment e -> created
số pod sau khi xoá quota: 7
Không có giai đoạn chuyển tiếp. Pod đang bị chặn được tạo ngay. Điều đó cũng có nghĩa: ai có quyền xoá ResourceQuota thì quota không giới hạn được họ. Phần sau về RBAC là nửa còn lại của bài toán.
Namespace giới hạn được gì, không giới hạn được gì
Có:
- Tài nguyên tính toán, qua
ResourceQuota - Số lượng object
- Phân quyền, qua
RolevàRoleBinding(phần sau) - Chính sách mạng, qua
NetworkPolicy(phần 59)
Không:
- Mạng — mặc định mọi pod gọi được mọi pod xuyên namespace (phần 59)
- Node — pod của hai namespace nằm chung node và tranh CPU thật
- Tên node, PV, StorageClass, ClusterRole — đây là object cấp cụm, không thuộc namespace nào
Vế thứ hai đáng nhớ: hai đội trong hai namespace vẫn ảnh hưởng lẫn nhau qua tài nguyên node. ResourceQuota chỉ giới hạn requests, còn một pod dùng vượt requests vẫn ăn CPU của hàng xóm (phần 53). Muốn cách ly thật thì cần node riêng — taint cộng nodeSelector (phần 69).
Ba loại object nên đặt quota
Ngoài CPU và RAM, hai loại hay bị bỏ sót:
spec:
hard:
count/secrets: "50"
services.nodeports: "0"
services.loadbalancers: "2"
services.nodeports: "0" chặn hẳn việc mở cổng trên node — thứ mà một Service NodePort vô tình có thể phơi ra Internet. services.loadbalancers chặn chi phí: mỗi LoadBalancer trên đám mây là một hoá đơn.
Mẫu số chung: nơi lỗi hiện ra không phải nơi bạn ra lệnh, và một rào chỉ chắc bằng quyền gỡ nó
Bài này gói hai bài học đúng ở mọi hệ nhiều tầng và bất đồng bộ. Thứ nhất: khi một thao tác đi qua nhiều tầng, lời từ chối ở tầng sâu không dội ngược về chỗ bạn gõ lệnh — thao tác cấp cao báo "thành công", còn ý định thật thất bại lặng lẽ ở một nơi khác. Bạn tạo Deployment (OK), nhưng ReplicaSet mới là kẻ tạo pod, và event lỗi của nó nằm trong một ngăn kéo bạn chưa mở. Đây là bản chất của mọi luồng khai báo/bất đồng bộ: kubectl apply thành công mà pod không bao giờ lên lịch; một message được broker "chấp nhận" nhưng consumer từ chối và rơi vào dead-letter — chỗ không ai bắt lúc gửi; một bản ghi được replica ACK nhưng lỗi lúc áp dụng trên bản sao; một email "đã gửi" thật ra chỉ mới xếp hàng rồi bị bật lại. Bài học: "đã tiếp nhận" không phải "đã thành công" — hãy tìm cái tầng thực sự thực thi và theo dõi lỗi ở đó, đừng tin vào tờ biên nhận ở quầy.
Thứ hai: một cơ chế kiểm soát chỉ mạnh ngang cái quyền để gỡ bỏ nó. Xoá ResourceQuota là bỏ giới hạn tức thì, nên "ai xoá được quota thì quota không giới hạn họ". Cùng lỗ hổng với một khoá mà người có quyền admin gỡ được trong một cú bấm, một cờ chỉ-đọc mà chính người dùng bật/tắt được, một rate-limit mà kẻ cấu hình vượt qua dễ dàng. Canh cửa X mà quên canh "quyền tháo cái chốt trên X" thì cái chốt chỉ là trang trí — phải canh luôn cái canh.
Mẫu số chung: trong hệ nhiều tầng, đừng đọc "thành công" ở tầng trên là xong — đi tới tầng thực thi mà xem lỗi; và khi dựng một rào chắn, hãy chắc rằng quyền gỡ rào cũng được canh chặt như chính rào đó.
Muốn tự soi, xem namespace nào của bạn không có giới hạn nào:
for ns in $(kubectl get ns -o name | cut -d/ -f2); do
q=$(kubectl -n $ns get resourcequota --no-headers 2>/dev/null | wc -l)
l=$(kubectl -n $ns get limitrange --no-headers 2>/dev/null | wc -l)
[ "$q" = "0" ] && echo "$ns quota=$q limitrange=$l"
done
Mỗi dòng là một namespace mà một deployment gõ nhầm replicas: 1000 sẽ chạy tới khi hết cụm.
Phần sau đo RBAC: quyền đọc Secret nghĩa là đọc được mật khẩu ở dạng rõ.