במאמר הזה מוסבר איך להפעיל שינוי אוטומטי של מספר העותקים של Pod אופקית (HPA) בשירות המנוהל של Google Cloud ל-Prometheus. כדי להפעיל את HPA, מבצעים אחת מהפעולות הבאות:
- שימוש ב-KEDA (Kubernetes Event-driven Autoscaling), פתרון קוד פתוח שעבר את השלב הראשוני ב-Cloud Native Computing Foundation.
- שימוש בספרייה Custom Metrics Stackdriver Adapter, שפותחה ונתמכת על ידי Google Cloud.
- שימוש בספריית Prometheus Adapter של צד שלישי.
אי אפשר להשתמש ב-Stackdriver Adapter וב-Prometheus Adapter ביחד באותו אשכול כי יש חפיפה בהגדרות המשאבים שלהם, כמו שמתואר בקטע פתרון בעיות. מומלץ לבחור רק פתרון אחד ל-HPA.
שימוש ב-KEDA
KEDA (Kubernetes Event-driven Autoscaling) הוא הכלי האחרון להתאמה אוטומטית לעומס שמשתמש במדדים של Prometheus, והוא הופך לפתרון מועדף בקהילת Prometheus.
כדי להתחיל, אפשר לעיין במסמכי העזרה של KEDA בנושא שילוב עם השירות המנוהל של Google Cloud ל-Prometheus.
שימוש במתאם של Stackdriver למדדים מותאמים אישית
המתאם של Stackdriver למדדים מותאמים אישית תומך בשליחת שאילתות למדדים מ-שירות מנוהל ל-Prometheus החל מגרסה v0.13.1 של המתאם.
כדי להגדיר דוגמה להגדרת HPA באמצעות Custom Metrics Stackdriver Adapter, מבצעים את הפעולות הבאות:
- מגדירים אוסף מנוהל באשכול.
מתקינים את Custom Metrics Stackdriver Adapter באשכול.
kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yamlפורסים כלי לייצוא מדדים של Prometheus לדוגמה ומשאב HPA:
kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/examples/prometheus-to-sd/custom-metrics-prometheus-sd.yamlהפקודה הזו פורסת אפליקציית ייצוא שפולטת את המדד
fooומשאב HPA. ה-HPA משנה את קנה המידה של האפליקציה הזו ל-5 רפליקות כדי להשיג את ערך היעד של המדדfoo.אם אתם משתמשים ב-Workload Identity Federation ל-GKE, אתם צריכים גם להעניק את התפקיד 'צפייה ב-Monitoring' לחשבון השירות שבו פועל המתאם. אפשר לדלג על השלב הזה אם לא הפעלתם איחוד זהויות של עומסי עבודה ל-GKE באשכול Kubernetes.
export PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format 'get(projectNumber)') gcloud projects add-iam-policy-binding projects/PROJECT_ID \ --role roles/monitoring.viewer \ --member=principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/custom-metrics/sa/custom-metrics-stackdriver-adapterמגדירים משאב PodMonitoring על ידי הצבת ההגדרה הבאה בקובץ בשם
podmonitoring.yaml.apiVersion: monitoring.googleapis.com/v1 kind: PodMonitoring metadata: name: prom-example spec: selector: matchLabels: run: custom-metric-prometheus-sd endpoints: - port: 8080 interval: 30sפורסים את משאב PodMonitoring החדש:
kubectl -n default apply -f podmonitoring.yamlתוך כמה דקות, השירות המנוהל ל-Prometheus מעבד את המדדים שנאספו מה-exporter ומאחסן אותם ב-Cloud Monitoring באמצעות שם ארוך. המדדים של Prometheus מאוחסנים לפי המוסכמות הבאות:
- הקידומת
prometheus.googleapis.com. - הסיומת הזו היא בדרך כלל אחת מהאפשרויות הבאות:
gauge,counter,summaryאוhistogram. עם זאת, למדדים לא מוקלדים יכולה להיות הסיומתunknownאוunknown:counter. כדי לוודא את הסיומת, מחפשים את המדד ב-Cloud Monitoring באמצעות Metrics Explorer.
- הקידומת
מעדכנים את ה-HPA שנפרס כדי לשלוח שאילתה למדד מ-Cloud Monitoring. המדד
fooנקלט כמדדprometheus.googleapis.com/foo/gauge. כדי שאפשר יהיה להריץ שאילתות על המדד באמצעות משאב HorizontalPodAutoscaler שנפרס, צריך להשתמש בשם הארוך ב-HPA שנפרס, אבל צריך לשנות אותו ולהחליף את כל הקווים הנטויים (/) בתו צינור (|):prometheus.googleapis.com|foo|gauge. מידע נוסף זמין בקטע Metrics available from Stackdriver במאגר Custom Metrics Stackdriver Adapter.מריצים את הפקודה הבאה כדי לעדכן את ה-HPA שנפרס:
kubectl edit hpa custom-metric-prometheus-sdמשנים את הערך של השדה
pods.metric.nameמ-fooל-prometheus.googleapis.com|foo|gauge. המקטעspecצריך להיראות כך:spec: maxReplicas: 5 metrics: - pods: metric: name: prometheus.googleapis.com|foo|gauge target: averageValue: "20" type: AverageValue type: Pods minReplicas: 1
בדוגמה הזו, הגדרת ה-HPA מחפשת את הערך הממוצע של המדד
prometheus.googleapis.com/foo/gaugeשיהיה20. מכיוון שהפריסה מגדירה את ערך המדד כ-40, בקר ה-HPA מגדיל את מספר הפודים עד לערך של השדהmaxReplicas(5) כדי לנסות להקטין את הערך הממוצע של המדד בכל הפודים ל-20.השאילתה של HPA מוגבלת למרחב השמות ולאשכול שבו משאב ה-HPA מותקן, כך שמדדים זהים באשכולות ובמרחבי שמות אחרים לא משפיעים על התאמה אוטומטית לעומס.
כדי לראות את שינוי הגודל של עומס העבודה, מריצים את הפקודה הבאה:
kubectl get hpa custom-metric-prometheus-sd --watchהערך של השדה
REPLICASמשתנה מ-1ל-5.NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE custom-metric-prometheus-sd Deployment/custom-metric-prometheus-sd 40/20 1 5 5 *
כדי לצמצם את הפריסה, צריך לעדכן את ערך מדד היעד כך שיהיה גבוה יותר מערך המדד המיוצא. בדוגמה הזו, הפריסה מגדירה את הערך של המדד
prometheus.googleapis.com/foo/gaugeל-40. אם מגדירים את ערך היעד למספר גבוה מ-40, הפריסה תצטמצם.לדוגמה, אפשר להשתמש ב-
kubectl editכדי לשנות את הערך של השדהpods.target.averageValueבהגדרת ה-HPA מ-20ל-100.kubectl edit hpa custom-metric-prometheus-sdמשנים את קטע המפרט כך שיתאים לערכים הבאים:
spec: maxReplicas: 5 metrics: - pods: metric: name: prometheus.googleapis.com|foo|gauge target: averageValue: "100" type: AverageValue type: Pods minReplicas: 1כדי לראות את צמצום העומס, מריצים את הפקודה הבאה:
kubectl get hpa custom-metric-prometheus-sd --watchהערך של השדה
REPLICASמשתנה מ-5ל-1. כך זה אמור להיות, והתהליך הזה איטי יותר מאשר הגדלת מספר ה-pods:NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE custom-metric-prometheus-sd Deployment/custom-metric-prometheus-sd 40/100 1 5 1 *
כדי לנקות את הדוגמה שנפרסה, מריצים את הפקודות הבאות:
kubectl delete -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yaml kubectl delete -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/examples/prometheus-to-sd/custom-metrics-prometheus-sd.yaml kubectl delete podmonitoring/prom-example
מידע נוסף זמין בדוגמה ל-Prometheus במאגר של מתאם Stackdriver למדדים מותאמים אישית, או במאמר בנושא שינוי קנה מידה של אפליקציה.
שימוש ב-Prometheus Adapter
אפשר להשתמש בהגדרות קיימות של prometheus-adapter כדי להגדיר שינוי גודל אוטומטי עם כמה שינויים בלבד. להגדרת קנה מידה של prometheus-adapter באמצעות השירות המנוהל ל-Prometheus יש שתי הגבלות נוספות בהשוואה להגדרת קנה מידה באמצעות Prometheus במעלה הזרם:
השאילתות צריכות להיות מנותבות דרך שרת ה-proxy של ממשק המשתמש הקצה של Prometheus, בדיוק כמו כשמבצעים שאילתות בשירות המנוהל ל-Prometheus באמצעות Prometheus API או ממשק המשתמש. ב-prometheus-adapter, צריך לערוך את
prometheus-adapterהפריסה כדי לשנות אתprometheus-urlהערך באופן הבא:--prometheus-url=http://frontend.NAMESPACE_NAME.svc:9090/
כאשר NAMESPACE_NAME הוא מרחב השמות שבו פרוס ה-frontend.
אי אפשר להשתמש בביטוי רגולרי (
=~או!~) בשם של מדד בשדה.seriesQueryבהגדרות הכללים. במקום זאת, צריך לציין את שמות המדדים באופן מלא. כמה פתרונות עקיפים מפורטים במאמר בנושא תאימות ל-PromQL.
יכול להיות שיעבור קצת יותר זמן עד שהנתונים יהיו זמינים בשירות המנוהל ל-Prometheus בהשוואה ל-Prometheus במעלה הזרם, ולכן הגדרת לוגיקה של התאמה אוטומטית לעומס שפועלת מהר מדי עלולה לגרום להתנהגות לא רצויה. למרות שאין ערובה לרעננות הנתונים, בדרך כלל הנתונים זמינים לשאילתה 3-7 שניות אחרי שהם נשלחים אל שירות מנוהל ל-Prometheus, לא כולל חביון ברשת.
כל השאילתות שמונפקות על ידי prometheus-adapter הן גלובליות בהיקף שלהן. המשמעות היא שאם יש לכם אפליקציות בשני מרחבי שמות שפולטות מדדים עם שמות זהים, הגדרת HPA שמשתמשת במדד הזה תבצע שינוי גודל על סמך נתונים משתי האפליקציות. מומלץ להשתמש תמיד במסננים namespace או cluster ב-PromQL כדי להימנע מסקיילינג באמצעות נתונים שגויים.
כדי להגדיר דוגמה להגדרת HPA באמצעות prometheus-adapter ואיסוף מנוהל, פועלים לפי השלבים הבאים:
- מגדירים אוסף מנוהל באשכול.
- פורסים את שרת ה-Proxy של ממשק המשתמש של Prometheus באשכול. אם אתם משתמשים באיחוד שירותי אימות הזהות של עומסי עבודה ב-GKE, אתם צריכים גם להגדיר חשבון שירות ולתת לו הרשאה.
- פורסים את קובצי המניפסט בספרייה
examples/hpa/במאגרprometheus-engine:-
example-app.yaml: פריסה ושירות לדוגמה שפולטים מדדים. -
pod-monitoring.yaml: משאב שמגדיר גירוד של מדדי הדוגמה. -
hpa.yaml: משאב ה-HPA שמגדיר את שינוי הגודל של עומס העבודה.
-
מוודאים שהתוסף
prometheus-adapterמותקן באשכול. כדי לעשות את זה, צריך לפרוס את המניפסט לדוגמה להתקנה באשכול. קובץ המניפסט הזה מוגדר כך:- שליחת שאילתה לפרוקסי של קצה קדמי שנפרס במרחב השמות
default. - מריצים שאילתת PromQL כדי לחשב ולהחזיר את המדד
http_requests_per_secondמהפריסה לדוגמה.
- שליחת שאילתה לפרוקסי של קצה קדמי שנפרס במרחב השמות
מריצים את הפקודות הבאות, כל אחת בסשן מסוף נפרד:
- יוצרים עומס HTTP על שירות
prometheus-example-app:kubectl run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://prometheus-example-app; done" - צופים ב-Horizontal Pod Autoscaler:
kubectl get hpa prometheus-example-app --watch - צופים בהגדלת עומס העבודה:
kubectl get po -lapp.kubernetes.io/name=prometheus-example-app --watch
- יוצרים עומס HTTP על שירות
כדי להפסיק את יצירת העומס של HTTP, משתמשים ב-Ctrl+C וצופים בעומס העבודה מצטמצם.
פתרון בעיות
המתאם Custom Metrics Stackdriver Adapter משתמש בהגדרות משאבים עם אותם שמות כמו אלה שבמתאם Prometheus Adapter, prometheus-adapter. החפיפה בשמות אומרת שהפעלת יותר ממתאם אחד באותו אשכול גורמת לשגיאות.
התקנת Prometheus Adapter באשכול שבו הותקן בעבר Custom Metrics Stackdriver Adapter עשויה להוביל לשגיאות כמו FailedGetObjectMetric בגלל שמות מתנגשים. כדי לפתור את הבעיה, יכול להיות שתצטרכו למחוק את v1beta1.external.metrics.k8s.io, את v1beta1.custom.metrics.k8s.io ואת v1beta2.custom.metrics.k8s.io apiservices שנרשמו קודם על ידי Custom Metrics Adapter.
טיפים לפתרון בעיות:
חלק ממדדי המערכת של Cloud Monitoring, כמו מדדי Pub/Sub, מתעדכנים באיחור של 60 שניות או יותר. הכלי Prometheus Adapter מריץ שאילתות באמצעות חותמת הזמן הנוכחית, ולכן יכול להיות ששאילתות על המדדים האלה באמצעות הכלי יחזירו תוצאה שגויה של 'אין נתונים'. כדי לשלוח שאילתות על מדדים מושהים, משתמשים במשנה
offsetב-PromQL כדי לשנות את היסט הזמן של השאילתה בסכום הנדרש.כדי לוודא שה-proxy של ממשק המשתמש בקצה הקדמי פועל כמצופה ואין בעיות בהרשאות, מריצים את הפקודה הבאה בטרמינל:
kubectl -n NAMESPACE_NAME port-forward svc/frontend 9090
לאחר מכן, פותחים טרמינל נוסף ומריצים את הפקודה הבאה:
curl --silent 'localhost:9090/api/v1/series?match%5B%5D=up'
אם הפרוקסי של ממשק המשתמש בחלק הקדמי פועל בצורה תקינה, התגובה במסוף השני תהיה דומה לתגובה הבאה:
curl --silent 'localhost:9090/api/v1/series?match%5B%5D=up' | jq . { "status": "success", "data": [ ... ] }אם מוצגת שגיאת 403, סימן ששרת ה-proxy של ממשק המשתמש בחלק הקדמי לא מוגדר כמו שצריך. מידע על פתרון שגיאה 403 זמין במדריך הגדרה והרשאה של חשבון שירות.
כדי לוודא ששרת ה-API של המדדים בהתאמה אישית זמין, מריצים את הפקודה הבאה:
kubectl get apiservices.apiregistration.k8s.io v1beta1.custom.metrics.k8s.io
כשה-apiserver זמין, התגובה תיראה כך:
$ kubectl get apiservices.apiregistration.k8s.io v1beta1.custom.metrics.k8s.io NAME SERVICE AVAILABLE AGE v1beta1.custom.metrics.k8s.io monitoring/prometheus-adapter True 33m
כדי לוודא שה-HPA פועל כמצופה, מריצים את הפקודה הבאה:
$ kubectl describe hpa prometheus-example-app Name: prometheus-example-app Namespace: default Labels:
Annotations: Reference: Deployment/prometheus-example-app Metrics: ( current / target ) "http_requests_per_second" on pods: 11500m / 10 Min replicas: 1 Max replicas: 10 Deployment pods: 2 current / 2 desired Conditions: Type Status Reason Message ---- ------ ------ ------- AbleToScale True ReadyForNewScale recommended size matches current size ScalingActive True ValidMetricFound the HPA was able to successfully calculate a replica count from pods metric http_requests_per_second ScalingLimited False DesiredWithinRange the desired count is within the acceptable range Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulRescale 47s horizontal-pod-autoscaler New size: 2; reason: pods metric http_requests_per_second above target אם התשובה מכילה הצהרה כמו
FailedGetPodsMetric, אז ה-HPA נכשל. הדוגמה הבאה ממחישה תגובה לקריאהdescribeכש-HPA נכשל:$ kubectl describe hpa prometheus-example-app Name: prometheus-example-app Namespace: default Reference: Deployment/prometheus-example-app Metrics: ( current / target ) "http_requests_per_second" on pods:
/ 10 Min replicas: 1 Max replicas: 10 Deployment pods: 1 current / 1 desired Conditions: Type Status Reason Message ---- ------ ------ ------- AbleToScale True ReadyForNewScale recommended size matches current size ScalingActive False FailedGetPodsMetric the HPA was unable to compute the replica count: unable to get metric http_requests_per_second: unable to fetch metrics from custom metrics API: the server could not find the metric http_requests_per_second for pods ScalingLimited False DesiredWithinRange the desired count is within the acceptable range Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedGetPodsMetric 104s (x11 over 16m) horizontal-pod-autoscaler unable to get metric http_requests_per_second: unable to fetch metrics from custom metrics API: the server could not find the metric http_requests_per_second for pods אם ה-HPA נכשל, צריך לוודא שאתם יוצרים מדדים באמצעות
load-generator. אפשר לבדוק את ה-API של המדדים המותאמים אישית ישירות באמצעות הפקודה:kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/" | jq .
פלט מוצלח ייראה כך:
$ kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/" | jq . { "kind": "APIResourceList", "apiVersion": "v1", "groupVersion": "custom.metrics.k8s.io/v1beta1", "resources": [ { "name": "namespaces/http_requests_per_second", "singularName": "", "namespaced": false, "kind": "MetricValueList", "verbs": [ "get" ] }, { "name": "pods/http_requests_per_second", "singularName": "", "namespaced": true, "kind": "MetricValueList", "verbs": [ "get" ] } ] }אם אין מדדים, לא יוצגו נתונים בקטע
"resources"בפלט, לדוגמה:kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/" | jq . { "kind": "APIResourceList", "apiVersion": "v1", "groupVersion": "custom.metrics.k8s.io/v1beta1", "resources": [] }