กลับไปยังบทความ

DevOps

Kubernetes Autoscaling: เข้าใจ HPA, VPA และ Cluster Autoscaler พร้อมตัวอย่างคำนวณ

Kubernetes autoscaling ทำงานสามชั้นยังไง ตั้งแต่สูตร HPA พร้อมตัวอย่างคำนวณ, การเลือก metric, ปรับ request ให้พอดีด้วย VPA, เพิ่ม node ด้วย Cluster Autoscaler และกับดักที่เจอบ่อยใน production

24 กันยายน 256910 นาทีในการอ่าน

kubernetes autoscaling hpa vpa cluster autoscaler

Kubernetes autoscaling ทำงานสามชั้น และแต่ละชั้นตอบคำถามคนละข้อ Horizontal Pod Autoscaler (HPA) ตัดสินว่าควรรันกี่ pod, Vertical Pod Autoscaler (VPA) ตัดสินว่าแต่ละ pod ควร request CPU และ memory เท่าไร ส่วน Cluster Autoscaler ตัดสินว่าคลัสเตอร์ต้องมีกี่ node ถึงจะวาง pod เหล่านั้นได้ครบ ทั้งสามเป็น controller แยกกันที่ไม่ได้ประสานงานกันโดยตรง และปัญหา autoscaling ส่วนใหญ่เกิดจากการไม่รู้ว่าชั้นหนึ่งจบตรงไหน อีกชั้นเริ่มตรงไหน

บทความนี้ไล่ตั้งแต่สูตรของ HPA พร้อมตัวอย่างคำนวณ, metric ที่ HPA ใช้ได้, VPA ทำอะไรและเมื่อไรควรใช้แค่โหมดแนะนำค่า, Cluster Autoscaler ตอบสนองต่อ pod ที่ค้าง Pending ยังไง และกับดักที่เจอบ่อยใน production

สามชั้นของ Kubernetes autoscaling

ชั้นปรับอะไรถูกกระตุ้นด้วยมากับ Kubernetes ไหม
HPAจำนวน replica ของ Deployment หรือ StatefulSetmetric เทียบกับเป้า (CPU, memory, custom, external)มี (autoscaling/v2) ต้องมี metrics-server
VPArequests (และ limits) ของ CPU และ memoryการใช้งานจริงที่สังเกตตามเวลาไม่มี ต้องติดตั้งแยก
Cluster Autoscalerจำนวน nodepod ค้าง Pending หรือ node ที่ใช้งานน้อยไม่มี ติดตั้งตาม cloud หรือ platform

ทั้งสามชั้นพึ่ง requests ของ resource ทั้งหมด HPA วัด CPU utilization เป็นเปอร์เซ็นต์ของ request, VPA มีหน้าที่ตั้ง request โดยตรง และ Cluster Autoscaler เพิ่ม node จาก request ไม่ใช่การใช้งานจริง ถ้า request ผิด ทุกชั้นจะตัดสินใจผิดตามไปด้วย วิธีตั้งค่าให้เหมาะอ่านได้ใน Kubernetes requests, limits และ QoS

HPA ทำงานยังไง: control loop และสูตรคำนวณ

HPA เป็น control loop เหมือน controller ตัวอื่นใน Kubernetes ค่า default คือวนทุก 15 วินาที แต่ละรอบอ่าน metric ปัจจุบัน คำนวณจำนวน replica ที่ควรมี แล้วอัปเดต field replicas ของเป้าหมาย

สูตรหลักคือ:

text
desiredReplicas = ceil( currentReplicas × currentMetricValue / targetMetricValue )

ตัวอย่างการคำนวณ

มี 3 pod แต่ละตัว request CPU 500m และตอนนี้ใช้อยู่ 450m คือ utilization 90% โดยตั้งเป้าไว้ที่ 50%

ให้คิดเป็น "งานรวม" สาม pod ที่ทำงาน 90% เท่ากับงาน 3 × 90 = 270 หน่วย ถ้าอยากให้ทุก pod ลดลงเหลือ 50% ต้องใช้ 270 ÷ 50 = 5.4 pod แต่รัน 0.4 pod ไม่ได้ HPA จึงปัดขึ้น:

text
desiredReplicas = ceil(3 × 90 / 50) = ceil(5.4) = 6

เมื่อมี 6 pod ช่วยกันรับโหลดเท่าเดิม utilization เฉลี่ยจะลดเหลือ 270 ÷ 6 = 45% ต่ำกว่าเป้าเล็กน้อย

