הגדרת מאגרי קיבולת

מאגרי קיבולת משפרים את הרספונסיביות והאמינות של עומסי עבודה קריטיים, על ידי ניהול יזום של קיבולת אשכולות פנויה ומצבים מושהים של קיבולת שהוקצתה והוגדרה מראש, באמצעות 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 או מקבלים גישה לאשכול GKE שעומד בדרישות הגרסה הבאות:
    • במאגרים פעילים, צריך להשתמש בגרסה 1.35.2-gke.1842000 ואילך באשכולות Standard ו-Autopilot.
    • כדי להשתמש במאגרי נתונים זמניים במצב המתנה, צריך להשתמש בגרסה 1.36.0-gke.2253000 ואילך באשכולות Standard, או בגרסה 1.36.0-gke.2853000 ואילך באשכולות Autopilot עם ComputeClasses בהתאמה אישית.
  • מפעילים יצירה אוטומטית של מאגרי צמתים באשכולות הרגילים. באשכולות Autopilot, היצירה האוטומטית של מאגרי צמתים כבר מופעלת. יצירה אוטומטית של מאגרי צמתים היא אופציונלית, אבל מומלצת למאגרים פעילים ונדרשת למאגרים במצב המתנה.

יצירת אובייקטים של Kubernetes שנדרשים מראש

כדי להגדיר CapacityBuffer, צריך מרחב שמות שמכיל את כל האובייקטים הנדרשים (CapacityBuffer עצמו ומשאבים נוספים כמו PodTemplate או עומס עבודה). ה-PodTemplate וה-CapacityBuffer צריכים להיות באותו מרחב שמות. אתם יכולים ליצור מרחב שמות או להשתמש במרחב שמות קיים, כולל מרחב השמות default.

בהתאם לסוג של CapacityBuffer שאתם מגדירים, תצטרכו גם אחת מהאפשרויות הבאות:

  • ‫PodTemplate: מגדיר את דרישות המשאבים ליחידה אחת של קיבולת מאגר. ההגדרה שצוינה באובייקט CapacityBuffer מפנה לתבנית Pod.
  • עומס עבודה: עומס עבודה קיים שאתם מפנים אליו באובייקט CapacityBuffer. במדריך הזה נעשה שימוש באובייקט Deployment כדוגמה לעומס עבודה, אבל מאגרי קיבולת תומכים בכל אחד מסוגי המשאבים הבאים:

    • פריסה
    • ReplicaSet
    • StatefulSet
    • ReplicationController
    • משימה
    • ‫CustomResourceDefinitions ‏ (CRD) שמטמיעים את משאב המשנה scale.

בקטע הזה מופיעות דוגמאות לאובייקטים האלה. אם כבר יש לכם עומס עבודה שאתם רוצים להגדיר עם מאגר קיבולת, המשיכו לקטע החלת מאגר קיבולת.

כדי ליצור עומס עבודה לדוגמה ב-Kubernetes:

  1. שומרים את המניפסט הבא בתור namespace.yaml:

    apiVersion: v1
    kind: Namespace
    metadata:
      name: capacity-buffer-example
      labels:
        name: capacity-buffer-example
    

    המניפסט הזה יוצר מרחב שמות בשם capacity-buffer-example.

  2. אופציונלי: כדי להשתמש במאגרי קיבולת עם 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 בהתאמה אישית.

  3. שומרים את המניפסט הבא בתור 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 של משאבים זמינים כחלק מהמאגר.

  4. שומרים את המניפסט הבא בתור 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 רפליקות, שהן אובייקט העזר לדוגמה של מאגר מבוסס-אחוזים בקטע הבא.

  5. מחילים את המניפסטים על האשכול:

    kubectl apply -f namespace.yaml -f custom-compute-class.yaml -f buffer-pod-template.yaml -f sample-workload-deployment.yaml
    
  6. מוודאים ש-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.

כדי ליצור מאגר עם רפליקות קבועות:

  1. שומרים את המניפסט הבא בתור 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 כדי לבקש מספר ספציפי של יחידות מאגר.

  2. מחילים את המניפסט:

    kubectl apply -f cb-fixed-replicas.yaml
    
  3. מוודאים ש-GKE החיל את מאגר הקיבולת:

    kubectl get capacitybuffer fixed-replica-buffer -n NAMESPACE
    

    השדה replicas בסטטוס אמור להציג את הערך 3, שמשקף את מספר הרפליקות שהגדרתם במניפסט. בשדה STATUS אמור להופיע הערך ReadyForProvisioning.

