פתרון בעיות שקשורות לאחסון ב-GKE

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

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

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

שגיאה 400: אי אפשר לצרף RePD למכונה וירטואלית שעברה אופטימיזציה

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

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

פתרון בעיות שקשורות לביצועי הדיסק

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

  • קובצי אימג' של Docker.
  • מערכת הקבצים של הקונטיינר למה שלא נטען כנפח (כלומר, מערכת הקבצים של שכבת-העל), ולעתים קרובות היא כוללת ספריות כמו /tmp.
  • אמצעי אחסון של emptyDir שמגובים בדיסק, אלא אם הצומת משתמש ב-SSD מקומי.

ביצועי הדיסק משותפים לכל הדיסקים מאותו סוג דיסק בצומת. לדוגמה, אם יש לכם דיסק אתחול בנפח 100GB מסוג pd-standard ו-PersistentVolume בנפח 100GB מסוג pd-standard עם הרבה פעילות, הביצועים של דיסק האתחול יהיו כמו של דיסק בנפח 200GB. בנוסף, אם יש הרבה פעילות ב-PersistentVolume, זה משפיע גם על הביצועים של דיסק האתחול.

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

INFO: task dockerd:2314 blocked for more than 300 seconds.
fs: disk usage and inodes count on following dirs took 13.572074343s
PLEG is not healthy: pleg was last seen active 6m46.842473987s ago; threshold is 3m0s

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

  • חשוב לעיין בהשוואות בין סוגי דיסקים לאחסון ולבחור את סוג ה-Persistent Disk שמתאים לצרכים שלכם.
  • הבעיה הזו מתרחשת לעיתים קרובות בצמתים שמשתמשים בדיסקים מתמידים סטנדרטיים בגודל של פחות מ-200GB. כדאי להגדיל את הדיסקים או לעבור ל-SSD, במיוחד באשכולות שמשמשים לייצור.
  • כדאי לשקול להפעיל SSD מקומי לאחסון זמני במאגרי הצמתים. השיטה הזו יעילה במיוחד אם יש לכם קונטיינרים שמשתמשים לעיתים קרובות בנפחי emptyDir.

ההרכבה של אמצעי אחסון מפסיקה להגיב בגלל ההגדרה fsGroup

אחת מהבעיות שיכולות לגרום לכישלון בטעינה של PersistentVolume היא Pod שמוגדר עם ההגדרה fsGroup. בדרך כלל, ניסיונות הטעינה מתבצעים מחדש באופן אוטומטי והבעיה נפתרת מעצמה. עם זאת, אם ב-PersistentVolume יש מספר גדול של קבצים, kubelet ינסה לשנות את הבעלות על כל קובץ במערכת הקבצים, מה שיכול להגדיל את זמן האחזור של טעינת אמצעי האחסון.

Unable to attach or mount volumes for pod; skipping pod ... timed out waiting for the condition

כדי לוודא אם שגיאת הטעינה שנכשלה נובעת מההגדרה fsGroup, אתם יכולים לבדוק את היומנים של ה-Pod. אם הבעיה קשורה להגדרה fsGroup, תופיע רשומת היומן הבאה:

Setting volume ownership for /var/lib/kubelet/pods/POD_UUID and fsGroup set. If the volume has a lot of files then setting volume ownership could be slow, see https://github.com/kubernetes/kubernetes/issues/69699

אם ה-PersistentVolume לא נטען תוך כמה דקות, נסו את השלבים הבאים כדי לפתור את הבעיה:

פעולות איטיות בדיסק גורמות לכשלים ביצירת Pod

מידע נוסף זמין בבעיה מספר 4604 ב-containerd.

גרסאות צומת GKE המושפעות: 1.18, 1.19, 1.20.0 עד 1.20.15-gke.2100, 1.21.0 עד 1.21.9-gke.2000, 1.21.10 עד 1.21.10-gke.100, 1.22.0 עד 1.22.6-gke.2000, 1.22.7 עד 1.22.7-gke.100, 1.23.0 עד 1.23.3-gke.700, 1.23.4 עד 1.23.4-gke.100

יכול להיות שיופיעו ביומני k8s_node container-runtime השגיאות הבאות:

