Vertical Pod Autoscaler và resource manager

K
Kai··6 min read

Bài 39 scale ngang — thêm pod khi tải lên. Còn scale dọc: thay vì thêm pod, chỉnh đúng lượng tài nguyên mỗi pod. Tài liệu phân biệt: "you can either increase or decrease the number of replicas ... or adjust the resources available to the replicas in-place. The first approach is referred to as horizontal scaling, while the second is referred to as vertical scaling." Bài này có hai phần ăn khớp với scale dọc: VerticalPodAutoscaler (right-size request/limit của pod theo usage thật) và — sang phía node — các resource manager của kubelet, mà ta sẽ test cụ thể CPU Manager static policy (ghim lõi CPU độc quyền cho pod Guaranteed).

VPA là add-on, không phải core

Như Metrics Server, VPA không có sẵn. Tài liệu: "Unlike the HPA, the VPA doesn't come with Kubernetes by default, but is an add-on that you or a cluster administrator may need to deploy ... You will need to have the Metrics Server installed." (May là ta đã có Metrics Server từ Bài 39.) VPA gồm ba thành phần — recommender (quan sát usage, đưa khuyến nghị), updater (đuổi pod để áp khuyến nghị), admission controller (sửa request lúc pod tạo). Ta chỉ cần recommender cho chế độ "chỉ khuyến nghị":

# CRD + RBAC + recommender (vpa-recommender:1.6.0), từ repo autoscaler
kubectl apply -f vpa-v1-crd-gen.yaml      # CRD VerticalPodAutoscaler
kubectl apply -f vpa-rbac.yaml
kubectl apply -f recommender-deployment.yaml

CRD verticalpodautoscalers.autoscaling.k8s.io cho ta tạo object VPA. Dựng một Deployment cố tình đặt request thấp (50m CPU, 16Mi) nhưng container ngốn nhiều (vòng dd đốt CPU), kèm một VPA updateMode: "Off":

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata: {name: vpa-demo}
spec:
  targetRef: {apiVersion: apps/v1, kind: Deployment, name: vpa-demo}
  updatePolicy: {updateMode: "Off"}        # chỉ khuyến nghị, KHÔNG tự sửa

updateMode: "Off" là chế độ an toàn nhất — VPA chỉ ghi khuyến nghị vào status, không đụng pod (các mode khác: Initial đặt request lúc tạo, Auto/Recreate đuổi pod để áp). Chờ recommender gom đủ metrics:

kubectl get vpa vpa-demo -o jsonpath='{.status.recommendation.containerRecommendations[0]}'
target={"cpu":"182m","memory":"250Mi"}
lowerBound={"cpu":"25m","memory":"250Mi"}
upperBound={"cpu":"1209782m", ...}

VPA quan sát workload thật và khuyến nghị target: cpu 182m, memory 250Mi — cao hơn hẳn request hiện tại 50m/16Mi, vì vòng dd thực sự ngốn ~182m CPU và 250Mi. Đây là giá trị của VPA: thay vì đoán request rồi đặt bừa (thừa thì lãng phí Allocatable của Bài 32, thiếu thì bị OOM/throttle), nó đo và đề xuất con số đúng — kèm lowerBound/upperBound (khoảng tin cậy). Kiểm chứng nó không sửa pod ở mode Off:

kubectl get deployment vpa-demo -o jsonpath='{.spec.template.spec.containers[0].resources.requests}'
# {"cpu":"50m","memory":"16Mi"}     <- vẫn nguyên

Request giữ 50m/16Mi — pod đang chạy không bị động. Trong thực tế, đội vận hành đọc khuyến nghị này rồi tự sửa manifest (an toàn, hợp GitOps), hoặc bật Auto để VPA tự áp (nhưng Auto đuổi và tạo lại pod để đổi request — gây gián đoạn; lưu ý tài liệu: tới v1.36 VPA chưa hỗ trợ resize in-place, dù tính năng resize thủ công in-place đã có). HPA và VPA không nên cùng quản CPU/memory một workload (đá nhau) — VPA hợp với workload khó scale ngang (stateful, single-instance).

Resource manager: CPU Manager static policy

VPA chỉnh con số request. Sang một tầng sâu hơn — cách kubelet đặt pod lên CPU vật lý. Mặc định (policy none), tài liệu: "the kubelet uses CFS quota to enforce pod CPU limits ... the workload can move to different CPU cores." — pod nhảy lõi tùy lúc, có overhead chuyển ngữ cảnh và cache. Với workload nhạy độ trễ (low-latency, real-time), ta muốn ghim pod vào lõi cố định. Đó là CPU Manager static policy.

Tài liệu nêu điều kiện chính xác để được lõi độc quyền: "Only containers that are both part of a Guaranteed pod and have integer CPU requests are assigned exclusive CPUs." — phải là pod Guaranteed (Bài 22) request CPU là số nguyên. Bật trên worker-0 (lưu ý: đổi policy phải xóa file state cũ, kẻo kubelet không khởi động):