มีสองรายละเอียดที่ทำให้พฤติกรรมจริงนุ่มนวลกว่าสูตรดิบ:

  • Tolerance ถ้าอัตราส่วน current / target ห่างจาก 1.0 ไม่เกิน 10% (อยู่ระหว่าง 0.9 ถึง 1.1) HPA จะไม่ทำอะไร เช่น 53% เทียบเป้า 50% ได้อัตราส่วน 1.06 จึงไม่เปลี่ยน ช่วยกันไม่ให้ปรับขึ้นลงถี่ ๆ
  • หลาย metric ถ้าตั้งทั้ง CPU และ requests-per-second HPA จะคำนวณจำนวนที่ต้องการจากแต่ละตัว แล้วเลือกค่าที่ สูงที่สุด

เพราะ utilization คิดเทียบกับ request ค่าจึงเกิน 100% ได้ pod ที่ request 500m แต่ limit 1 CPU อาจรายงาน 200% ซึ่งเป็นเรื่องปกติ และเป็นอีกเหตุผลที่ request ควรสะท้อนการใช้งานจริง

HPA manifest สำหรับ production

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
        - type: Percent
          value: 100
          periodSeconds: 30
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 20
          periodSeconds: 60

block behavior คุมความเร็วในการขยับ แนวที่นิยมคือ scale up เร็ว scale down ช้า ในตัวอย่างนี้ HPA เพิ่ม pod ได้เท่าตัวทุก 30 วินาที แต่ลดได้ไม่เกิน 20% ต่อนาที และจะลดก็ต่อเมื่อค่าที่แนะนำต่ำต่อเนื่องครบ 5 นาที ค่า default ของ scale-down stabilization window ก็คือ 300 วินาทีอยู่แล้ว แต่เขียนไว้ชัด ๆ จะได้เห็นเจตนา

ดูว่า HPA เห็นอะไรอยู่:

bash
kubectl get hpa api
kubectl describe hpa api   # shows current metrics, conditions and recent scaling events

ถ้าช่อง TARGETS ขึ้น <unknown> แปลว่า metrics-server ไม่ทำงาน หรือ pod ไม่ได้ตั้ง CPU request

เลือก metric ให้ HPA: resource, custom และ external

CPU เป็นค่า default เพราะมีให้ใช้เสมอ แต่ไม่ได้เป็นสัญญาณที่ถูกต้องทุกครั้ง

ชนิด metricตัวอย่างแหล่งข้อมูล
ResourceCPU หรือ memory utilization ทั้ง podmetrics-server
ContainerResourceCPU เฉพาะ container ของแอป ไม่นับ sidecarmetrics-server
Podsrequests per second ต่อ podCustom metrics API (เช่น Prometheus Adapter)
Objectrequests per second ที่ IngressCustom metrics API
Externalความยาวคิวใน RabbitMQ, SQS หรือ consumer lag ของ KafkaExternal metrics API (เช่น KEDA)

หลักคิดคร่าว ๆ:

  • web service ที่กิน CPU: ใช้ CPU utilization ได้ดี
  • service ที่รอ I/O เป็นหลัก: CPU ต่ำตลอดแต่ latency พุ่ง ควร scale ตาม requests per second หรือจำนวน request ที่ค้างอยู่
  • worker ที่ดึงงานจากคิว: scale ตาม backlog ส่วนใหญ่ใช้ KEDA ซึ่ง scale Deployment ลงเหลือศูนย์ได้ตอนคิวว่าง
  • memory: เป็นสัญญาณที่อ่อนสำหรับ runtime ส่วนใหญ่ JVM, Go และ Node.js มักไม่คืน memory หลังโหลดลด HPA จึง scale ขึ้นแล้วไม่ลงอีก

ต้องเก็บ metric เหล่านี้ได้ก่อนถึงจะ scale ตามได้ ส่วนนี้อ่านต่อได้ที่ observability สำหรับ microservices

Vertical Pod Autoscaler: ตั้ง request ให้พอดี

คนส่วนใหญ่ตั้ง request ด้วยการเดา ตั้งสูงไป node ก็ดูเต็มทั้งที่ว่าง เปลืองเงิน ตั้งต่ำไป pod ก็โดน CPU throttle หรือโดน OOMKill VPA คอยดูการใช้งานจริงตามเวลา แล้วแนะนำหรือปรับค่าที่เหมาะกว่าให้

VPA มีสามส่วน:

  • Recommender: วิเคราะห์ประวัติการใช้งานแล้วคำนวณค่าเป้าหมาย
  • Updater: evict pod ที่ request ห่างจากค่าแนะนำมาก ให้เกิดใหม่พร้อมค่าใหม่
  • Admission controller: เขียนทับ request ของ pod ใหม่ตอนถูกสร้าง
yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: api
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  updatePolicy:
    updateMode: "Off"
  resourcePolicy:
    containerPolicies:
      - containerName: "*"
        minAllowed:
          cpu: 100m
          memory: 128Mi
        maxAllowed:
          cpu: "2"
          memory: 2Gi

