פתרון בעיות באשכולות של Autopilot

בעיות באשכולות Autopilot של Google Kubernetes Engine‏ (GKE) יכולות להשפיע על הזמינות של האפליקציה ועל היעילות התפעולית שלה. הבעיות האלה עלולות לשבש את כל מחזור החיים של האפליקציות, החל מהפריסה הראשונית ועד להתאמה של האפליקציה לשינויים בעומס.

בדף הזה אפשר לאבחן ולפתור בעיות נפוצות שספציפיות לאשכולות של Autopilot. במאמר הזה מוסבר איך לפתור בעיות שמונעות הקצאת משאבים לאשכול, בעיות שקשורות לשינוי גודל האשכול כמו שגיאות out of resources, ובעיות שקשורות לעומסי עבודה כמו שגיאות באחסון זמני או פודים שנתקעו במצב Pending.

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

בעיות באשכול

אי אפשר ליצור אשכול: לא נרשמו צמתים

הבעיה הבאה מתרחשת כשמנסים ליצור אשכול Autopilot עם חשבון שירות IAM שמושבת או שאין לו את ההרשאות הנדרשות. יצירת האשכול נכשלת עם הודעת השגיאה הבאה:

All cluster resources were brought up, but: only 0 nodes out of 2 have registered.

כדי לפתור את הבעיה:

  1. בודקים אם חשבון השירות שמוגדר כברירת מחדל ב-Compute Engine או חשבון השירות המותאם אישית ב-IAM שרוצים להשתמש בו מושבת:

    gcloud iam service-accounts describe SERVICE_ACCOUNT
    

    מחליפים את SERVICE_ACCOUNT בכתובת האימייל של חשבון השירות, לדוגמה my-iam-account@my-first-project.iam.gserviceaccount.com.

    אם חשבון השירות מושבת, הפלט דומה לפלט הבא:

    disabled: true
    displayName: my-service-account
    email: my-service-account@my-project.iam.gserviceaccount.com
    ...
    
  2. אם חשבון השירות מושבת, צריך להפעיל אותו:

    gcloud iam service-accounts enable SERVICE_ACCOUNT
    

אם חשבון השירות מופעל והשגיאה נמשכת, צריך להעניק לחשבון השירות את ההרשאות המינימליות שנדרשות ל-GKE:

gcloud projects add-iam-policy-binding PROJECT_ID \
    --member "serviceAccount:SERVICE_ACCOUNT" \
    --role roles/container.defaultNodeServiceAccount

מרחב שמות נתקע במצב Terminating (סיום) כשלצבר יש 0 צמתים

הבעיה הבאה מתרחשת כשמוחקים מרחב שמות באשכול אחרי שהאשכול מצטמצם לאפס צמתים. רכיב metrics-server לא יכול לקבל את בקשת המחיקה של מרחב השמות כי יש לו אפס עותקים.

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

kubectl describe ns/NAMESPACE_NAME

מחליפים את NAMESPACE_NAME בשם של מרחב השמות.

הפלט שיתקבל:

Discovery failed for some groups, 1 failing: unable to retrieve the complete
list of server APIs: metrics.k8s.io/v1beta1: the server is currently unable to
handle the request

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

בעיות שקשורות לשינוי גודל

הגדלת קנה המידה של הצומת נכשלה: קיים סיכון שה-Pod לא יתוזמן

הבעיה הבאה מתרחשת כשרישום ביומן של יציאה טורית מושבת בGoogle Cloud פרויקט. כדי לנפות ביעילות בעיות בצמתים באשכולות GKE Autopilot, צריך להפעיל רישום ביומן של יציאה טורית. אם הרישום ביומן של יציאה טורית מושבת, Autopilot לא יכול להקצות צמתים להרצת עומסי העבודה.

הודעת השגיאה ביומן האירועים של Kubernetes דומה להודעה הבאה:

LAST SEEN   TYPE      REASON          OBJECT                          MESSAGE
12s         Warning   FailedScaleUp   pod/pod-test-5b97f7c978-h9lvl   Node scale up in zones associated with this pod failed: Internal error. Pod is at risk of not being scheduled

יכול להיות שהרישום ביומן של יציאה טורית מושבת ברמת הארגון באמצעות מדיניות ארגונית שמחייבת את האילוץ compute.disableSerialPortLogging. אפשר גם להשבית את הרישום ביומן של היציאה הטורית ברמת הפרויקט או ברמת המכונה הווירטואלית (VM).

כדי לפתור את הבעיה:

  1. צריך לבקש מהאדמין של מדיניות הארגון Google Cloud להסיר את האילוץ compute.disableSerialPortLogging בפרויקט עם אשכול Autopilot.
  2. אם אין לכם מדיניות ארגון שמחילה את האילוץ הזה, נסו להפעיל את הכתיבה ליומן של היציאה הטורית במטא-נתונים של הפרויקט. כדי לבצע את הפעולה הזו נדרשת הרשאת IAM‏ compute.projects.setCommonInstanceMetadata.

הגדלת הקיבולת של הצומת נכשלה: אין מספיק משאבים ב-GCE

הבעיה הבאה מתרחשת כשעומסי העבודה שלכם מבקשים יותר משאבים מאלה שזמינים לשימוש באזור או בתחום של Compute Engine. יכול להיות שה-Pods יישארו במצב Pending.

  • בודקים את האירועים ב-Pod:

    kubectl events --for='pod/POD_NAME' --types=Warning
    

    מחליפים את RESOURCE_NAME בשם של משאב Kubernetes בהמתנה. לדוגמה pod/example-pod.

    הפלט אמור להיראות כך:

    LAST SEEN         TYPE            REASON                  OBJECT                   Message
    19m               Warning         FailedScheduling        pod/example-pod          gke.io/optimize-utilization-scheduler  0/2 nodes are available: 2 node(s) didn't match Pod's node affinity/selector. preemption: 0/2 nodes are available: 2 Preemption is not helpful for scheduling.
    14m               Warning         FailedScheduling        pod/example-pod          gke.io/optimize-utilization-scheduler  0/2 nodes are available: 2 node(s) didn't match Pod's node affinity/selector. preemption: 0/2 nodes are available: 2 Preemption is not helpful for scheduling.
    12m (x2 over 18m) Warning         FailedScaleUp           cluster-autoscaler       Node scale up in zones us-central1-f associated with this pod failed: GCE out of resources. Pod is at risk of not being scheduled.
    34s (x3 over 17m) Warning         FailedScaleUp           cluster-autoscaler       Node scale up in zones us-central1-b associated with this pod failed: GCE out of resources. Pod is at risk of not being scheduled.
    

כדי לפתור את הבעיה, נסו את הפתרונות הבאים:

  • פורסים את ה-Pod באזור או באזור אחר. אם ל-Pod שלכם יש הגבלה אזורית, כמו בורר טופולוגיה, כדאי להסיר את ההגבלה אם אפשר. הוראות מפורטות במאמר בנושא מיקום של פודים של GKE באזורים ספציפיים.
  • יוצרים אשכול באזור אחר ומנסים שוב לפרוס את האפליקציה.
  • נסו להשתמש בסוג אחר של מכונת חישוב. יש סיכוי גבוה יותר שיהיו משאבים זמינים בכיתות Compute שמגובות על ידי סוגים קטנים יותר של מכונות ב-Compute Engine. לדוגמה, סוג המכונה שמוגדר כברירת מחדל ב-Autopilot הוא בעל הזמינות הגבוהה ביותר. רשימה של מחלקות מחשוב וסוגי המכונות התואמים מופיעה במאמר מתי כדאי להשתמש במחלקות מחשוב ספציפיות.
  • אם אתם מריצים עומסי עבודה של GPU, יכול להיות שה-GPU המבוקש לא יהיה זמין במיקום הצומת. נסו לפרוס את עומס העבודה במיקום אחר או לבקש סוג אחר של GPU.

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