Error: failed to reserve container name "container-name-abcd-ef12345678-91011_default_12131415-1234-5678-1234-12345789012_0": name "container-name-abcd-ef12345678-91011_default_12131415-1234-5678-1234-12345789012_0" is reserved for "1234567812345678123456781234567812345678123456781234567812345678"

צמצום הפגיעה

  1. אם הפודים נכשלים, כדאי להשתמש ב-restartPolicy:Always או ב-restartPolicy:OnFailure ב-PodSpec.
  2. מגדילים את מספר פעולות הקלט/פלט לשנייה (IOPS) של דיסק האתחול (לדוגמה, משדרגים את סוג הדיסק או מגדילים את נפח הדיסק).

תיקון

הבעיה תוקנה ב-containerd 1.6.0 ואילך. גרסאות GKE עם התיקון הזה הן 1.20.15-gke.2100 ואילך, 1.21.9-gke.2000 ואילך, 1.21.10-gke.100 ואילך, 1.22.6-gke.2000 ואילך, 1.22.7-gke.100 ואילך, 1.23.3-gke.1700 ואילך ו-1.23.4-gke.100 ואילך.

שינויים בהרחבת הנפח לא משתקפים במערכת הקבצים של הקונטיינר

כשמבצעים הרחבת נפח, חשוב תמיד לעדכן את PersistentVolumeClaim. שינוי של PersistentVolume באופן ישיר עלול לגרום לכך שהנפח לא יורחב. זה יכול להוביל לאחד מהתרחישים הבאים:

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

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

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

  1. משאירים את האובייקט PersistentVolume ששונה כמו שהוא.
  2. עורכים את האובייקט PersistentVolumeClaim ומגדירים את spec.resources.requests.storage לערך גבוה יותר מזה ששימש ב-PersistentVolume.
  3. מוודאים שהגודל של PersistentVolume השתנה לערך החדש.

אחרי השינויים האלה, ה-kubelet אמור לשנות באופן אוטומטי את הגודל של PersistentVolume, ‏ PersistentVolumeClaim ומערכת הקבצים של הקונטיינר.

מוודאים שהשינויים באים לידי ביטוי ב-Pod.

kubectl exec POD_NAME  -- /bin/bash -c "df -h"

מחליפים את POD_NAME ב-Pod שמצורף ל-PersistentVolumeClaim.

לסוג המכונה שנבחר צריך להיות כונן SSD מקומי אחד או יותר

יכול להיות שתקבלו את השגיאה הבאה כשאתם יוצרים אשכול או מאגר צמתים שמשתמש ב-SSD מקומי:

The selected machine type (c3-standard-22-lssd) has a fixed number of local SSD(s): 4. The EphemeralStorageLocalSsdConfig's count field should be left unset or set to 4, but was set to 1.

בהודעת השגיאה, יכול להיות שיופיע LocalNvmeSsdBlockConfig במקום EphemeralStorageLocalSsdConfig, בהתאם למה שציינתם.

השגיאה הזו מתרחשת כשמספר כונני ה-SSD המקומיים שצוינו לא תואם למספר כונני ה-SSD המקומיים שכלולים בסוג המכונה.

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

מאגרי אחסון ב-Hyperdisk: יצירת אשכול או מאגר של צמתים נכשלה

יכול להיות שתקבלו את השגיאה ZONE_RESOURCE_POOL_EXHAUSTED או שגיאות דומות במשאבי Compute Engine כשאתם מנסים להקצות דיסקים מסוג Hyperdisk Balanced כדיסקי הפעלה או דיסקים מצורפים של הצומת ב-Hyperdisk Storage Pool.

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

  • יכול להיות שבאזור אין מספיק דיסקים זמינים מסוג Hyperdisk Balanced.
  • יכול להיות שבאזור אין מספיק קיבולת כדי ליצור את הצמתים של סוג המכונה שציינתם, כמו c3-standard-4.

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

  1. בוחרים אזור חדש באותו מחוז, שיש בו מספיק קיבולת לסוג המכונה שבחרתם ושמאגרי Hyperdisk Balanced זמינים בו.
  2. מוחקים את מאגר האחסון הקיים ויוצרים אותו מחדש באזור החדש. הסיבה לכך היא ש-Storage Pool הם משאבים של תחום מוגדר.
  3. יוצרים את האשכול או את מאגר הצמתים באזור החדש.

זוהה לחץ גבוה על האחסון בצומת

