ไปสเกล Pod กันครับ (k8s hpa with custom metrics)

Patipat Ponyanan
Grean Developers Family
2 min readAug 2, 2019
Photo by Puk Patrick on Unsplash

วันก่อนครับ มี error connection timeout เด้งเขามารัวๆ หลังจากทำการตรวจสอบคิดว่าน่าจะเกิดจากจำนวน Pod ไม่เพียงพอต่อการรองรับ request ที่ส่งเข้ามา ก็สเกล Pod ให้มีจำนวนมากขึ้นกว่าเดิม จบปิดมู้ แต่ช้าก่อน ถ้าทำแบบนั้นช่วงที่ request เข้ามาน้อยๆ ก็ทำให้เราใช้ทรัพยากรได้อย่างไม่คุ้มค่า บวกกับค่าใช้จ่ายที่ไม่ควรเสีย

ใน Kubernetes เราสามารถทำ Auto Scaling ได้ ด้วยการใช้ HPA (Horizontal Pod Autoscaler)โดยพื้นฐานแล้วเราสามารถใช้ค่า Cpu, Memory เป็นตัวชี้วัดการเพิ่มลดจำนวน Pod ได้ แต่ครั้งนี้อาจจะไม่ตอบโจทย์เพราะอยากให้จำนวน request ที่วิ่งเข้ามาเป็นตัวชี้วัด นอกเหนือจาก Metrics พื้นฐาน แล้ว HPA ยัง รองรับ Custom Metrics ด้วย นั่นหมายความว่าเราสามารถทำ Auto Scaling ตามจำนวน request ได้

สร้าง Custom Metrics
เพื่อให้ HPA สามารถนำค่า request per second ไปใช้ในการสเกลได้ เราต้องแปลง ค่าเรานั้นให้รองรับ HPA เสียก่อน ในที่นี้จะใช้ Nginx Ingress Controller เป็นตัว Provide metrics ให้กับเรา ค่า metrics พวกนี้จะถูกดูดไปเก็บอยู่ที่ Prometheus ซึ่งเราสามารถ query จาก promethus ได้

nginx_ingress_controller_requests{ingress="app",namespace="app"} 126

การแปลงผลลัพธ์ที่ได้จากคิวรี่ให้กลายเป็น custom metrics เราจะใช้ Prometheus Adapter https://github.com/directxman12/k8s-prometheus-adapter (พระเอกของงาน)

Config Prometheus Adapter

- seriesQuery: 'nginx_ingress_controller_requests{ingress!="",namespace!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
ingress: {resource: "ingress"}
name:
matches: "^(.*)_controller_requests"
as: "${1}_request_per_second"
metricsQuery: 'sum(irate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)'

การทำงานก็คือ Prometheus Adapter ส่ง query เข้าไปที่ Prometheus แล้วนำค่าที่ได้มาสร้างเป็น custom metrics ให้ ซึ่งเราสามารถเช็คได้โดย kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/app/ingress/app/nginx_ingress_request_per_second" จะได้ผลลัพธ์หน้าตาประมาณนี้

{
"kind": "MetricValueList",
"apiVersion": "custom.metrics.k8s.io/v1beta1",
"metadata": {
"selfLink": "/apis/custom.metrics.k8s.io/v1beta1/namespaces/app/ingress/app/nginx_ingress_request_per_second"
},
"items": [
{
"describedObject": {
"kind": "Ingress",
"namespace": "app",
"name": "app",
"apiVersion": "extensions/v1beta1"
},
"metricName": "nginx_ingress_request_per_second",
"timestamp": "2019-08-02T08:25:53Z",
"value": "126m"
}
]
}

ณ ตอนนี้ custom metrics ของเราก็พร้อมแล้ว ซึ่ง HPA สามารถนำค่าจาก metrics นี้ไปใช้ได้

สร้าง HPA

kind: HorizontalPodAutoscaler
apiVersion: autoscaling/v2beta1
metadata:
name: app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: app
minReplicas: 3
maxReplicas: 12
metrics:
- type: Object
object:
metricName: nginx_ingress_request_per_second
target:
apiVersion: extensions/v1beta1
kind: ingress
name: app
targetValue: 5

สิ่งที่เกิดขึ้นคือเราจะได้ HPA ตัวหนึ่งที่ทำการดูแลจำนวน Pod ของ Deployment app ให้มีการสเกลตาม Metrics ที่มีชื่อ nginx_ingress_request_per_second โดยที่จะสเกลก็ต่อเมื่อมีค่าของ metrics เกิน 5 (5 request per second) จำนวนสูงสุดที่มีได้คือ 12 และต่ำสุดคือ 3

เราสามารถเช็คการทำงานของ hpa ได้โดย kubectl get hpa

NAME                    REFERENCE                TARGETS        MINPODS   MAXPODS   REPLICAS   AGE
app-hpa Deployment/app 200m/5 3 12 3 1d

สิ่งสำคัญคือการทดสอบว่ามันจะสเกลจริงไหม ลองกำหนด targetValue ต่ำๆ แล้วยิงรีเควสดู

Feedback หลังจากใช้ HPA
A: หลังจากที่เริ่มใช้งาน HPA กับ request per second ปัญหา connection timeout ก็ไม่มีอีกเลย
B: wow เพราะว่า HPA ทำงานได้อย่างมีประสิทธิภาพ สเกลตามจำนวนรีเควสได้ดีมาก ???
A: ป่าว เพราะ app นั้นไม่มีใครรีเควสมาอีกเลย ลบ app ทิ้งไปแล้วววว 😢

--

--