ssh worker-0 'printf "cpuManagerPolicy: static\nkubeReserved:\n  cpu: \"200m\"\n  memory: \"256Mi\"\n" | sudo tee -a /var/lib/kubelet/kubelet-config.yaml
  sudo rm -f /var/lib/kubelet/cpu_manager_state      # bắt buộc khi đổi policy
  sudo systemctl restart kubelet'
ssh worker-0 'sudo cat /var/lib/kubelet/cpu_manager_state'
# {"policyName":"static","defaultCpuSet":"0-1",...}

State file xác nhận policy static, defaultCpuSet: 0-1shared pool ban đầu gồm cả hai CPU (node có 2 vCPU). Giờ thả hai pod lên worker-0: một Guaranteed request CPU nguyên (=1), một Burstable request phân số (200m):

# pinned: requests==limits cpu=1, memory=128Mi  => Guaranteed, cpu nguyên
# shared: requests cpu=200m                      => Burstable, cpu phân số
echo "pinned: $(kubectl exec pinned -- cat /sys/fs/cgroup/cpuset.cpus.effective)"
echo "shared: $(kubectl exec shared -- cat /sys/fs/cgroup/cpuset.cpus.effective)"
ssh worker-0 'sudo cat /var/lib/kubelet/cpu_manager_state'
pinned: 1
shared: 0
{"policyName":"static","defaultCpuSet":"0","entries":{"...":{"c":"1"}},...}

Đọc kết quả: pod pinned (Guaranteed, cpu nguyên) được cấp CPU 1 độc quyềncpuset.cpus.effective = 1. Shared pool co lại còn CPU 0 (defaultCpuSet từ 0-1 xuống 0), và state file ghi rõ container của pinned giữ "c":"1". Pod shared (Burstable) chạy trong shared pool — cpuset = 0, tài liệu xác nhận: "Containers in Guaranteed pods with fractional CPU requests also run on CPUs in the shared pool." (cùng chỗ với mọi pod không-độc-quyền). Từ giờ pinned chỉ chạy trên CPU 1, không pod nào khác đụng vào — đúng thứ workload nhạy độ trễ cần.

Hai resource manager còn lại (chỉ điểm danh, cơ chế tương tự ở tầng NUMA): Memory Manager đảm bảo bộ nhớ pod nằm trên cùng node NUMA với CPU nó được ghim; Topology Manager điều phối CPU Manager + Memory Manager + device plugin (Bài 61) để mọi tài nguyên một pod cùng một miền NUMA — tối quan trọng cho HPC/AI workload cần băng thông bộ nhớ tối đa.

🧹 Dọn dẹp

kubectl delete pod pinned shared --now
# revert CPU Manager (lại phải xóa state file vì đổi policy về none):
ssh worker-0 'sudo mv /var/lib/kubelet/kubelet-config.yaml.bak /var/lib/kubelet/kubelet-config.yaml
  sudo rm -f /var/lib/kubelet/cpu_manager_state && sudo systemctl restart kubelet'
# gỡ VPA (không cần cho các bài sau; GIỮ Metrics Server):
kubectl delete -f recommender-deployment.yaml -f vpa-rbac.yaml -f vpa-v1-crd-gen.yaml

Đã revert worker-0 về policy none (node Ready lại) và gỡ VPA (giữ Metrics Server cho các bài sau). Cụm về CoreDNS + metrics-server. Manifest ở github.com/nghiadaulau/kubernetes-from-scratch, thư mục 40-vpa-cpumanager.

Tổng kết

Scale dọc gồm hai tầng. VerticalPodAutoscaler (add-on, cần Metrics Server) right-size request/limit theo usage thật: ta cài recommender, tạo VPA updateMode: Off, và nó khuyến nghị cpu 182m / memory 250Mi cho một workload đặt request 50m/16Mikhông sửa pod ở mode Off (mode Auto thì đuổi-tạo-lại để áp; chưa resize in-place tới v1.36); đừng để VPA và HPA cùng quản CPU/memory một workload. Sang phía node, CPU Manager static policy ghim lõi CPU độc quyền cho pod Guaranteed có CPU nguyên: ta thấy pod pinned được cấp riêng CPU 1 (cpuset=1), shared pool co lại còn CPU 0, state file ghi sổ rõ ràng — workload nhạy độ trễ hết cảnh nhảy lõi. Memory/Topology Manager mở rộng ý tưởng đó lên căn chỉnh NUMA.

Hết Part VIII. Part IX bước vào storage — và đây là lúc cần dò kỹ cái gì tạo ra cái gì: Bài 41 mở đầu với volume, ephemeral volume, projected volume — các kiểu volume gắn vào pod (emptyDir, hostPath, configMap/secret như Bài 31, projected gộp nhiều nguồn), bước đệm trước khi vào PV/PVC/StorageClass và CSI ở các bài sau.

Related Posts