HPA Custom Metrics คืออะไร? พลิกโฉม Autoscaling บน Kubernetes ด้วย Prometheus Metrics
ในโลกของ Microservices และการประมวลผลแบบคลาวด์-เนทีฟ (Cloud-Native) ความสามารถในการปรับขนาดทรัพยากรได้อย่างอัตโนมัติและแม่นยำถือเป็นหัวใจสำคัญของการรักษาประสิทธิภาพและควบคุมต้นทุน Kubernetes (K8s) มอบเครื่องมือทรงพลังอย่าง Horizontal Pod Autoscaler (HPA) ให้เราใช้ แต่ HPA พื้นฐานที่ปรับขนาดตาม CPU และ Memory ใช่ว่าจะเพียงพอสำหรับทุกสถานการณ์เสมอไป วันนี้เราจะเจาะลึกไปอีกขั้นกับ HPA Custom Metrics และการผนวกพลังของ Prometheus เพื่อสร้างระบบ Autoscaling ที่ตอบโจทย์ธุรกิจและเทคนิคได้อย่างแท้จริง
ทำความรู้จักกับ Horizontal Pod Autoscaler (HPA) พื้นฐาน
Horizontal Pod Autoscaler (HPA) เป็นตัวควบคุม (Controller) ใน Kubernetes ที่ทำหน้าที่เพิ่มหรือลดจำนวน Pod (Replicas) ของ Deployment, StatefulSet หรือรีซอร์สอื่นๆ โดยอัตโนมัติ ตามเมตริกที่กำหนด เป้าหมายคือเพื่อรักษาระดับการใช้งานทรัพยากรให้อยู่ในระดับที่กำหนด (Target Value)
HPA พื้นฐานที่ทุกคนมักเริ่มต้นใช้คือการสเกลตามเมตริกจาก Resource Metrics API เช่น ค่าเฉลี่ยการใช้ CPU หรือ Memory ของ Pods ตัวอย่างเช่น คุณอาจตั้งค่าให้ HPA เพิ่มจำนวน Pod เมื่อการใช้ CPU เฉลี่ยเกิน 70% ของ Request ที่กำหนด กลไกนี้ทำงานได้ดีสำหรับบริการที่ภาระงานสัมพันธ์โดยตรงกับ CPU/Memory แต่ในโลกความจริง เมตริกที่บ่งชี้ "ความคับคั่ง" ของบริการอาจซับซ้อนกว่านั้นมาก
ข้อจำกัดของ HPA แบบ Resource Metrics และก้าวสู่ Custom Metrics
ลองนึกภาพบริการเหล่านี้:
- คิวข้อความ (Message Queue): คุณต้องการสเกล Worker Pod ตามจำนวนข้อความที่ค้างอยู่ในคิว (Queue Length) ไม่ใช่ตามการใช้ CPU ของ Worker
- API Gateway หรือ Web Service: คุณต้องการสเกลตามจำนวน Request ต่อวินาที (RPS), อัตราความผิดพลาด (HTTP 5xx) หรือเวลาในการตอบสนอง (Latency) ที่เพิ่มสูงขึ้น
- บริการด้านการเงิน: อาจต้องการสเกลตามจำนวนธุรกรรมต่อวินาที
ในกรณีเหล่านี้ CPU Usage อาจไม่กระเพื่อมมากนักแม้คิวจะยาวเป็นกิโล หรือในทางกลับกัน CPU อาจสูงชั่วขณะหนึ่งโดยที่จำนวน Request จริงยังน้อยอยู่ การสเกลตาม Resource Metrics ล้วนๆ อาจทำให้ได้จำนวน Pod ที่ไม่เหมาะสม ทั้งมากเกินไปหรือน้อยเกินไป นี่คือจุดที่ HPA Custom Metrics เข้ามาแก้ไขปัญหาโดยอนุญาตให้คุณกำหนดเมตริกสำหรับ Autoscaling จากแหล่งข้อมูลใดๆ ก็ตามที่คุณสามารถวัดได้
สถาปัตยกรรมและส่วนประกอบสำคัญของ HPA Custom Metrics
สำหรับ HPA ที่จะเข้าถึงเมตริกที่ไม่ได้มาจาก Resource Metrics API ได้ ระบบ Kubernetes ต้องการส่วนประกอบเพิ่มเติมหลักๆ ดังนี้:
- Custom Metrics API: เป็น API Server ที่ทำหน้าที่เป็นตัวกลางให้ HPA ดึงเมตริกที่กำหนดเองได้ Kubernetes เองไม่มีตัวนี้มาให้ เราต้องติดตั้งเพิ่ม ซึ่งมักมาพร้อมกับ...
- Prometheus Adapter (หรือตัว adapter อื่นๆ): เป็นตัวเชื่อมระหว่าง Custom Metrics API และแหล่งเก็บเมตริกอย่าง Prometheus มันจะแปลง query ของ Prometheus ให้เป็น API ที่ HPA เข้าใจได้
- แหล่งที่มาของเมตริก (เช่น Prometheus): ระบบเก็บและประมวลผลเมตริกที่เราต้องการ ซึ่ง Prometheus เป็นตัวเลือกยอดนิยมเนื่องจากเก็บข้อมูลเป็น time series และมีชุมชนที่แข็งแรง
เมื่อ HPA ต้องการประเมินว่าจะสเกลหรือไม่ มันจะส่งคำขอไปยัง Custom Metrics API -> Prometheus Adapter -> Prometheus เพื่อดึงค่าล่าสุดของเมตริกที่เรากำหนด แล้วนำมาเปรียบเทียบกับ Target Value ที่ตั้งไว้
ติดตั้งและกำหนดค่า Prometheus Adapter สำหรับ Custom Metrics
ก่อนอื่นต้องมี Kubernetes Cluster และติดตั้ง Prometheus ไว้แล้ว (เช่นผ่าน Helm chart `prometheus-community/kube-prometheus-stack`) จากนั้นเราสามารถติดตั้ง Prometheus Adapter ได้ง่ายๆ ด้วย Helm เช่นกัน
# เพิ่ม repo ของ prometheus-community
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# ติดตั้ง prometheus-adapter
helm install prometheus-adapter prometheus-community/prometheus-adapter \
--namespace monitoring \
--set prometheus.url=http://prometheus-server.monitoring.svc.cluster.local \
--set prometheus.port=80
หลังจากติดตั้งแล้ว เราสามารถตรวจสอบว่า Custom Metrics API ทำงานได้และมีเมตริกอะไรบ้างโดยใช้คำสั่ง:
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq .
สร้าง HPA ที่สเกลตาม Custom Metrics จาก Prometheus
สมมติว่าเรามี Deployment ชื่อ `api-worker` ที่ประมวลผลงานจากคิว Redis และเราต้องการสเกลตามความยาวของคิว (จำนวนงานที่ค้างอยู่) ใน Redis ที่มีชื่อคิวว่า `my_jobs`
ขั้นตอนที่ 1: เปิดเผยเมตริกใน Prometheus
แอปพลิเคชันของคุณหรือตัว exporter ต้องส่งเมตริก `redis_queue_length` ไปยัง Prometheus อยู่แล้ว โดยมี label `queue="my_jobs"`
ขั้นตอนที่ 2: กำหนดค่า Prometheus Adapter Rule
เราต้องบอก Adapter ว่าเมตริก `redis_queue_length` นั้นสามารถดึงผ่าน Custom Metrics API ได้และให้แมปมันอย่างไร โดยปกติเรากำหนดผ่านค่า `rules` ใน `values.yaml` ของ Helm chart หรือผ่าน ConfigMap ซึ่ง rule อาจมีหน้าตาแบบนี้:
rules:
custom:
- seriesQuery: 'redis_queue_length{queue!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "^(.*)_length"
as: "${1}_per_pod"
metricsQuery: 'sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)'
แต่สำหรับตัวอย่างง่ายๆ เราอาจใช้ rule ที่ตรงไปตรงมาเพื่อดึงค่าล่าสุดของคิว:
apiVersion: v1
kind: ConfigMap
metadata:
name: adapter-config
namespace: monitoring
data:
config.yaml: |
rules:
- seriesQuery: 'redis_queue_length{namespace!="",queue!=""}'
resources:
template: <<.Resource>>
name:
matches: "redis_queue_length"
as: "queue_length"
metricsQuery: 'redis_queue_length{queue="my_jobs"}'
ขั้นตอนที่ 3: สร้าง HPA Object
นี่คือส่วนสำคัญ เราจะสร้าง HPA ที่อ้างอิงเมตริก `queue_length` จาก Custom Metrics API
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-worker-hpa
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-worker
minReplicas: 2
maxReplicas: 10
metrics:
- type: Pods
pods:
metric:
name: queue_length # ชื่อเมตริกที่ปรากฏใน Custom Metrics API
target:
type: AverageValue
averageValue: 10 # เป้าหมาย: คิวความยาวเฉลี่ย 10 งานต่อ Pod
HPA นี้จะพยายามรักษาให้ค่าเฉลี่ยของ `queue_length` ต่อ Pod อยู่ที่ 10 หากคิวยาวเฉลี่ย 30 งาน และมี Pod อยู่ 2 Pod (เฉลี่ย 15 งาน/Pod ซึ่งเกิน 10) HPA จะคำนวณว่า需要 Pod ใหม่ = ceil(30 / 10) = 3 Pods จึงจะสั่งเพิ่ม Replicas เป็น 3 Pods
ตัวอย่างจริง: Autoscaling Web Service ตาม Requests Per Second (RPS)
สำหรับบริการเว็บ เรามักใช้เมตริกจาก Prometheus เช่น `http_requests_total` (จาก nginx-ingress controller หรือตัวแอปเอง) มาคำนวณเป็น RPS
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: frontend-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: frontend
minReplicas: 3
maxReplicas: 20
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: 50 # เป้าหมาย: 50 RPS ต่อ Pod
โดยที่ `http_requests_per_second` ถูกกำหนดใน Prometheus Adapter rule ดังนี้:
rules:
- seriesQuery: 'http_requests_total{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "http_requests_total"
as: "http_requests_per_second"
metricsQuery: 'sum(rate(http_requests_total{namespace="default",service="frontend"}[2m])) by (pod)'
การผสานรวมระบบคลาวด์อย่างมีประสิทธิภาพเป็นกุญแจสำคัญสำหรับสถาปัตยกรรมสมัยใหม่ ซึ่งการจัดการเมตริกและการสเกลอัตโนมัติก็เป็นส่วนหนึ่งของกระบวนการนั้น
ตารางเปรียบเทียบ: HPA Resource Metrics vs. Custom Metrics vs. External Metrics
| ลักษณะ | HPA Resource Metrics (CPU/Memory) | HPA Custom Metrics (Pod/Object) | HPA External Metrics |
|---|---|---|---|
| แหล่งที่มาของเมตริก | Resource Metrics API (จาก cAdvisor) | Custom Metrics API (มักมาจาก Prometheus) | External Metrics API (จาก cloud provider, Kafka, ฯลฯ) |
| ขอบเขตเมตริก | ต่อ Pod หรือต่อ Container | ต่อ Pod หรือต่อ Object ใน Namespace | เมตริกระดับ global (เช่น ความยาวคิว SQS ของ AWS ทั้งหมด) |
| กรณีใช้ตัวอย่าง | บริการที่ใช้ CPU/Memory สูงตามภาระงาน | สเกลตาม RPS, Latency, ความยาวคิวภายใน | สเกลตามความยาวคิว Cloud Message Queue, คอนซูเมอร์แล็กของ Kafka Topic |
| ความซับซ้อนในการตั้งค่า | ต่ำ (มีในตัว Kubernetes) | ปานกลาง (ต้องติดตั้ง Adapter และกำหนด Rule) | ปานกลางถึงสูง (ขึ้นกับ provider) |
| ความยืดหยุ่น | ต่ำ | สูงมาก (วัดอะไรก็ได้ที่ส่งเข้า Prometheus) | สูง (เชื่อมกับบริการภายนอกได้) |
แนวทางปฏิบัติและข้อควรระวัง
- กำหนด Target Value อย่างระมัดระวัง: เริ่มจากค่าที่อนุรักษ์นิยมและค่อยๆ ปรับโดยสังเกตพฤติกรรมของระบบ การวิเคราะห์ข้อมูลเมตริกย้อนหลังจากเครื่องมือเช่น xmsignal.com สามารถช่วยในการตัดสินใจได้
- ใช้ Stabilization Window: ใน `spec.behavior` ของ HPA v2 สามารถกำหนด `scaleUp/scaleDown.stabilizationWindowSeconds` เพื่อป้องกันการสเกลที่กระตุกเกินไป (Flapping)
- ตรวจสอบให้แน่ใจว่าเมตริกมีอยู่เสมอ: หากเมตริกหายไป (กลายเป็น `NoData`) HPA อาจจะหยุดสเกลหรือมีพฤติกรรมไม่แน่นอน ควรกำหนด `behavior.scaleDown.selectPolicy` เป็น `Disabled` ในกรณีที่เมตริกหาย
- ทดสอบภายใต้ภาระงาน: ทดสอบการสเกลอัพและสเกลดาวน์ด้วยการシミュเล이트ภาระงานจริงก่อนนำขึ้น production
- ติดตามและแจ้งเตือน: ตั้ง Alert เมื่อ HPA สเกลถึงขีดสุด (`maxReplicas`) หรือเมื่อไม่สามารถสเกลได้ ซึ่งเป็นสัญญาณว่าอาจต้องปรับค่า Threshold หรือเพิ่ม Resource Limit
การจัดการทรัพยากรคลาวด์อย่างชาญฉลาดไม่เพียงแต่เพิ่มประสิทธิภาพแต่ยังช่วยในการวางแผนงบประมาณ ซึ่งการทำความเข้าใจเครื่องมือเหล่านี้ก็เหมือนกับการเข้าใจตลาดการเงินที่ต้องอาศัยข้อมูลที่แม่นยำและทันท่วงที
FAQ (คำถามที่พบบ่อย)
HPA Custom Metrics แตกต่างจาก External Metrics อย่างไร?
HPA Custom Metrics จะดึงเมตริกที่สัมพันธ์กับวัตถุภายใน Kubernetes Cluster (เช่น ต่อ Pod, ต่อ Namespace) ส่วน HPA External Metrics จะดึงเมตริกที่ไม่ได้สัมพันธ์กับวัตถุใน Cluster เลย (เช่น จำนวนข้อความใน Amazon SQS Queue, ความลึกของคิว RabbitMQ ที่รันบนเซิร์ฟเวอร์ภายนอก) โดยทั้งสองใช้ API คนละตัวกัน (custom.metrics.k8s.io vs. external.metrics.k8s.io) แต่สามารถใช้ Prometheus Adapter รองรับทั้งสองแบบได้หากกำหนดค่า rule ถูกต้อง
จำเป็นต้องใช้ Prometheus เท่านั้นหรือไม่?
ไม่จำเป็น Prometheus เป็นเพียงตัวเลือกที่นิยมที่สุดเนื่องจากมี ecosystem ที่ครบครัน แต่ Custom Metrics API สามารถใช้กับ adapter อื่นๆ ได้ เช่น Microsoft Azure Adapter, Google Stackdriver Adapter, หรือแม้แต่เขียน adapter ขึ้นมาเองเพื่อดึงเมตริกจากแหล่งข้อมูลอื่นๆ เช่น Datadog, InfluxDB ได้ หลักการคือต้องมีตัวกลางที่ดึงเมตริกจากแหล่งนั้นๆ และแปลงเป็นรูปแบบที่ Custom Metrics API ต้องการ
หากเมตริกจาก Prometheus หายไป (Missing Metrics) จะเกิดอะไรขึ้น?
พฤติกรรมของ HPA ขึ้นอยู่กับเวอร์ชันและการกำหนดค่า โดยทั่วไปหาก HPA v2 ไม่สามารถดึงเมตริกที่กำหนดได้ (ได้สถานะ `NoData`) มันจะถือว่าเมตริกนั้นเป็น `0` สำหรับการสเกลดาวน์ (scaleDown) และจะ ไม่ดำเนินการ ใดๆ สำหรับการสเกลอัพ (scaleUp) ซึ่งเป็นพฤติกรรมที่ปลอดภัยกว่า เพื่อป้องกันการสเกลโดยขาดข้อมูล อย่างไรก็ตาม ควรตั้ง Alert ใน Prometheus ตัวเองเพื่อแจ้งเตือนเมื่อเมตริกสำคัญหายไป และตรวจสอบสุขภาพของ exporter หรือแอปพลิเคชันที่เกี่ยวข้องทันที
สามารถใช้หลายเมตริกพร้อมกันใน HPA เดียวได้หรือไม่?
ได้แน่นอน นี่คือหนึ่งในความสามารถของ HPA API เวอร์ชัน 2 (`autoscaling/v2`) คุณสามารถระบุเมตริกได้หลายตัวในฟิลด์ `spec.metrics[]` HPA จะคำนวณจำนวน replica ที่ต้องการจาก ทุกเมตริก และเลือกค่าที่ มากที่สุด มาใช้ ตัวอย่างเช่น คุณอาจกำหนดให้สเกลตามทั้ง CPU > 70% และ RPS > 50 ต่อ Pod หากระบบคำนวณจาก CPU ต้องการ 4 Pods แต่จาก RPS ต้องการ 6 Pods HPA จะสเกลไปที่ 6 Pods เพื่อให้เป็นไปตามเงื่อนไขที่เข้มงวดที่สุดของทุกเมตริก
การทำ Autoscaling แบบ Custom Metrics ส่งผลต่อการจัดการทรัพยากรคลาวด์โดยรวมอย่างไร?
การทำ Autoscaling ที่แม่นยำช่วยเพิ่มประสิทธิภาพการใช้ทรัพยากรคลาวด์ได้อย่างมาก ลดการจ่ายเงินสำหรับเซิร์ฟเวอร์ที่ idle ลง และรับประ