ค่า updateMode:

  • Off แค่ให้คำแนะนำ อ่านได้จาก kubectl describe vpa api ในส่วน Recommendation แล้วเราเอาไปแก้ manifest เอง
  • Initial ใช้ค่าแนะนำเฉพาะตอนสร้าง pod
  • Recreate (และ Auto) จะ evict pod ที่รันอยู่เพื่อใส่ค่าใหม่

VPA เวอร์ชันใหม่ ๆ มีโหมด InPlaceOrRecreate เพิ่มมาด้วย ซึ่งใช้ in-place pod resize ปรับ resource โดยไม่ต้อง restart pod ถ้าคลัสเตอร์รองรับ

เริ่มจากโหมด Off ปลอดภัยที่สุด ได้ตัวเลขที่อิงข้อมูลจริงโดยไม่มี pod โดน evict แบบไม่ทันตั้งตัว

อย่าให้ HPA และ VPA ปรับตาม CPU หรือ memory ของ workload เดียวกันพร้อมกัน เมื่อโหลดขึ้น HPA เพิ่ม pod การใช้งานต่อ pod ลดลง VPA ก็ลด request ลง utilization ที่คิดเป็นเปอร์เซ็นต์ของ request ที่เล็กลงก็กลับขึ้นไปอีก สอง controller ก็ตีกันไปมา ถ้าต้องใช้ทั้งคู่ ให้ HPA ใช้ custom metric อย่าง requests per second แล้วปล่อย CPU และ memory ให้ VPA หรือใช้ VPA แค่โหมด Off

Cluster Autoscaler: เพิ่มและลด node

HPA สร้าง pod ได้ แต่ถ้าไม่มี node ไหนมีที่พอกับ request ของ pod เหล่านั้น pod ก็ค้างอยู่ที่ Pending Cluster Autoscaler คอยดูสถานการณ์นี้โดยเฉพาะ:

  1. Scale up: เมื่อมี pod ที่ schedule ไม่ได้เพราะทรัพยากรไม่พอ Cluster Autoscaler จะจำลองดูว่าถ้าเพิ่ม node จาก node group ที่มีอยู่ pod จะลงได้ไหม ถ้าได้ก็ขอ node ใหม่จาก cloud provider
  2. Scale down: เมื่อ resource ที่ถูก request บน node ต่ำกว่าเกณฑ์ (default 50%) ต่อเนื่องนานพอ (default 10 นาที) และ pod ทุกตัวบน node ย้ายไปที่อื่นได้ node นั้นจะถูก drain แล้วลบทิ้ง

สิ่งที่ขวางการ scale down คือ pod ที่ย้ายอย่างปลอดภัยไม่ได้ เช่น PodDisruptionBudget ที่ไม่ยอมให้ disrupt เลย, pod เปล่าที่ไม่มี controller ดูแล, pod ที่มี annotation cluster-autoscaler.kubernetes.io/safe-to-evict: "false" และ pod บางตัวใน kube-system

บน AWS และ Azure มี Karpenter เป็นทางเลือกยอดนิยม แทนที่จะ scale node group ที่กำหนดไว้ล่วงหน้า มันเลือก instance type ที่พอดีกับ pod ที่ค้างอยู่โดยตรง แต่ตัวกระตุ้นยังเหมือนเดิม คือ pod ที่ Pending และ request ของมัน

สามตัวนี้ทำงานร่วมกันยังไง

เมื่อ traffic พุ่ง ลำดับเหตุการณ์จะเป็นแบบนี้:

  1. โหลดเพิ่ม CPU utilization ขึ้นเกินเป้า
  2. HPA เพิ่ม replicas ภายใน 15 ถึง 60 วินาที
  3. pod ใหม่ที่ลง node เดิมได้จะเริ่มทันที
  4. pod ที่ลงไม่ได้จะค้าง Pending แล้ว Cluster Autoscaler ขอ node เพิ่ม
  5. node ใหม่ boot เข้าร่วมคลัสเตอร์ และ pull image ซึ่งใช้เวลาเป็นนาที
  6. pod ผ่าน readiness probe แล้วเริ่มรับ traffic

การ scale pod ใช้เวลาเป็นวินาที ส่วนการ scale node ใช้เวลาเป็นนาที ถ้า traffic พุ่งเร็วกว่าเวลาที่ใช้สร้าง node ต้องเผื่อที่ว่างไว้ ด้วยการตั้ง minReplicas สูงขึ้น หรือทำ overprovisioning คือรัน placeholder pod ที่ priority ต่ำไว้จองที่ เมื่อ pod จริงมา มันจะ preempt placeholder ทันที แล้ว placeholder ที่ถูกไล่ออกก็ไปค้าง Pending ทำให้ Cluster Autoscaler เพิ่ม node ล่วงหน้าก่อนที่จะขาดจริง