אם אתם רואים אירועים או תנאים של צומת שקשורים ל-StoragePressureRootFileSystem עם הסיבה StoragePressureDetected, זה אומר שמערכת הקבצים ברמה הבסיסית (root) של הצומת או נקודת טעינה קריטית של אחסון נמצאות בשימוש גבוה בדיסק, קרוב לקיבולת שלהן.

כשמתארים צומת באמצעות הפקודה kubectl describe node NODE_NAME, יכול להיות שיופיע אירוע דומה לזה:

Events:
  Type     Reason                      Age   From                     Message
  ----     ------                      ----  ----                     -------
  ...
  Warning  StoragePressureDetected     46m   device-capacity-monitor  Node condition StoragePressureRootFileSystem is now: True, reason: StoragePressureDetected, message: "Disk /dev/nvme0n1 usage 89% exceeds threshold 85%"

הסיבה:

הסיבה StoragePressureDetected מציינת שהשימוש בדיסק במערכת הקבצים ברמה הבסיסית (root) של הצומת (לרוב mnt/stateful_partition או נקודות טעינה קשורות) חרג מסף שהוגדר מראש (לדוגמה, 85%). הסיבות האפשריות לכך:

  • עומסי עבודה שכותבים נתונים רבים מדי לנפחי emptyDir שלא מגובים על ידי כונני SSD מקומיים.
  • קובצי אימג' גדולים של קונטיינרים נשלפים לצומת.
  • קובצי יומן מצטברים בצומת.
  • תהליכים אחרים שצורכים מקום בכונן.

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

ניפוי באגים ופתרון בעיות:

זיהוי השימוש בדיסק: משתמשים ב-SSH כדי להתחבר לצומת המושפע, ומשתמשים בפקודות כמו df -h כדי לבדוק את השימוש בדיסק בנקודות טעינה שונות, תוך שימת לב ל-/mnt/stateful_partition ולכל נקודות טעינה של אחסון זמני.

ניתוח דפוסי האחסון של עומסי העבודה: בודקים את בקשות האחסון ואת דפוסי השימוש של ה-Pods שפועלים בצומת. לבדוק אם עומסי עבודה ספציפיים צורכים כמות לא פרופורציונלית של אחסון זמני.

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

  • שימוש בדיסקי אתחול גדולים יותר: כשיוצרים מאגרי צמתים, בוחרים דיסק אתחול גדול יותר אם עומסי העבודה דורשים יותר נפח אחסון זמני במערכת הקבצים הבסיסית.
  • שימוש בכונני SSD מקומיים גדולים יותר לאחסון זמני: לעומסי עבודה שדורשים אחסון זמני עם ביצועים גבוהים וזמן אחזור קצר, מגדירים את מאגרי הצמתים כך שישתמשו בכונני SSD מקומיים. כך אפשר להגדיר קיבולת נפרדת וגדולה יותר לנפחי emptyDir.
  • התאמת הבקשות או המגבלות של עומסי העבודה: ודאו שהמפרטים של ה-Pod כוללים בקשות ומגבלות מתאימות לאחסון זמני, כדי לעזור למתזמן למקם את ה-Pods בצמתים עם מספיק מקום ולמנוע שימוש מוגזם בדיסק.
  • לנקות משאבים שלא בשימוש: אם קבצים מיותרים, קובצי אימג' של קונטיינרים ישנים או יומנים תורמים לשימוש הגבוה בדיסק, צריך להסיר אותם מהצומת.

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

פתרון בעיות שקשורות לאירועי מחסור בזיכרון (OOM) ב-Cloud Storage FUSE

