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

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

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

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

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

מוגבלת היכולת להשתמש בדיסקים לאחסון מתמיד אזורי עם מכונות מותאמות לצריכת זיכרון גבוהה (memory-optimized) או מכונות מותאמות לצריכת מעבד גבוהה (compute-optimized).

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

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

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

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

ביצועי הדיסק משותפים לכל הדיסקים מאותו סוג דיסק בצומת. לדוגמה, אם יש לכם דיסק אתחול של pd-standard בנפח 100GB ו-PersistentVolume של pd-standard בנפח 100GB עם הרבה פעילות, הביצועים של דיסק האתחול יהיו כמו של דיסק בנפח 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 שמתאים לצרכים שלכם.
  • הבעיה הזו מתרחשת לעיתים קרובות בצמתים שמשתמשים בדיסקים מתמידים סטנדרטיים עם גודל של פחות מ-200 GB. כדאי להגדיל את גודל הדיסקים או לעבור ל-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. אם יש כשלים ב-Pods, כדאי להשתמש ב-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 של ה-SSD המקומי, והערך הנכון יוגדר אוטומטית.

Hyperdisk Storage Pools: Cluster or node pool creation fails

יכול להיות שתקבלו את השגיאה 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) ב-Pods שקשורים למנהל התקן ה-CSI של Cloud Storage FUSE, אפשר לאסוף ולנתח תמונות מצב של המעבד והזיכרון באמצעות Cloud Profiler. את האפשרויות האלה מגדירים בהגדרת קונטיינר משני (sidecar) של מנהל התקן ה-CSI של Cloud Storage FUSE.

כשמתרחש OOM kill ב-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. שליפת ה-UID של ה-Pod: מרחיבים את יומן האירועים הרלוונטי של OOM ורושמים את חותמת הזמן ואת ה-UID המדויק של ה-Pod שנמצא ב-jsonPayload.involvedObject.uid.

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

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