מאגרי קיבולת משפרים את הרספונסיביות והאמינות של עומסי עבודה קריטיים, על ידי ניהול יזום של קיבולת אשכולות פנויה ומצבים מושהים של קיבולת שהוקצתה והוגדרה מראש, באמצעות Kubernetes CapacityBuffer CustomResourceDefinition (CRD). שימוש במאגרי קיבולת מאפשר לכם להגדיר במפורש כמות ספציפית של קיבולת צומת לא מנוצלת בתוך האשכול. הקיבולת השמורה הזו עוזרת לצמצם את זמן התזמון של Pod.
כשצריך להגדיל במהירות עומס עבודה בעדיפות גבוהה, עומס העבודה החדש יכול להשתמש בקיבולת הריקה באופן מיידי בלי לחכות להקצאת צמתים. הגישה הזו מצמצמת את זמן האחזור ומונעת תחרות על משאבים במהלך עליות פתאומיות בביקוש.
בדף הזה מוסבר איך להגדיר מאגרים זמניים למשאבי מחשוב עם רפליקה קבועה, מגבלת משאבים ואחוזים, להקצות מאגר זמני לעומס עבודה ספציפי, להתאים אישית את ההתנהגות של המאגר הזמני במצב המתנה ולטעון מראש קובצי אימג' של קונטיינרים.
לפני שמתחילים
לפני שמתחילים, חשוב לוודא שביצעתם את המשימות הבאות:
- מפעילים את Google Kubernetes Engine API. הפעלת Google Kubernetes Engine API
- כדי להשתמש ב-Google Cloud CLI למשימה הזו, מתקינים ואז מאתחלים את ה-CLI של gcloud. אם כבר התקנתם את ה-CLI של gcloud, הריצו את הפקודה
gcloud components updateכדי לקבל את הגרסה העדכנית. יכול להיות שבגרסאות קודמות של ה-CLI של gcloud אי אפשר להריץ את הפקודות שבמסמך הזה.
- ליצור או לקבל גישה לאשכול GKE שפועל בגרסה 1.37.1-gke.3020000 ואילך. כדי להשתמש במאגרי המתנה באשכולות במצב Autopilot, צריך ComputeClasses בהתאמה אישית.
- מפעילים יצירה אוטומטית של מאגרי צמתים באשכולות הרגילים. באשכולות Autopilot, היצירה האוטומטית של מאגרי צמתים כבר מופעלת. יצירה אוטומטית של מאגר צמתים היא אופציונלית, אבל מומלצת למאגרים פעילים ונדרשת למאגרים במצב המתנה.
יצירת אובייקטים של Kubernetes שנדרשים מראש
כדי להגדיר CapacityBuffer, צריך מרחב שמות שמכיל את כל האובייקטים הנדרשים (CapacityBuffer עצמו ומשאבים נוספים כמו PodTemplate או עומס עבודה). ה-PodTemplate וה-CapacityBuffer צריכים להיות באותו מרחב שמות. אתם יכולים ליצור מרחב שמות או להשתמש במרחב שמות קיים, כולל מרחב השמות default.
בהתאם לסוג של CapacityBuffer שאתם מגדירים, תצטרכו גם אחת מהאפשרויות הבאות:
- PodTemplate: מגדיר את דרישות המשאבים ליחידה אחת של קיבולת מאגר. ההגדרה שצוינה באובייקט CapacityBuffer מפנה לתבנית Pod.
עומס עבודה: עומס עבודה קיים שאתם מפנים אליו באובייקט CapacityBuffer. במדריך הזה נעשה שימוש באובייקט Deployment כדוגמה לעומס עבודה, אבל מאגרי קיבולת תומכים בכל אחד מסוגי המשאבים הבאים:
- פריסה
- ReplicaSet
- StatefulSet
- ReplicationController
- משימה
- CustomResourceDefinitions (CRD) שמטמיעים את משאב המשנה
scale.
בקטע הזה מופיעות דוגמאות לאובייקטים האלה. אם כבר יש לכם עומס עבודה שאתם רוצים להגדיר עם מאגר זמני למשאבי מחשוב, אתם יכולים לדלג לקטע החלת מאגר זמני למשאבי מחשוב.
כדי ליצור עומס עבודה לדוגמה ב-Kubernetes, מבצעים את השלבים הבאים:
שומרים את המניפסט הבא בתור
namespace.yaml:apiVersion: v1 kind: Namespace metadata: name: capacity-buffer-example labels: name: capacity-buffer-exampleהמניפסט הזה יוצר מרחב שמות בשם
capacity-buffer-example.אופציונלי: כדי להשתמש במאגרים זמניים למשאבי מחשוב עם ComputeClass בהתאמה אישית, שומרים את המניפסט הבא בתור
custom-compute-class.yaml:apiVersion: cloud.google.com/v1 kind: ComputeClass metadata: name: ccc-example spec: # Buffers are also created according to these priorities priorities: - machineFamily: n4 - machineFamily: n4d - machineFamily: c4 - machineFamily: c4d nodePoolAutoCreation: enabled: trueהמניפסט הזה יוצר
ComputeClassבהתאמה אישית שמגדיר את עדיפויות המחשוב של הצמתים ש-GKE מקצה, ושולט בהן. מידע נוסף זמין במאמר ComputeClasses בהתאמה אישית.שומרים את המניפסט הבא בתור
buffer-pod-template.yaml:apiVersion: v1 kind: PodTemplate metadata: name: buffer-unit-template namespace: capacity-buffer-example # the namespace must be the same namespace as the CapacityBuffer template: spec: terminationGracePeriodSeconds: 0 containers: - name: buffer-container image: registry.k8s.io/pause:3.9 resources: requests: cpu: "1" memory: "1Gi" limits: cpu: "1" memory: "1Gi" # Optional: Using buffers with a custom ComputeClass / # controls the properties of the provisioned nodes. nodeSelector: cloud.google.com/compute-class: ccc-exampleהמניפסט הזה יוצר
PodTemplateשמגדיר את דרישות המשאבים ליחידה אחת של קיבולת מאגר (1מעבד ו-1Giזיכרון). ההגדרה הזו מציינת את הגודל של יחידות הקיבולת ש-GKE מקצה למאגר הנתונים הזמני. לדוגמה, עם PodTemplate כזה, אם האשכול יוגדל, GKE לא יחשיב צמתים עם פחות ממעבד אחד ו-1Gi של משאבים זמינים כחלק מהמאגר.שומרים את המניפסט הבא בתור
sample-workload-deployment.yaml:apiVersion: apps/v1 kind: Deployment metadata: name: critical-workload-ref namespace: capacity-buffer-example # the namespace must be the same namespace as the CapacityBuffer spec: replicas: 10 selector: matchLabels: app: critical-workload template: metadata: labels: app: critical-workload spec: containers: - name: busybox image: busybox command: ["sleep", "3600"] resources: requests: cpu: 100m # Optional: Using buffers with a custom ComputeClass / # controls the properties of the provisioned nodes. nodeSelector: cloud.google.com/compute-class: ccc-exampleהמניפסט הזה יוצר פריסה לדוגמה עם 10 רפליקות, שהן אובייקט העזר לדוגמה של מאגר מבוסס-אחוזים בקטע הבא.
מחילים את המניפסטים על האשכול:
kubectl apply -f namespace.yaml -f custom-compute-class.yaml -f buffer-pod-template.yaml -f sample-workload-deployment.yamlמוודאים ש-GKE יצר את האובייקטים:
kubectl get podtemplate -n capacity-buffer-example kubectl get deployment critical-workload-ref -n capacity-buffer-exampleהפלט אמור להיראות כך:
NAME AGE buffer-unit-template 1m NAME READY UP-TO-DATE AVAILABLE AGE critical-workload-ref 10/10 10 10 1m
החלת מאגר זמני למשאבי מחשוב
בקטע הזה מפורטות דוגמאות לסוגים השונים של מאגרי קיבולת זמניים שאפשר להחיל על עומסי העבודה.
הגדרת מאגר קבוע של רפליקות
כשמגדירים CapacityBuffer עם רפליקות קבועות, מציינים את המספר המדויק של יחידות המאגר שרוצים על סמך PodTemplate.
כדי ליצור מאגר עם רפליקות קבועות:
שומרים את המניפסט הבא בתור
cb-fixed-replicas.yaml:apiVersion: autoscaling.x-k8s.io/v1beta1 kind: CapacityBuffer metadata: name: fixed-replica-buffer namespace: NAMESPACE spec: podTemplateRef: name: POD_TEMPLATE replicas: 3 provisioningStrategy: "STRATEGY"מחליפים את מה שכתוב בשדות הבאים:
-
NAMESPACE: השם של מרחב השמות, לדוגמהcapacity-buffer-example. -
POD_TEMPLATE: ה-PodTemplate שמגדיר את דרישות המשאבים, לדוגמהbuffer-unit-template. STRATEGY: אסטרטגיית ההקצאה,"buffer.x-k8s.io/active-capacity"(ברירת המחדל) או"buffer.gke.io/standby-capacity".
המניפסט הזה יוצר משאב CapacityBuffer שמפנה ל-PodTemplate כדי לבקש מספר ספציפי של יחידות מאגר.
-
מחילים את המניפסט:
kubectl apply -f cb-fixed-replicas.yamlמוודאים ש-GKE החיל את מאגר הקיבולת:
kubectl get capacitybuffer fixed-replica-buffer -n NAMESPACEהשדה
replicasבסטטוס אמור להציג את הערך3, שמשקף את מספר הרפליקות שהגדרתם במניפסט. בשדהSTATUSאמור להופיע הערךReadyForProvisioning.
הגדרת מאגר זמני למגבלות על משאבים
אתם יכולים להשתמש בשדה limits כדי להגדיר את כמות המשאבים המקסימלית שהמאגר הזמני יכול לצרוך, שמחושבת על סמך הגודל של PodTemplate.
כדי ליצור מאגר למגבלות המשאבים:
שומרים את המניפסט הבא בתור
cb-resource-limits.yaml:apiVersion: autoscaling.x-k8s.io/v1beta1 kind: CapacityBuffer metadata: name: resource-limit-buffer namespace: NAMESPACE spec: podTemplateRef: name: POD_TEMPLATE limits: cpu: "5" memory: "5Gi" provisioningStrategy: "STRATEGY"מחליפים את מה שכתוב בשדות הבאים:
-
NAMESPACE: השם של מרחב השמות, לדוגמהcapacity-buffer-example. -
POD_TEMPLATE: ה-PodTemplate שמגדיר את דרישות המשאבים, לדוגמהbuffer-unit-template. STRATEGY: אסטרטגיית ההקצאה,"buffer.x-k8s.io/active-capacity"(ברירת המחדל) או"buffer.gke.io/standby-capacity".
המניפסט הזה יוצר משאב CapacityBuffer עם מגבלה כוללת של 5 מעבדים וזיכרון בנפח 5GiB. אם אתם משתמשים בדוגמה של PodTemplate מהשלב הקודם, אתם מגדירים כל יחידה כ-
1מעבד (CPU) ו-1Giזיכרון, מה שאמור להוביל ל-5 יחידות מאגר.-
מחילים את המניפסט:
kubectl apply -f cb-resource-limits.yamlמוודאים ש-GKE החיל את מאגר הקיבולת:
kubectl get capacitybuffer resource-limit-buffer -n NAMESPACEבודקים את הסטטוס של CapacityBuffer. בשדה
replicasאמור להופיע ערך שנגזר מהמגבלות שהגדרתם. אם אתם משתמשים בדוגמה של PodTemplate מהקטע הקודם, אמורות להופיע5יחידות מאגר, כי זה המספר המקסימלי של יחידות שמתאימות למגבלות שהוגדרו.
הגדרת מאגר נתונים זמני שמבוסס על אחוזים
הגדרת מאגר מבוסס-אחוזים משנה באופן דינמי את גודל המאגר על סמך אחוז מעומס עבודה קיים שניתן להתאמה לעומס. אפשר להשתמש במאגרים זמניים למשאבי מחשוב שמבוססים על אחוזים רק באובייקטים של Kubernetes שניתנים להתאמה, שמטמיעים את משאב המשנה scale, כמו Deployments, StatefulSets, ReplicaSets או Jobs. אי אפשר להגדיר מאגר מבוסס-אחוזים לתבניות Pod כי אין להן שדה replicas.
ברוב המקרים, כדאי להתחיל עם אסטרטגיות של רפליקות קבועות או מגבלות משאבים, ולא עם מאגרים מבוססי-אחוזים. כדאי להביא בחשבון את הנקודות הבאות:
- מאגרים שמבוססים על אחוזים פחות מגיבים להגדלות פתאומיות אם עומס העבודה מוגדל למספרים נמוכים או לאפס, כי מרווח הבטיחות פוחת ביחס לפודים הפעילים.
- צמתים במצב המתנה צוברים חיובים על מחשוב במהלך תקופת האתחול שלהם לפני ההשעיה. שינוי דינמי של מאגרי ה-standby בהתאם לתנודות בעומס העבודה מבזבז משאבי מחשוב, ולכן כדאי להשתמש ברפליקות קבועות או במגבלות משאבים למאגרי ה-standby.
- מאגרים מבוססי-אחוזים שימושיים בעיקר למאגרים פעילים בפריסות גדולות, שבהן מספר הרפליקות אף פעם לא קטן.
כדי לשמור על שטח אחסון זמני מינימלי כשעומס העבודה שאליו מתייחסים מצטמצם, מגדירים את השדה replicas יחד עם השדה percentage. GKE משתמש בשדה שמוביל ליותר יחידות מאגר. כדי להגביל את גודל שטח האחסון הזמני, צריך להגדיר גם את השדה limits.
לדוגמה, נניח שיש מאגר עם percentage: 20 ו-replicas: 3 שמפנה לפריסה עם 10 רפליקות. 20% מ-10 רפליקות הם 2, וזה פחות מהערך 3 של replicas, לכן GKE מקצה 3 יחידות מאגר. אם הפריסה תתרחב ל-20 רפליקות, המאגר יגדל ל-4 יחידות. למידע נוסף, קראו את מאמרי העזרה של CapacityBuffer.
כדי ליצור מאגר באחוזים, מבצעים את השלבים הבאים:
שומרים את המניפסט הבא בתור
cb-percentage-based.yaml:apiVersion: autoscaling.x-k8s.io/v1beta1 kind: CapacityBuffer metadata: name: percentage-buffer namespace: NAMESPACE spec: scalableRef: apiGroup: apps kind: Deployment name: SCALABLE_RESOURCE_NAME percentage: 20 provisioningStrategy: "STRATEGY"מחליפים את מה שכתוב בשדות הבאים:
-
NAMESPACE: השם של מרחב השמות. -
SCALABLE_RESOURCE_NAME: השם של המשאב שניתן להתאמה לעומס, לדוגמהcritical-workload-ref. STRATEGY: אסטרטגיית ההקצאה,"buffer.x-k8s.io/active-capacity"(ברירת המחדל) או"buffer.gke.io/standby-capacity".
המניפסט הזה יוצר משאב CapacityBuffer שמבקש שטח אחסון זמני ששווה ל-20% מהרפליקות של המשאב שאליו מתייחסים. אם אתם משתמשים בדוגמה לפריסה מהקטע הקודם, ערך הרפליקה מוגדר ל-
10.-
מחילים את המניפסט:
kubectl apply -f cb-percentage-based.yamlמוודאים ש-GKE החיל את מאגר הקיבולת:
kubectl get capacitybuffer percentage-buffer -n NAMESPACEבודקים את הסטטוס של CapacityBuffer. בשדה
replicasצריך להופיע ערך מחישוב האחוזים. אם אתם משתמשים בדוגמה לפריסה מהקטע הקודם, אמורות להופיע2יחידות מאגר, שהן 20% מ-10 הרפליקות שהוגדרו בפריסה.כדי לבדוק את ההתאמה הדינמית לעומס, מגדילים את הפריסה באופן ידני ל-20 רפליקות:
kubectl scale deployment critical-workload-ref -n NAMESPACE --replicas=20הבקר CapacityBuffer מגיב ומשנה באופן אוטומטי את קנה המידה של המאגר ל-4 רפליקות.
הקצאת מאגר זמני למשאבי מחשוב לעומס עבודה ספציפי
כברירת מחדל, כל Pod בהמתנה שתואם לאילוצי התזמון של צומת מאגר יכול לתבוע את קיבולת המאגר. כשיש כמה עומסי עבודה שמשתמשים באותו מאגר צמתים או באותו ComputeClass בהתאמה אישית, אתם יכולים להשתמש בהפרדת עומסי עבודה כדי להקצות מאגר זמני למשאבי מחשוב לעומס עבודה ספציפי. הגישה הזו דורשת יצירה אוטומטית של מאגר צמתים, או מאגר צמתים ייעודי למאגרים פעילים באשכולות רגילים.
כדאי להימנע מהקצאת מאגרים זמניים נפרדים למשאבי מחשוב לעומסי עבודה רבים. לכל מאגר ייעודי נדרש מאגר צמתים משלו, מה שמאריך את הזמן שנדרש להקצאה ולמילוי מחדש של המאגרים. בקטע מדרגיות מפורטות המלצות למספר המאגרים.
כדי להקצות מאגר זמני למשאבי מחשוב לעומס עבודה ספציפי:
במניפסט של עומס העבודה, כמו
sample-workload-deployment.yamlמיצירת אובייקטים של Kubernetes שהם דרישות קדם, מוסיפים למפרט של ה-Pod סלקטור של צומת וסובלנות בהתאמה אישית:spec: tolerations: - key: KEY operator: Equal value: "VALUE" effect: NoSchedule nodeSelector: KEY: "VALUE" cloud.google.com/compute-class: ccc-exampleמחליפים את מה שכתוב בשדות הבאים:
-
KEY: מפתח מותאם אישית לתווית של ה-taint והצומת, למשלgroup. למידע על תחיליות מוגבלות של מפתחות, ראו המגבלות של הפרדת עומסי עבודה. -
VALUE: ערך מותאם אישית שמזהה את עומס העבודה, כמוcritical-workload.
באשכולות Autopilot, צריך להשאיר את ה-ComputeClass בהתאמה אישית או את בורר החומרה בשדה
nodeSelectorכדי שהמאגר ישתמש בחיוב מבוסס-צומת במקום במודל החיוב מבוסס-Pod שלא נתמך.-
מגדירים את CapacityBuffer כך שישתמש באותם אילוצי תזמון:
- אם CapacityBuffer משתמש בשדה
scalableRef, צריך להפנות לעומס העבודה בשדהspec.scalableRef. המאגר מקבל באופן אוטומטי את השדותtolerationsו-nodeSelectorשל עומס העבודה. - אם ב-CapacityBuffer נעשה שימוש בשדה
podTemplateRef, צריך להוסיף את אותם שדותtolerationsו-nodeSelectorלמניפסט של PodTemplate, כמוbuffer-pod-template.yaml.
- אם CapacityBuffer משתמש בשדה
מחילים את המניפסטים ויוצרים את CapacityBuffer. איך משתמשים במאגר זמני למשאבי מחשוב אם אתם טוענים מראש תמונות במאגרי המתנה, הוסיפו גם את הסובלנות בהתאמה אישית ל-DaemonSet של הטעינה מראש, כדי שתוכלו לתזמן אותה בצומתי המאגר הייעודיים. אם המאגר משתמש ב-ComputeClass בהתאמה אישית, ה-DaemonSet צריך גם סובלנות ל-taint של ComputeClass:
tolerations: - key: "buffer.gke.io/standby-capacity-node-suspended" operator: "Exists" - key: KEY operator: Equal value: "VALUE" effect: NoSchedule - key: cloud.google.com/compute-class operator: Equal value: ccc-example effect: NoScheduleאחרי שהמידרוג האוטומטי של האשכול מקצה את צומתי המאגר, מוודאים שלצמתים יש את תווית הצומת הייעודית ואת ה-taint של הצומת:
kubectl get nodes -l KEY=VALUE \ -o custom-columns='NAME:.metadata.name,TAINTS:.spec.taints[*].key'כל צומת בפלט מציג את מפתח ה-taint של
KEY. אם המאגר משתמש ב-ComputeClass בהתאמה אישית, הצמתים גם מציגים את מפתח ה-taintcloud.google.com/compute-class. לצומתי מאגר זמני במצב המתנה יש גם טיינטים שמנוהלים על ידי GKE, כמוbuffer.gke.io/standby-capacity-node-suspendedבצמתים מושעים. כשהיקף עומס העבודה גדל, מריצים את הפקודהkubectl get pods -n NAMESPACE -o wideומוודאים שבעמודהNODEמופיעים שמות הצמתים הייעודיים מהפלט הזה.
התאמה אישית של התנהגות המאגר הזמני במצב המתנה
אתם יכולים להשתמש בהערות כדי להתאים אישית את האופן שבו מאגרי הנתונים במצב המתנה מתחילים ומתרעננים.
מוסיפים את ההערות האלה לשדה metadata.annotations של המשאב CapacityBuffer:
-
buffer.gke.io/standby-capacity-init-time: משך הזמן שבו צומת נשאר פעיל אחרי היצירה, לפני ש-GKE משעה אותו. מציינים מחרוזת משך זמן, כמו5mאו1h. ערך ברירת המחדל הוא5m.
buffer.gke.io/standby-capacity-refresh-frequency: התדירות שבה GKE מרענן צמתים מושהים. מציינים מחרוזת של משך זמן, כמו12hאו24h, עד למקסימום של120h(5 ימים). ערך ברירת המחדל הוא24h.
בדוגמה הבאה מוצג מניפסט עם השדות האופציונליים האלה, שמאפשרים להתאים אישית את ההתנהגות של מאגרי הנתונים הזמניים במצב המתנה:
apiVersion: autoscaling.x-k8s.io/v1beta1
kind: CapacityBuffer
metadata:
name: customized-standby-buffer
namespace: my-namespace
annotations:
buffer.gke.io/standby-capacity-init-time: "15m"
buffer.gke.io/standby-capacity-refresh-frequency: "12h"
spec:
podTemplateRef:
name: buffer-unit-template
replicas: 3
provisioningStrategy: "buffer.gke.io/standby-capacity"
טעינה מראש של תמונות במאגרי המתנה
כדי לקצר את זמני ההפעלה של עומסי העבודה כשצומת המתנה ממשיך לפעול, אתם יכולים לטעון מראש קובצי אימג' של קונטיינרים באמצעות DaemonSet. ה-DaemonSet פועל במהלך תקופת ההפעלה לפני שהצומת מושעה.
כדי לטעון מראש תמונות באמצעות DaemonSet:
שומרים את המניפסט הבא בתור
image-puller-daemonset.yaml:apiVersion: apps/v1 kind: DaemonSet metadata: name: image-prefetch-daemonset namespace: NAMESPACE spec: selector: matchLabels: name: image-prefetch template: metadata: labels: name: image-prefetch spec: tolerations: - key: "buffer.gke.io/standby-capacity-node-suspended" operator: "Exists" initContainers: - name: image-puller image: IMAGE_NAME command: ["sh", "-c", "true"] containers: - name: pause image: registry.k8s.io/pause:3.9מחליפים את מה שכתוב בשדות הבאים:
-
NAMESPACE: מרחב השמות של DaemonSet, לדוגמהcapacity-buffer-example. -
IMAGE_NAME: שם התמונה לטעינה מראש, לדוגמהyour-app-image:latest.
המניפסט הזה כולל את הסבילות
buffer.gke.io/standby-capacity-node-suspendedכדי שה-DaemonSet יישאר מתוזמן בצומתי המתנה אחרי ש-GKE משעה אותם.-
מחילים את המניפסט של DaemonSet על האשכול:
kubectl apply -f image-puller-daemonset.yamlמוודאים שה-DaemonSet נוצר:
kubectl get daemonset image-prefetch-daemonset -n NAMESPACEמוודאים שהמאגר הזמני למשאבי מחשוב נוצר ומוכן להקצאה:
kubectl get capacitybuffer CAPACITY_BUFFER_NAME -n NAMESPACEבודקים את הסטטוס שלו. בשדה
STATUSאמור להופיע הערךReadyForProvisioning.
מעקב אחרי הסטטוס והביצועים של המאגר הזמני למשאבי מחשוב
אתם יכולים לעקוב אחרי הסטטוס והתקינות של מאגרי הקיבולת באמצעות פקודות kubectl ומדדי Cloud Monitoring.
אימות סטטוס המשאב CapacityBuffer
כדי לבדוק את התקינות של המאגרים הזמניים למשאבי מחשוב ולוודא שהם מוכנים לקבל עומסי עבודה:
כדי לקבל את הסטטוס של כל מאגרי הקיבולת באשכול:
kubectl get capacitybuffer -Aבודקים את הסטטוס המפורט, התנאים ויומני האירועים של מאגר נתונים ספציפי:
kubectl describe capacitybuffer CAPACITY_BUFFER_NAME -n NAMESPACE
זיהוי צומתי מאגר המתנה מושעים
מכונות וירטואליות במאגר זמני מוקצות מראש, אבל נשארות במצב מושהה כדי להפחית את העלויות. אפשר לזהות את הצמתים המושהים האלה כי יש להם תנאי בהתאמה אישית. כדי לבדוק את המכונות של הצמתים שהושעו, מריצים את הפקודה הבאה:
kubectl get nodes -o custom-columns='NAME:.metadata.name,SUSPENDED:.status.conditions[?(@.type=="Suspended")].status'
הסטטוס True מציין שהמכונה הווירטואלית במצב המתנה מושעית. הסטטוס False או <none> מציין צומת פעיל שפועל.
מעקב אחרי הביצועים באמצעות Cloud Monitoring
כדי לעקוב אחרי ההשפעה מקצה לקצה של מאגרי הקיבולת על זמני ההרחבה, אפשר להשתמש בלוחות הבקרה של זמן האחזור בהפעלה במסוף Google Cloud . בתרשימים Pod Startup Latency Distribution ו-Pod First Ready Latency אפשר לעקוב אחרי המדד kubernetes.io/pod/latencies/pod_first_ready, מיצירת ה-Pod ועד שהוא מוכן. אפשר גם לשלוח שאילתות לגבי אירועי Scheduled ב-Cloud Logging כדי למדוד את זמן האחזור של תזמון Pod. הוראות מופיעות במאמר מעקב אחרי מדדי זמן האחזור בהפעלה.
הסרת מאגרים זמניים למשאבי מחשוב
אם אתם לא צריכים יותר מאגר זמני למשאבי מחשוב בשביל עומסי העבודה, אתם יכולים למחוק את האובייקט CapacityBuffer. כך מסירים את הפלייסהולדרים של Pods ומאפשרים ל-cluster autoscaler לצמצם את הצמתים.
kubectl delete capacitybuffer CAPACITY_BUFFER_NAME -n NAMESPACE
מחליפים את CAPACITY_BUFFER_NAME בשם של ה-CapacityBuffer שרוצים למחוק.
פתרון בעיות
בקטע הבא מוסבר איך לפתור בעיות נפוצות שקשורות למאגרים זמניים למשאבי מחשוב.
מאגר הקיבולת לא מוכן בגלל מודל החיוב
אם יוצרים CapacityBuffer לעומס עבודה שמשתמש במודל החיוב שמבוסס על Pod (תשלום לפי Pod), המאגר הזמני למשאבי מחשוב לא יהיה מוכן להקצאה.
כדי לזהות את הבעיה, בודקים את הסטטוס של CapacityBuffer:
kubectl describe capacitybuffer BUFFER_NAME -n NAMESPACE
מחפשים תנאי מסוג ReadyForProvisioning עם סטטוס False.
כדי לפתור את הבעיה, ודאו שה-CapacityBuffer מפנה לעומס עבודה או ל-PodTemplate שתואמים לחיוב מבוסס-צומת.
פודים של מאגר זמני לא מתוזמנים בצמתים שהוקצו על ידי ComputeClasses בהתאמה אישית
אם התלות בהגדרות של CapacityBuffer (לדוגמה, PodTemplate) בוחרת ComputeClass בהתאמה אישית, יכול להיות שתיתקלו בבעיה שבה פודים של מאגר זמני לא מתוזמנים בצמתים שהוקצו לאחרונה. הבעיה הזו גורמת למנגנון המידרוג האוטומטי של האשכול ליצור שוב ושוב מאגרי צמתים וצמתים מיותרים, בזמן שצמתים לא פעילים נמחקים.
הבעיה הזו מתרחשת אם ה-ComputeClass בהתאמה אישית מקצה צמתים עם טולרציות או דרישות ספציפיות של זמן ריצה, ולפודים של המאגר (או ל-PodTemplate שהם מסתמכים עליו) אין טולרציות או הגדרות תואמות.
כדי לפתור את הבעיה, צריך לעדכן את ההגדרות של CapacityBuffer ולציין את ה-tolerations, ה-node selectors או ה-runtime classes הנדרשים. לדוגמה, אם התלות בוחרת ComputeClass בהתאמה אישית שמוגדר עם GKE Sandbox, צריך לעדכן את התלות כדי לבצע אחת מהפעולות הבאות:
- צריך לציין את השדה
spec.runtimeClassName: gvisor. - מוסיפים סבילות מפורשת ל-taint של
sandbox.gke.io/runtime=gvisor:NoSchedule.
מידע נוסף מופיע במאמרי העזרה בנושא ComputeClass בהתאמה אישית.
צומתי מאגר הנתונים הזמני במצב המתנה לא ממשיכים לפעול
יכול להיות שצומתי מאגר הנתונים להחלפה במצב השהיה לא יופעלו מחדש בגלל אחת מהסיבות הבאות:
- כדי להמשיך צומת המתנה מושהה, צריך קיבולת זמינה ב-Compute Engine באזור של סוג המכונה של הצומת ודור ספציפי של פלטפורמת המעבד (CPU), כמו בסדרת המכונות E2 או N2. אם לאזור אין מספיק קיבולת מחשוב בשביל ההגדרה המדויקת הזו, הצומת לא ימשיך לפעול. מידע נוסף זמין במאמר מגבלות ההשעיה וההמשך של Compute Engine.
- אם ב-ComputeClass בהתאמה אישית מופעלת העברה פעילה לצמתים בעדיפות גבוהה יותר (
activeMigration.optimizeRulePriority: true) ומכונות וירטואליות במודל Spot מוגדרות בעדיפות גבוהה יותר ממכונות וירטואליות על פי דרישה, GKE מעביר עם הזמן צמתים במצב המתנה על פי דרישה למכונות וירטואליות במודל Spot. מכונות וירטואליות במודל Spot כפופות יותר למגבלות קיבולת, ואי אפשר להמיר מכונות וירטואליות מושהות במודל Spot למכונות וירטואליות על פי דרישה כשמחדשים את הפעילות שלהן. לכן, המגבלות האלה יכולות למנוע את חידוש הפעילות של צומתי ההמתנה האלה. - המשך הפעילות של הצומת חורג ממכסות Compute Engine של הפרויקט. מידע נוסף מופיע במאמר הקצאת המאגר הזמני למשאבי מחשוב נכשלה בגלל מגבלות המכסות.
כדי לזהות למה צומת המתנה לא המשיך לפעול, שולחים שאילתה ליומני הביקורת של Compute Engine ב-Logs Explorer לגבי שגיאות מסוג ZONE_RESOURCE_POOL_EXHAUSTED או QUOTA_EXCEEDED בפעולות של compute.instances.resume.
כדי לפתור או לצמצם את הבעיה, נסו אחד או יותר מהפתרונות הבאים:
- להשתמש באשכולות אזוריים כדי שהמידרוג האוטומטי של האשכול יוכל להקצות או להמשיך צמתים בכמה אזורים.
- מגדירים כמה משפחות מכונות ברשימה
spec.prioritiesשל ה-ComputeClass בהתאמה אישית. אם אי אפשר להמשיך את הצמתים שהושעו, הכלי למידרוג אוטומטי של האשכול יכול לחזור ליצירת צמתים חדשים על סמך כלל עדיפות אחר. לצמתים החדשים האלה לוקח יותר זמן להיות מוכנים מאשר לצמתים במצב המתנה שהופעלו מחדש. ההגדרה הזו לא עוזרת לצמתים מושעים להמשיך, כי צומת מושעה יכול להמשיך רק עם סוג המכונה המקורי שלו. - משתמשים ב-ComputeClass ייעודי בהתאמה אישית עם מכונות וירטואליות על פי דרישה, או מגדירים את השדה
activeMigration.optimizeRulePriorityלערךfalseב-ComputeClass שבו משתמש מאגר ה-standby. - כדאי לשלב את מאגר הנתונים הזמני במצב המתנה עם מאגר נתונים זמני פעיל קטן, כדי לספוג עליות חדות מיידיות בתנועה.
אי אפשר להקצות מאגר קיבולת בגלל מגבלות המכסה
אם מאגר הקיבולת חורג מהמכסות של Kubernetes ResourceQuotas או של Compute Engine, יכול להיות שהוא יקצה פחות רפליקות מהכמות שביקשתם או לא יצליח להגדיל את המשאבים.
כדי לזהות איזו מכסה מגבילה את המאגר, בודקים אחת או יותר מהמכסות הבאות:
בודקים אם ResourceQuota של Kubernetes במרחב השמות מגביל את המאגר:
kubectl describe capacitybuffer \ CAPACITY_BUFFER_NAME \ -n NAMESPACEבפלט של
status.conditions, מחפשים תנאי שלLimitedByQuotasעם הסטטוסTrue. גם יחידות מאגר פעילות וגם יחידות מאגר במצב המתנה, כולל יחידות שמגובות על ידי צומתי המתנה מושהים, נספרות במגבלות ResourceQuota של מרחב השמות כאילו יחידות ה-Pod של המאגר פועלות. אם מאגר נתונים זמני חורג מהמכסה של מרחב השמות, GKE מגביל את השדהstatus.replicasכך שיתאים למכסה הזמינה.בודקים אם מכסות הפרויקט ב-Compute Engine חוסמות את הקצאת הצמתים. ב-Logs Explorer, מחפשים אירועים מסוג
no.scale.up.nap.quota.exceededביומןcontainer.googleapis.com/cluster-autoscaler-visibility, או מחפשים שגיאות מסוגQUOTA_EXCEEDEDביומני הביקורת של Compute Engine.כשבודקים את המכסות של Compute Engine למאגרי המתנה, צריך לוודא את שתי המכסות הבאות בנוסף למכסות של vCPU וזיכרון:
- צומתי המתנה מושעים שומרים על כתובות ה-IP הפנימיות וכתובות ה-IP החיצוניות שלהם, מה שצורך את מכסת כתובות ה-IP שבשימוש.
- כשמשהים VM במצב המתנה, Compute Engine כותב את הזיכרון של הצומת בדיסק, מה שצורך מכסת דיסק של
Persistent disk standard (GB)באופן יחסי לגודל הזיכרון של הצומת.
כדי לפתור את הבעיה, אתם יכולים לבצע אחת מהפעולות הבאות:
- מגדילים את ה-ResourceQuota של Kubernetes במרחב השמות של המאגר הזמני.
- בקשה לשינוי המכסות ב-Compute Engine
- מקטינים את הערך של
replicas,limitsאוpercentageבמניפסט של CapacityBuffer.
פודים של DaemonSet מפסיקים לקבל נתונים אחרי שצומת במצב המתנה ממשיך לפעול
אחרי שצומת מאגר מושעה במצב המתנה ממשיך לפעול, יכול להיות שפודים של DaemonSet בצומת הזה יפסיקו לקבל נתונים דרך חיבורים ארוכי טווח שנפתחו לפני שהצומת הושעה. ה-Pods לא מתעדים שגיאות ולא מתחברים מחדש.
הבעיה הזו מתרחשת כש-DaemonSet Pod פועל כלקוח לקבלה בלבד, למשל לתגובת HTTP בסטרימינג, לחיבור WebSocket, לאירועים שנשלחים מהשרת או לקריאת gRPC לסטרימינג מהשרת. בזמן שהצומת בהמתנה מושעה, הזמן הקצוב של השרת או של מכשיר רשת ביניים מסתיים והחיבור נסגר. כשהצומת ממשיך לפעול, מקבץ הרשת (network stack) של ה-Pod עדיין מחשיב את החיבור כפתוח. מכיוון שה-Pod לא שולח נתונים, הוא אף פעם לא מזהה שהחיבור נסגר וממתין לנתונים ללא הגבלת זמן.
כדי לפתור את הבעיה, צריך להגדיר את אפליקציית הלקוח בפוד של DaemonSet כך שתזהה חיבורים סגורים ותתחבר מחדש. אפשר לעשות את זה באחת מהדרכים הבאות:
- מגדירים את TCP keepalive בצד הלקוח באפליקציה של ה-Pod (הפעלת keepalive רק בשרת לא שולחת התראה ל-Pod). מידע נוסף על פרמטרים של TCP keepalive זמין במאמר חיבורים במצב חוסר פעילות.
- מפעילים את אפשרות השקע
SO_KEEPALIVEבספריית הלקוח או בסביבת זמן הריצה של השפה, כמוsocket.setKeepAlive(true, initialDelay)ב-Node.js אוnet.Dialer.KeepAliveב-Go. בחלק מסביבות זמן הריצה, האפשרות TCP keepalive מושבתת כברירת מחדל. - הגדירו זמן קצר של חוסר פעילות ב-keepalive (
TCP_KEEPIDLE), למשל כמה שניות, במקום להסתמך על ברירת המחדל של מערכת ההפעלה. - עדכנו את האפליקציה כך שתתפוס את שגיאת החיבור (כמו איפוס החיבור או זמן קצוב לתפוגה) כשבדיקת ה-keepalive נכשלת, ותפתח חיבור חדש.
- מפעילים את אפשרות השקע
- מגדירים בלקוח אותות חיים (heartbeats) ברמת האפליקציה או פסקי זמן לקריאה, כמו מסגרות פינג ופונג של WebSocket או פינגים של gRPC keepalive, ומעדכנים את האפליקציה כך שתתחבר מחדש כשמתרחש פסק זמן.