กับดักที่พบบ่อยใน Kubernetes autoscaling

  • ไม่ตั้ง CPU request HPA คำนวณ utilization ไม่ได้ และ Cluster Autoscaler วางแผน capacity ไม่ได้
  • ตั้ง replicas ไว้ใน Deployment manifest ทุกครั้งที่ kubectl apply หรือ GitOps sync จำนวนจะถูกรีเซ็ตและตีกับ HPA เมื่อ HPA ดูแล workload แล้วให้ลบ spec.replicas ออก
  • HPA และ VPA ใช้ metric เดียวกัน ตามที่อธิบายไปข้างบน
  • แอป start ช้า ถ้า pod ใช้ 90 วินาทีกว่าจะพร้อม การรอ scale ตอน CPU 90% ถือว่าช้าไป ให้ลดเป้าลงหรือเพิ่ม minReplicas
  • เพดานที่มองไม่เห็น maxReplicas, จำนวน node สูงสุดของ Cluster Autoscaler และ quota ของ cloud ล้วนจำกัดการ scale แบบเงียบ ๆ ควรมี alert เมื่อชนเพดาน
  • scale ผิดชั้น เพิ่ม pod ของ API ไม่ช่วยอะไรถ้าคอขวดอยู่ที่ database และอาจแย่ลงเพราะเปิด connection เพิ่ม
  • PDB เข้มเกินไป ตั้ง minAvailable เท่ากับจำนวน replica ทำให้ drain node ไม่ได้เลย และไม่มีวัน scale down

คำถามที่พบบ่อย

HPA กับ VPA ต่างกันยังไง

HPA เปลี่ยนจำนวน pod ส่วน VPA เปลี่ยน CPU และ memory ที่แต่ละ pod request HPA ใช้รับโหลดที่กระจายไปหลาย replica ได้ ส่วน VPA ใช้แก้ pod ที่ตั้งขนาดไว้ผิด

ทำไม HPA ขึ้น <unknown> ที่ช่อง targets

ส่วนใหญ่เป็นเพราะไม่ได้ติดตั้ง metrics-server หรือมันไม่ทำงาน หรือ pod เป้าหมายไม่ได้ตั้ง CPU request ลองรัน kubectl top pods และดู block resources ของ container

HPA scale ลงเหลือศูนย์ได้ไหม

การตั้งค่ามาตรฐานทำไม่ได้ minReplicas ต้องเป็นอย่างน้อย 1 เว้นแต่จะเปิด feature gate ที่ยังเป็น alpha ถ้าต้องการ scale-to-zero สำหรับงานแบบ event-driven ให้ใช้ KEDA

Cluster Autoscaler ใช้เวลาเพิ่ม node นานแค่ไหน

การตัดสินใจเร็ว ปกติไม่ถึงนาที เวลาส่วนใหญ่หมดไปกับการที่ cloud provider boot VM, node เข้าร่วมคลัสเตอร์ และการ pull image รวมแล้วควรเผื่อไว้หลายนาที

ควรใช้ CPU หรือ memory กับ HPA

service ส่วนใหญ่ใช้ CPU หรือ request rate memory แทบไม่ลดลงเมื่อโหลดลด HPA ที่อิง memory จึงมักขึ้นแล้วค้างอยู่อย่างนั้น

เช็กลิสต์ autoscaling

  • ทุก container มี CPU และ memory request ที่สมจริง ใช้ VPA โหมด Off ช่วยหาค่า
  • HPA ของงานที่ผู้ใช้เข้าถึงตั้ง minReplicas อย่างน้อย 2 หรือ 3 และ scale down ช้ากว่า scale up
  • metric ตรงกับคอขวดจริง ไม่ว่าจะเป็น CPU, request rate หรือความยาวคิว
  • ลบ spec.replicas ออกจาก manifest ที่ HPA ดูแล
  • ตั้ง Cluster Autoscaler หรือ Karpenter พร้อม alert เมื่อชนจำนวน node สูงสุดหรือ quota
  • PDB ยอมให้ disrupt ได้อย่างน้อยหนึ่งตัว

autoscaling ทำงานได้ดีเมื่อทั้งสามชั้นได้ request ที่ถูกต้องและใช้สัญญาณที่ใช่ ถ้าอยากฝึกปรับ HPA และ Cluster Autoscaler บนคลัสเตอร์จริง ลองดู คอร์ส DevOps ของ Vectorkub

พร้อมสร้างสิ่งที่ยิ่งใหญ่หรือยัง?

เล่าโปรเจคของคุณให้เราฟัง — เราจะพาคุณไปถึง

เลือกคุกกี้ที่เราใช้ได้ คุกกี้ที่จำเป็นช่วยให้เว็บไซต์ทำงานได้และเปิดใช้งานเสมอ