הצמתים לא מצליחים להתרחב: חריגה ממגבלות המשאבים של ה-Pod באזור

הבעיה הבאה מתרחשת כש-Autopilot לא מקצה צמתים חדשים ל-Pod באזור מסוים כי צומת חדש יגרום לחריגה ממגבלות המשאבים.

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

    "napFailureReasons": [
            {
              "messageId": "no.scale.up.nap.pod.zonal.resources.exceeded",
              ...

השגיאה הזו מתייחסת לאירוע noScaleUp, שבו הקצאת משאבים אוטומטית של צומת לא הקצתה אף קבוצת צמתים ל-Pod באזור.

אם נתקלתם בשגיאה הזו, כדאי לבדוק את הדברים הבאים:

בעיות בעומסי עבודה

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

מערכת GKE לא תיצור Pod אם בקשות האחסון הזמני של ה-Pod חורגות מהמקסימום של 10GiB ב-Autopilot ב-GKE בגרסה 1.28.6-gke.1317000 ואילך.

כדי לאבחן את הבעיה הזו, צריך לתאר את בקרת עומס העבודה, כמו Deployment או Job:

kubectl describe CONTROLLER_TYPE/CONTROLLER_NAME

מחליפים את מה שכתוב בשדות הבאים:

  • CONTROLLER_TYPE: הסוג של בקר עומס העבודה, כמו replicaset או daemonset. רשימה של סוגי בקרים מופיעה במאמר ניהול עומסי עבודה.
  • CONTROLLER_NAME: השם של עומס העבודה התקוע.

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

# lines omitted for clarity

Events:

{"[denied by autogke-pod-limit-constraints]":["Max ephemeral-storage requested by init containers for workload '' is higher than the Autopilot maximum of '10Gi'.","Total ephemeral-storage requested by containers for workload '' is higher than the Autopilot maximum of '10Gi'."]}

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

חבילות (Pods) נתקעות במצב Pending

יכול להיות ש-Pod ייתקע במצב Pending אם תבחרו צומת ספציפי לשימוש ב-Pod, אבל סכום בקשות המשאבים ב-Pod וב-DaemonSets שצריכים לפעול בצומת יעלה על הקיבולת המקסימלית שניתן להקצות לצומת. יכול להיות שהסטטוס של ה-Pod יהיה Pending והוא יישאר לא מתוזמן.

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

אפשר גם לנסות לתזמן את DaemonSets לפני שתזמנו את פודי העומס הרגילים.

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

בגרסה 1.24 של GKE ואילך, אם עומסי העבודה בצומת מסוים חווים שיבושים, קריסות או התנהגות לא אמינה דומה באופן עקבי, אפשר להודיע ל-GKE על הצומת הבעייתי באמצעות בידוד שלו באמצעות הפקודה הבאה:

kubectl drain NODE_NAME --ignore-daemonsets

מחליפים את NODE_NAME בשם הצומת הבעייתי. אפשר להריץ את הפקודה kubectl get nodes כדי למצוא את שם הצומת.

‫GKE מבצע את הפעולות הבאות:

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

תזמון של פודים באשכולות ריקים נמשך יותר זמן מהצפוי

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

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

בודקים אם ה-Pods ממתינים לצמתים חדשים:

  1. תאר את ה-Pod בהמתנה:

    kubectl describe pod POD_NAME
    

    מחליפים את POD_NAME בשם של ה-Pod בהמתנה.

  2. בודקים את הקטע Events בפלט. אם ה-Pod ממתין לצמתים חדשים, הפלט אמור להיראות כך:

    Events:
      Type     Reason            Age   From                                   Message
      ----     ------            ----  ----                                   -------
      Warning  FailedScheduling  11s   gke.io/optimize-utilization-scheduler  no nodes available to schedule pods
      Normal   TriggeredScaleUp  4s    cluster-autoscaler                     pod triggered scale-up: [{https://www.googleapis.com/compute/v1/projects/example-project/zones/example-zone/instanceGroups/gk3-example-cluster-pool-2-9293c6db-grp 0->1 (max: 1000)} {https://www.googleapis.com/compute/v1/projects/example-project/zones/example-zone/instanceGroups/gk3-example-cluster-pool-2-d99371e7-grp 0->1 (max: 1000)}]
    

    האירוע TriggeredScaleUp מציין שהאשכול מתרחב מאפס צמתים למספר הצמתים שנדרש להפעלת עומס העבודה שנפרס.

תזמון של System Pods באשכולות ריקים נכשל

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

כשמצמצמים את מספר הצמתים באשכול לאפס, עומסי העבודה של מערכת GKE לא מתוזמנים ונשארים במצב Pending. זו התנהגות צפויה ולא נדרשת פעולה. בפעם הבאה שתפרסו עומס עבודה באשכול, GKE יגדיל את האשכול ופודים של המערכת בהמתנה יפעלו בצמתים האלה.

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

  1. בודקים אם יש צמתים באשכול:

    kubectl get nodes
    

    הפלט שמתקבל הוא הבא, שמציין שלקלאסטר אין צמתים:

    No resources found
    
  2. בודקים את הסטטוס של ה-Pods במערכת:

    kubectl get pods --namespace=kube-system
    

    הפלט אמור להיראות כך:

    NAME                                                       READY   STATUS    RESTARTS   AGE
    antrea-controller-horizontal-autoscaler-6d97f7cf7c-ngfd2   0/1     Pending   0          9d
    egress-nat-controller-84bc985778-6jcwl                     0/1     Pending   0          9d
    event-exporter-gke-5c5b457d58-7njv7                        0/2     Pending   0          3d5h
    event-exporter-gke-6cd5c599c6-bn665                        0/2     Pending   0          9d
    konnectivity-agent-694b68fb7f-gws8j                        0/2     Pending   0          3d5h
    konnectivity-agent-7d659bf64d-lp4kt                        0/2     Pending   0          9d
    konnectivity-agent-7d659bf64d-rkrw2                        0/2     Pending   0          9d
    konnectivity-agent-autoscaler-5b6ff64fcd-wn7fw             0/1     Pending   0          9d
    konnectivity-agent-autoscaler-cc5bd5684-tgtwp              0/1     Pending   0          3d5h
    kube-dns-65ccc769cc-5q5q7                                  0/5     Pending   0          3d5h
    kube-dns-7f7cdb9b75-qkq4l                                  0/5     Pending   0          9d
    kube-dns-7f7cdb9b75-skrx4                                  0/5     Pending   0          9d
    kube-dns-autoscaler-6ffdbff798-vhvkg                       0/1     Pending   0          9d
    kube-dns-autoscaler-8b7698c76-mgcx8                        0/1     Pending   0          3d5h
    l7-default-backend-87b58b54c-x5q7f                         0/1     Pending   0          9d
    metrics-server-v1.31.0-769c5b4896-t5jjr                    0/1     Pending   0          9d
    
  3. בודקים את הסיבה לסטטוס Pending של ה-Pods במערכת:

    kubectl describe pod --namespace=kube-system SYSTEM_POD_NAME
    

    מחליפים את SYSTEM_POD_NAME בשם של Pod במערכת מהפלט של הפקודה הקודמת.

    הפלט אמור להיראות כך:

    ...
    Events:
    Type     Reason            Age                       From               Message
    ----     ------            ----                      ----               -------
    Warning  FailedScheduling  4m35s (x27935 over 3d5h)  default-scheduler  no nodes available to schedule pods
    ...
    

    בפלט, הערך no nodes available to schedule pods בשדה Message של האירוע FailedScheduling מציין שלא בוצע תזמון של ה-Pod של המערכת כי האשכול ריק.

הגישה לצמתים הבסיסיים אסורה באשכול GKE Autopilot. לכן, צריך להריץ את כלי השירות tcpdump מתוך Pod ואז להעתיק אותו באמצעות הפקודה kubectl cp. אם בדרך כלל מריצים את כלי השירות tcpdump מתוך Pod באשכול GKE Autopilot, יכול להיות שתופיע השגיאה הבאה:

    tcpdump: eth0: You don't have permission to perform this capture on that device
    (socket: Operation not permitted)

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

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: tcpdump
  name: tcpdump
spec:
  containers:
  - image: nginx
    name: nginx
    resources:
      limits:
        cpu: 500m
        ephemeral-storage: 1Gi
        memory: 2Gi
      requests:
        cpu: 500m
        ephemeral-storage: 1Gi
        memory: 2Gi
    securityContext:
      capabilities:
        # This section drops NET_RAW to mitigate security vulnerabilities
        drop:
        - NET_RAW

כפתרון, אם עומס העבודה שלכם דורש את היכולת NET_RAW, אתם יכולים להפעיל אותה מחדש:

  1. מוסיפים את היכולת NET_RAW לקטע securityContext במפרט ה-YAML של ה-Pod:

    securityContext:
      capabilities:
        add:
        - NET_RAW
    
  2. מריצים את הפקודה tcpdump מתוך Pod:

    tcpdump port 53 -w packetcap.pcap
    tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
    
  3. משתמשים בפקודה kubectl cp כדי להעתיק אותו למכונה המקומית לצורך ניתוח נוסף:

    kubectl cp POD_NAME:/PATH_TO_FILE/FILE_NAME/PATH_TO_FILE/FILE_NAME
    
  4. משתמשים ב-kubectl exec כדי להריץ את הפקודה tcpdump כדי לבצע תיעוד של חבילות נתונים ברשת ולהפנות את הפלט:

    kubectl exec -it POD_NAME -- bash -c "tcpdump port 53 -w -" > packet-new.pcap
    

אי אפשר להריץ Pods בצומת בגלל כשל ב-NRI RunPodSandbox

במקרים נדירים, צומת Autopilot בסוגי מחשוב ברירת המחדל, מאוזן, Scale-Out,‏ autopilot ו-autopilot-spot יכול להיכנס למצב שבו פודים של המערכת, כמו anetd, ופודים של משתמשים שהוקצו לצומת, לא יכולים לעבור למצב פעיל. אם anetd לא יכול לעבור למצב פעיל, סימן שהרישות של ה-Pod פגום חלקית או לגמרי בצומת. מצב השגיאה הזה מופעל רק במהלך שדרוגים מסוימים של מישור הבקרה של האשכול.

אם אתם מבחינים בבעיה, אתם יכולים לצמצם אותה בצומת מסוים באמצעות ההליך לצמצום בעיות בביצועי עומס עבודה לא אמינים בצומת מסוים.

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

  • 1.35: כל גרסאות התיקון
  • 1.34: 1.34.1-gke.3899000 ואילך
  • 1.33: גרסה 1.33.5-gke.2392000 ואילך

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

  1. מוצאים את efficiency-daemon Pod שפועל בצומת החשוד:

    DAEMON_POD_NAME=$(kubectl get pods \
      --namespace kube-system \
      --selector k8s-app=efficiency-daemon \
      --field-selector spec.nodeName=NODE_NAME \
      --output custom-columns=":metadata.name" \
      --no-headers)
    echo $DAEMON_POD_NAME
    

    אם אין efficiency-daemon pod בצומת, הצומת לא מושפע מהבעיה הספציפית הזו.

  2. בודקים אם יש שגיאות ספציפיות ב-efficiency-daemon Pod:

    kubectl logs -n kube-system $DAEMON_POD_NAME | grep "connect: operation not permitted" | wc -l
    

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

אם הודעת השגיאה הבאה מופיעה שוב ושוב באירועים של Pod עבור Pod שהוקצה לצומת, סימן שהצומת מושפע מהבעיה הזו: Failed to create pod sandbox: rpc error: code = Unknown desc = NRI RunPodSandbox failed: rpc error: code = Unknown desc = internal error: reconciliation failed.

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