הגדרת מאגר זמני למגבלות על משאבים

אתם יכולים להשתמש בשדה limits כדי להגדיר את כמות המשאבים המקסימלית שהמאגר הזמני יכול לצרוך, שמחושבת על סמך הגודל של PodTemplate.

כדי ליצור מאגר למגבלות המשאבים:

  1. שומרים את המניפסט הבא בתור 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 יחידות מאגר.

  2. מחילים את המניפסט:

    kubectl apply -f cb-resource-limits.yaml
    
  3. מוודאים ש-GKE החיל את מאגר הקיבולת:

    kubectl get capacitybuffer resource-limit-buffer -n NAMESPACE
    

    בודקים את הסטטוס של CapacityBuffer. בשדה replicas אמור להופיע ערך שנגזר מהמגבלות שהגדרתם. אם אתם משתמשים בדוגמה של PodTemplate מהקטע הקודם, אתם אמורים לראות 5 יחידות מאגר, כי זה המספר המקסימלי של יחידות שמתאימות למגבלות שהוגדרו.

הגדרת מאגר נתונים זמני שמבוסס על אחוזים

הגדרת מאגר מבוסס-אחוזים משנה באופן דינמי את גודל המאגר על סמך אחוז מעומס עבודה קיים שניתן להתאמה לעומס. מאגרי קיבולת שמבוססים על אחוזים נתמכים רק באובייקטים של Kubernetes שניתנים להתאמה, שמטמיעים את משאב המשנה של ההתאמה, כמו Deployments, ‏ StatefulSets, ‏ ReplicaSets או Jobs. אי אפשר להגדיר מאגר מבוסס-אחוזים לתבניות Pod כי אין להן שדה replicas.

ברוב המקרים, כדאי להתחיל עם אסטרטגיות של רפליקות קבועות או מגבלות משאבים, ולא עם מאגרים שמבוססים על אחוזים. כדאי להביא בחשבון את הנקודות הבאות:

  • מאגרים שמבוססים על אחוזים פחות מגיבים להגדלות פתאומיות אם עומס העבודה מוגדל למספרים נמוכים או לאפס, כי מרווח הבטיחות פוחת ביחס לפודים הפעילים.
  • צומתי המתנה צוברים חיובי מחשוב במהלך תקופת האתחול שלהם לפני ההשעיה. התאמה דינמית של מאגרי ה-standby בהתאם לתנודות בעומס העבודה מבזבזת משאבי מחשוב, ולכן כדאי להשתמש ברפליקות קבועות או במגבלות משאבים למאגרי ה-standby.
  • מאגרים מבוססי-אחוזים שימושיים בעיקר למאגרים פעילים בפריסות גדולות, שבהן מספר הרפליקות אף פעם לא קטן.

ב-GKE בגרסה 1.36.3-gke.1244000 ואילך, כדי לשמור על שטח אחסון זמני מינימלי כשעומס העבודה המקושר מצטמצם, מגדירים את השדה replicas יחד עם השדה percentage. ‫GKE משתמש בשדה שמוביל ליותר יחידות מאגר. כדי להגביל את שטח האחסון הזמני, צריך להגדיר גם את השדה limits.

לדוגמה, נניח שיש מאגר עם percentage: 20 ו-replicas: 3 שמפנה לפריסה עם 10 רפליקות. ‫20% מ-10 רפליקות הם 2, וזה פחות מהערך 3 של replicas, אז GKE מקצה 3 יחידות מאגר. אם הפריסה מתרחבת ל-20 רפליקות, המאגר גדל ל-4 יחידות. מידע נוסף זמין במאמרי העזרה של CapacityBuffer.

כדי ליצור מאגר באחוזים, מבצעים את השלבים הבאים:

  1. שומרים את המניפסט הבא בתור 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.

  2. מחילים את המניפסט:

    kubectl apply -f cb-percentage-based.yaml
    
  3. מוודאים ש-GKE החיל את מאגר הקיבולת:

    kubectl get capacitybuffer percentage-buffer -n NAMESPACE
    

    בודקים את הסטטוס של CapacityBuffer. בשדה replicas צריך להופיע ערך מחישוב האחוזים. אם אתם משתמשים בדוגמה לפריסה מהקטע הקודם, אתם אמורים לראות 2 יחידות מאגר, שהן 20% מ-10 הרפליקות שהוגדרו בפריסה.

  4. כדי לבדוק את ההתאמה הדינמית לעומס, מגדילים את הפריסה באופן ידני ל-20 רפליקות:

    kubectl scale deployment critical-workload-ref -n NAMESPACE --replicas=20
    

    הבקר CapacityBuffer מגיב ומגדיל באופן אוטומטי את המאגר ל-4 רפליקות.