אם בפודים שלכם יש ניצול זיכרון גבוה או אירועי חוסר בזיכרון (OOM) שקשורים למנהל התקן ה-CSI של Cloud Storage FUSE, אתם יכולים לאסוף ולנתח תמונות מצב של המעבד (CPU) והזיכרון באמצעות Cloud Profiler. אתם יכולים להגדיר את האפשרויות האלה בהגדרת קונטיינר משני (sidecar) של מנהל התקן ה-CSI של Cloud Storage FUSE.

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

  1. בדיקה ב-Cloud Logging: עוברים אל Cloud Logging כדי למצוא את אירוע ה-OOM של Kubernetes.
  2. סינון לפי שם Pod: מריצים את השאילתה הבאה כדי לאתר את יומני האירועים של Pod ספציפי, ומחליפים את POD_NAME בשם ה-Pod של עומס העבודה:

    jsonPayload.involvedObject.name="POD_NAME"
    jsonPayload.involvedObject.kind="Pod"
    OOMKilled
    
  3. חילוץ מזהה ה-Pod: מרחיבים את יומן האירועים הרלוונטי של ה-OOM ורושמים את חותמת הזמן ואת מזהה ה-Pod המדויק שנמצא ב-jsonPayload.involvedObject.uid.

  4. ניתוח ב-Cloud Profiler: עוברים אל Cloud Profiler, מסננים את Service Version לפי הפורמט POD_NAME_POD_UID ומשנים את טווח התאריכים כך שיתאים לחותמת הזמן של ה-OOM. כך תוכלו לראות את פרופיל הזיכרון המדויק של המכונה הספציפית של מאגר הנתונים, רגע לפני שהיא נסגרה.

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

אם יש לכם עומסי עבודה עם שמירת מצב (stateful) שנתקעו במצב Pending עם הודעות שגיאה כמו ZONE_RESOURCE_POOL_EXHAUSTED או GCE_STOCKOUT, יכול להיות שבאזור מסוים יש קיבולת מחשוב מוגבלת.

אם צומת מחשוב עובר למצב אופליין באזור שאין בו מספיק קיבולת, יכול להיות שהחלפת הצומת תיכשל. פודים עם שמירת מצב (stateful) שמסתמכים על PersistentVolumes קיימים באזור (כמו דיסק pd-ssd או pd-balanced רגיל) מוגבלים לאזור שבו נוצר אמצעי האחסון. מכיוון שצריך לתזמן את ה-Pod באזור הספציפי הזה כדי לטעון את אמצעי האחסון, ה-Pod נשאר במצב Pending עד שקיבולת המחשוב באזור הזה תהיה זמינה.

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

  • הימנעו משימוש ב-fallback באותו אזור בין pd-ssd ל-pd-balanced: מכיוון שנפחי האחסון של pd-ssd ו-pd-balanced חולקים את אותן מגבלות קיבולת ומאגרי זמינות באזור, שינוי של StorageClass מ-pd-ssd ל-pd-balanced באותו אזור לא יפתור את המחסור בקיבולת. אל תנסו את זה כחלופה.
  • שימוש בתצורות שתואמות ל-Hyperdisk: חלק מסדרות המכונות תומכות ב-Hyperdisks. כדאי להשתמש בתבנית שתואמת ל-Hyperdisk, כי יכול להיות שיש לה אילוצי זמינות שונים שעוזרים כשל-zone אין מספיק קיבולת. לפני שמעבירים עומסי עבודה, חשוב לוודא שיש לכם מספיק מכסות דיסק.
  • הפעלת קישור טופולוגיה דינמי (WaitForFirstConsumer): כשיוצרים עומסי עבודה חדשים עם שמירת מצב, מגדירים את StorageClass עם צמד הערכים בשדה volumeBindingMode: WaitForFirstConsumer. ההגדרה הזו דוחה את יצירת אמצעי האחסון עד לתזמון של Pod לצומת תקין באזור עם קיבולת מספקת של משאבי מחשוב. כך אפשר למנוע יצירה מוקדמת של אמצעי אחסון, שעלולה לנעול את ה-Pod באזור לא מקוון או באזור שבו נגמרו המשאבים.
  • הגדרת בחירה אוטומטית של סוג הדיסק למכונות וירטואליות חלופיות: בגרסה 1.35.3-gke.1290000 של GKE ואילך, אתם יכולים להגדיר ComputeClasses בהתאמה אישית לעומסי עבודה שמועברים בין דורות של מכונות וירטואליות (לדוגמה, מ-N4 ל-N2 או מ-C4 ל-C2). באמצעות בחירה אוטומטית של סוג הדיסק, אתם יכולים להגדיר StorageClass עם צמדי הערכים בשדות parameters.type: dynamic ו-use-allowed-disk-topology: "true". ההגדרה הזו מאפשרת ל-GKE להקצות באופן אוטומטי דיסקים מסוג hyperdisk-balanced בדורות חדשים יותר של מכונות, ולחזור לדיסקים מסוג pd-balanced בדורות ישנים יותר.

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