התאמה אישית של התנהגות המאגר הזמני במצב המתנה

אתם יכולים להשתמש בהערות כדי להתאים אישית את האופן שבו מאגרי הנתונים הזמניים במצב המתנה מתחילים ומתרעננים. מוסיפים את ההערות האלה לשדה metadata.annotations של המשאב CapacityBuffer:

  • ‫buffer.gke.io/standby-capacity-init-time: משך הזמן שבו צומת נשאר פעיל אחרי היצירה שלו, לפני ש-GKE משעה אותו. מציינים מחרוזת משך זמן, כמו 5m או 1h. ערך ברירת המחדל הוא 5m.
  • ‫buffer.gke.io/standby-capacity-refresh-frequency: התדירות שבה GKE מרענן צמתים מושהים. מציינים מחרוזת של משך זמן, כמו 12h או 24h. ערך ברירת המחדל הוא 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:

  1. שומרים את המניפסט הבא בתור 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 משעה אותם.

  2. מחילים את המניפסט של DaemonSet על האשכול:

    kubectl apply -f image-puller-daemonset.yaml
    
  3. מוודאים שה-DaemonSet נוצר:

    kubectl get daemonset image-prefetch-daemonset -n NAMESPACE
    
  4. מוודאים שהמאגר הזמני של הקיבולת נוצר ומוכן להקצאה:

    kubectl get capacitybuffer CAPACITY_BUFFER_NAME -n NAMESPACE
    

    בודקים את הסטטוס שלו. בשדה STATUS אמור להופיע הערך ReadyForProvisioning.

מעקב אחרי הסטטוס והביצועים של מאגר הנתונים הזמני של הקיבולת

אתם יכולים לעקוב אחרי הסטטוס והתקינות של מאגרי הקיבולת באמצעות פקודות kubectl ומדדי Cloud Monitoring.

אימות סטטוס המשאב CapacityBuffer

כדי לבדוק את התקינות של מאגרי הקיבולת ולוודא שהם מוכנים לקבל עומסי עבודה, בצעו את הפעולות הבאות:

  1. כדי לקבל את הסטטוס של כל מאגרי הקיבולת באשכול:

    kubectl get capacitybuffer -A
    
  2. בודקים את הסטטוס המפורט, התנאים ויומני האירועים של מאגר נתונים ספציפי:

    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 של ה-placeholder ומאפשרים ל-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 שתואמים לחיוב מבוסס-צומת.

שגיאות הרשאה במשאבים מותאמים אישית שניתנים להתאמה לעומס

אם מגדירים CapacityBuffer שיעבוד עם אובייקטים מותאמים אישית שניתנים להרחבה (באמצעות השדה scalableRef), יכול להיות שהמידרוג האוטומטי של אשכולות לא יצליח להתאים את המאגר לעומס אם אין לו את ההרשאות הנדרשות. הבעיה הזו מתרחשת רק בגרסאות GKE שקודמות ל-1.36.0-gke.2459000.

כדי לפתור את הבעיה, צריך לתת את ההרשאות הנדרשות באופן ידני על ידי יצירת ClusterRole ו-ClusterRoleBinding, כמו בדוגמה הבאה:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: custom-scale-getter
rules:
- apiGroups: ["api.example.com"]
  resources: ["customreplicatedresources/scale"]
  verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: ca-custom-scale-getter
subjects:
- kind: User
  name: "system:cluster-autoscaler"
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: custom-scale-getter

מידע נוסף על הגדרת RBAC זמין במסמכי התיעוד של Kubernetes RBAC.

פודים של מאגר זמני לא מתוזמנים בצמתים שהוקצו על ידי ComputeClasses בהתאמה אישית

אם התלות בהגדרות של CapacityBuffer (לדוגמה, PodTemplate) בוחרת ComputeClass בהתאמה אישית, יכול להיות שתיתקלו בבעיה שבה פודים של מאגר זמני לא מתוזמנים בצמתים שהוקצו לאחרונה. הבעיה הזו גורמת למנגנון המידרוג האוטומטי של האשכול ליצור שוב ושוב מאגרי צמתים וצמתים מיותרים, בזמן שצמתים לא פעילים נמחקים.

הבעיה הזו מתרחשת אם ה-ComputeClass בהתאמה אישית מקצה צמתים עם taints או דרישות ספציפיות של זמן ריצה, ולפודים של המאגר (או ל-PodTemplate שהם מסתמכים עליו) אין tolerations או הגדרות תואמות.

כדי לפתור את הבעיה, צריך לעדכן את ההגדרות של 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 בהתאמה אישית (CCC) ייעודי עם מכונות וירטואליות על פי דרישה, או מגדירים את השדה 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, מחפשים ביומן container.googleapis.com/cluster-autoscaler-visibility אירועים מסוג no.scale.up.nap.quota.exceeded, או מחפשים ביומני הביקורת של Compute Engine שגיאות מסוג QUOTA_EXCEEDED.

    כשבודקים את המכסות של Compute Engine למאגרי המתנה, צריך לוודא את שתי המכסות הבאות בנוסף למכסות של vCPU וזיכרון:

    • צומתי המתנה מושעים שומרים על כתובות ה-IP הפנימיות וכתובות ה-IP החיצוניות שלהם, מה שצורך את מכסת כתובות ה-IP שבשימוש.
    • כשמשהים VM במצב המתנה, Compute Engine כותב את הזיכרון של הצומת בדיסק, מה שצורך מכסת דיסק של Persistent disk standard (GB) באופן יחסי לגודל הזיכרון של הצומת.

כדי לפתור את הבעיה, אתם יכולים לבצע אחת מהפעולות הבאות:

פודים של DaemonSet מפסיקים לקבל נתונים אחרי שצומת במצב המתנה ממשיך לפעול

אחרי שצומת מאגר מושעה במצב המתנה ממשיך לפעול, יכול להיות שפודים של DaemonSet בצומת הזה יפסיקו לקבל נתונים דרך חיבורים ארוכי טווח שנפתחו לפני שהצומת הושעה. ה-Pods לא מתעדים שגיאות ולא מתחברים מחדש.

הבעיה הזו מתרחשת כש-DaemonSet Pod פועל כלקוח לקבלה בלבד, למשל לתגובת HTTP בסטרימינג, לחיבור WebSocket, לאירועים שנשלחים מהשרת או לקריאת gRPC לסטרימינג מהשרת. בזמן שהצומת בהמתנה מושעה, הזמן הקצוב של השרת או של מכשיר רשת ביניים מסתיים והחיבור נסגר. כשהצומת ממשיך לפעול, מקבץ הרשת (network stack) של ה-Pod עדיין מחשיב את החיבור כפתוח. מכיוון שה-Pod לא שולח נתונים, הוא אף פעם לא מזהה שהחיבור נסגר וממתין לנתונים ללא הגבלת זמן.

כדי לפתור את הבעיה, צריך להגדיר את אפליקציית הלקוח ב-DaemonSet Pod לזהות חיבורים סגורים ולהתחבר מחדש, באחת מהדרכים הבאות:

  • מגדירים את TCP keepalive בצד הלקוח באפליקציה של ה-Pod (הפעלת keepalive רק בשרת לא שולחת התראה ל-Pod). מידע נוסף על פרמטרים של TCP keepalive זמין במאמר חיבורים במצב חוסר פעילות.
    1. מפעילים את אפשרות השקע SO_KEEPALIVE בספריית הלקוח או בסביבת זמן הריצה של השפה, כמו socket.setKeepAlive(true, initialDelay) ב-Node.js או net.Dialer.KeepAlive ב-Go. בחלק מסביבות זמן הריצה, האפשרות TCP keepalive מושבתת כברירת מחדל.
    2. הגדירו זמן קצר של חוסר פעילות ב-keepalive (TCP_KEEPIDLE), למשל כמה שניות, במקום להסתמך על ברירת המחדל של מערכת ההפעלה.
    3. עדכנו את האפליקציה כך שתתפוס את שגיאת החיבור (כמו איפוס החיבור או זמן קצוב לתפוגה) כשבדיקת keepalive נכשלת, ותפתח חיבור חדש.
  • מגדירים בלקוח אותות חיים (heartbeats) ברמת האפליקציה או פסקי זמן לקריאה, כמו מסגרות פינג ופונג של WebSocket או פינגים של gRPC keepalive, ומעדכנים את האפליקציה כך שתתחבר מחדש כשמתרחש פסק זמן.

המאמרים הבאים