אם אתם נתקלים בבעיות של סגירת קונטיינרים לא צפויה, חוסר יציבות או שגיאות CrashLoopBackOff באפליקציות של Google Kubernetes Engine (GKE), יכול להיות שהסיבה לכך היא אירועים של חוסר זיכרון (OOM). אירועי OOM מתרחשים כשאין יותר זיכרון בקונטיינר או בצומת, ולכן תהליכים מסוימים מסתיימים כדי לפנות משאבים.
בדף הזה מוסבר איך לזהות אם אירוע OOM התרחש ברמת הקונטיינר או ברמת הצומת, ואיך ליישם אסטרטגיות יעילות לצמצום הסיכון כדי למנוע הישנות של האירוע.
המידע הזה חשוב למפתחי אפליקציות שצריכים לוודא שהאפליקציות שלהם מוגדרות עם בקשות ומגבלות זיכרון מתאימות, ושהן לא מכילות דליפות זיכרון. הוא חשוב גם לאדמינים ולמפעילים של פלטפורמות שמנטרים את ניצול המשאבים של הצמתים ומוודאים שיש לאשכול קיבולת זיכרון מספקת לתמיכה בעומסי העבודה שנפרסו. מידע נוסף על התפקידים הנפוצים ודוגמאות למשימות שאנחנו מתייחסים אליהן בתוכן של Google Cloud זמין במאמר תפקידים ומשימות נפוצים של משתמשי GKE.
סיבות נפוצות לאירועי OOM
אירועי OOM מתרחשים בדרך כלל במהלך עומס או עלייה חדה בתנועה, כשהשימוש בזיכרון של האפליקציה עולה ומגיע למגבלת הזיכרון שהוגדרה לקונטיינר.
התרחישים הבאים יכולים לגרום לאירוע OOM:
- מגבלת זיכרון לא מספיקה: ההגדרה
resources.limits.memoryבמניפסט של ה-Pod נמוכה מדי ביחס לדרישות הזיכרון הטיפוסיות או המקסימליות של האפליקציה. - בקשות או מגבלות זיכרון לא מוגדרות: אם גם
resources.limits.memoryוגםresources.requests.memoryלא מוגדרים, השימוש בזיכרון של הקונטיינר לא מוגבל. - עומס גבוה או עומס עם עליות חדות: עליות חדות וקיצוניות בעומס עלולות להעמיס יתר על המשאבים של המערכת, כולל הזיכרון, גם אם המגבלות בדרך כלל מספיקות.
- דליפת זיכרון: יכול להיות שיש באפליקציה פגם בקוד שגורם לה לא לשחרר זיכרון בצורה תקינה.
אירועי OOM יכולים לגרום לכשל מדורג, כי נשארים פחות קונטיינרים לטיפול בתנועה, מה שמגדיל את העומס על הקונטיינרים שנותרו. יכול להיות שגם המאגרי התגים האלה ייסגרו.
איך Kubernetes מטפל באירועי OOM
הכלי Linux OOM Killer מטפל בכל אירוע OOM. ה-OOM Killer הוא תהליך ליבה שמופעל כשהזיכרון של המערכת כמעט מלא. המטרה שלו היא למנוע קריסה מוחלטת של המערכת על ידי סיום תהליכים באופן אסטרטגי כדי לפנות משאבים. הליבה משתמשת במערכת ניקוד כדי לבחור איזה תהליך לעצור, במטרה לשמור על יציבות המערכת ולמזער את אובדן הנתונים.
בסביבת Kubernetes, OOM Killer פועל בשני היקפים שונים: קבוצת הבקרה (cgroup), שמשפיעה על קונטיינר אחד, והמערכת, שמשפיעה על הצומת כולו.
הפסקת פעולה של מאגר בגלל חריגה מזיכרון (OOM)
פעולת OOM kill ברמת מאגר מתרחשת כשמאגר מנסה לחרוג ממגבלת הזיכרון שהוגדרה לו מראש. Kubernetes מקצה כל קונטיינר לקבוצת cgroup ספציפית עם מגבלת זיכרון קשיחה. כשהשימוש בזיכרון של קונטיינר מגיע למגבלה הזו, ליבת המערכת מנסה קודם לפנות זיכרון בתוך קבוצת הבקרה הזו. אם הליבה לא יכולה לפנות מספיק זיכרון באמצעות התהליך הזה, מופעל ה-OOM Killer של ה-cgroup. היא מסיימת תהליכים בתוך קבוצת הבקרה הספציפית הזו כדי לאכוף את מגבלת המשאבים.
כשמפסיקים את התהליך הראשי במאגר בדרך הזו, Kubernetes מזהה את האירוע ומסמן את הסטטוס של המאגר כ-OOMKilled. ההגדרה של ה-Pod restartPolicy קובעת את התוצאה:
-
AlwaysאוOnFailure: המאגר מופעל מחדש. -
Never: הקונטיינר לא מופעל מחדש ונשאר במצב שהופסק.
ה-OOM Killer מבודד את הכשל בקונטיינר הבעייתי, וכך מונע ממצב שבו Pod אחד פגום גורם לקריסה של הצומת כולו.
איך גרסת cgroup משפיעה על ההתנהגות של OOM Killer
התנהגות של OOM kill יכולה להיות שונה באופן משמעותי בין גרסאות שונות של cgroup. אם אתם לא בטוחים באיזו גרסת cgroup אתם משתמשים, כדאי לבדוק את מצב ה-cgroup של צמתי האשכול.
ב-
cgroup v1, אירוע OOM ב-cgroup של זיכרון של קונטיינר עלול לגרום להתנהגות בלתי צפויה. ה-OOM Killer עשוי להפסיק כל תהליך ב-cgroup הזה, כולל תהליכי צאצא שלא שייכים לתהליך הראשי של הקונטיינר (PID 1).התנהגות כזו יוצרת אתגר משמעותי ל-Kubernetes. מכיוון ש-Kubernetes עוקב בעיקר אחרי התקינות של תהליך הקונטיינר הראשי, הוא לא מודע לפעולות ה "חלקיות" האלה של סגירת תהליכים בגלל מחסור בזיכרון. תהליך המכולה הראשי עשוי להמשיך לפעול, גם אם תהליכים משניים קריטיים הופסקו. התנהגות כזו עלולה לגרום לכשלים עדינים באפליקציה שלא גלויים מיד ל-Kubernetes או למפעילים, אבל עדיין גלויים ביומן המערכת של הצומת (
journalctl).cgroup v2מציע התנהגות צפויה יותר של OOM Killer.כדי להבטיח את שלמות עומס העבודה בסביבת cgroup v2, תהליך OOM killer מונע השבתות חלקיות ומבטיח אחד משני תרחישים: או שכל המשימות ששייכות ל-cgroup ולצאצאים שלו מסתיימות (מה שמאפשר ל-Kubernetes לראות את הכשל), או שאם בעומס העבודה אין משימות שצורכות יותר מדי זיכרון, עומס העבודה לא מושפע והוא ממשיך לפעול בלי סיום לא צפוי של תהליכים פנימיים.
בתרחישים שבהם רוצים את ההתנהגות של
cgroup v1סיום תהליך יחיד, kubelet מספק את הדגלsingleProcessOOMKillעבורcgroup v2. הדגל הזה מאפשר לכם שליטה מפורטת יותר, ומאפשר לסיים תהליכים ספציפיים במהלך אירוע OOM, במקום את כל קבוצת הבקרה.
סגירת תהליכים ברמת המערכת בגלל מחסור בזיכרון (OOM)
מחיקה של תהליך בגלל חוסר זיכרון ברמת המערכת היא אירוע חמור יותר שמתרחש כשנגמר הזיכרון הזמין בכל הצומת, ולא רק במאגר יחיד. האירוע הזה יכול לקרות אם השימוש המשולב בזיכרון של כל התהליכים (כולל כל ה-Pods וה-daemons של המערכת) חורג מהקיבולת של הצומת.
כשאין יותר זיכרון בצומת הזה, כלי ה-OOM Killer הגלובלי מעריך את כל התהליכים בצומת ומסיים תהליך כדי לפנות זיכרון לכל המערכת. התהליך שנבחר הוא בדרך כלל תהליך קצר טווח שמשתמש בכמות גדולה של זיכרון.
כדי למנוע מצבים חמורים של OOM, Kubernetes משתמש בהוצאה של תהליכים מהזיכרון בגלל עומס על הצומת כדי לנהל את משאבי הצומת. התהליך הזה כולל הוצאה של Pods פחות קריטיים מצומת כשמקורות כמו זיכרון או נפח אחסון בדיסק מגיעים לרמה קריטית. הפסקת פעולה של תהליך בגלל חוסר זיכרון ברמת המערכת מצביעה על כך שתהליך ההוצאה מהזיכרון לא הצליח לפנות זיכרון מספיק מהר כדי למנוע את הבעיה.
אם תהליך של קונטיינר מסתיים על ידי OOM Killer, ההשפעה בדרך כלל זהה להשפעה של סיום שהופעל על ידי cgroup: הקונטיינר מסומן כ-OOMKilled ומופעל מחדש על סמך המדיניות שלו. עם זאת, אם תהליך מערכת קריטי יופסק (מה שקורה לעיתים רחוקות), הצומת עצמו עלול להיות לא יציב.
בדיקת אירועי OOM
בקטעים הבאים מוסבר איך לזהות ולאשר אירוע OOM, החל מכלי Kubernetes הפשוטים ביותר ועד לניתוח מפורט יותר של יומנים.
בודקים את סטטוס ה-Pod כדי לראות אם יש אירועי OOM גלויים
השלב הראשון באישור אירוע OOM הוא לבדוק אם Kubernetes זיהה את אירוע ה-OOM. Kubernetes מזהה את האירוע כשהתהליך הראשי של הקונטיינר נסגר, וזו התנהגות רגילה בסביבות cgroup v2.
בודקים את הסטטוס של ה-Pod:
kubectl describe pod POD_NAMEמחליפים את
POD_NAMEבשם של ה-Pod שרוצים לבדוק.אם התרחש אירוע OOM גלוי, הפלט דומה לזה:
... Last State: Terminated Reason: OOMKilled Exit Code: 137 Started: Tue, 13 May 2025 19:05:28 +0000 Finished: Tue, 13 May 2025 19:05:30 +0000 ...
אם מופיע OOMKilled בשדה Reason, סימן שאשרתם את האירוע. Exit Code מתוך 137 מציין גם סיום פעולה עקב חוסר זיכרון. אם בשדה Reason מופיע ערך שונה, או אם ה-Pod עדיין פועל למרות שגיאות באפליקציה, צריך להמשיך לקטע הבא כדי לבדוק את הבעיה לעומק.
חיפוש ביומנים של אירועי OOM בלתי נראים
פעולת OOM kill היא "בלתי נראית" ל-Kubernetes אם תהליך צאצא נסגר אבל תהליך הקונטיינר הראשי ממשיך לפעול (תרחיש נפוץ בסביבות cgroup v1). כדי למצוא הוכחות לאירועים האלה, צריך לחפש ביומנים של הצומת.
כדי למצוא מקרים של סגירת תהליכים בגלל חוסר זיכרון שלא נראים, משתמשים ב-Logs Explorer:
במסוף Google Cloud , עוברים אל Logs Explorer.
בחלונית השאילתה, מזינים אחת מהשאילתות הבאות:
אם כבר יש לכם Pod שאתם חושבים שחווה אירוע OOM, שלחו שאילתה ל-Pod הספציפי הזה:
resource.type="k8s_node" jsonPayload.MESSAGE:(("POD_NAME" AND "ContainerDied") OR "TaskOOM event") resource.labels.cluster_name="CLUSTER_NAME"מחליפים את מה שכתוב בשדות הבאים:
-
POD_NAME: שם ה-Pod שרוצים לשלוח אליו שאילתה. -
CLUSTER_NAME: שם האשכול שאליו שייך ה-Pod.
-
כדי לגלות אילו Pods או צמתים חוו אירוע OOM, שולחים שאילתה לכל עומסי העבודה ב-GKE:
resource.type="k8s_node" jsonPayload.MESSAGE:("ContainerDied" OR "TaskOOM event") resource.labels.cluster_name="CLUSTER_NAME"
לוחצים על Run query.
בפלט, מאתרים אירועי OOM על ידי חיפוש רשומות ביומן שמכילות את המחרוזת
TaskOOM.אופציונלי: אם חיפשתם אירועי OOM בכל עומסי העבודה של GKE ואתם רוצים לזהות את ה-Pod הספציפי שחווה את אירועי ה-OOM, מבצעים את השלבים הבאים:
- לכל אירוע, רושמים את מזהה מאגר התגים שמשויך אליו.
כדי לזהות עצירות של קונטיינרים, מחפשים רשומות ביומן שמכילות את המחרוזת
ContainerDiedושקרו זמן קצר אחרי אירועי ה-OOM. משווים את מזהה מאגר התגים מהאירוע OOM לשורה המתאימהContainerDied.אחרי שמתאימים את
container IDs, השורהContainerDiedבדרך כלל כוללת את שם ה-Pod שמשויך לקונטיינר שנכשל. ה-Pod הזה הושפע מאירוע ה-OOM.
שימוש ב-journalctl לקבלת מידע בזמן אמת
אם אתם צריכים לבצע ניתוח של המערכת בזמן אמת, השתמשו בפקודות journalctl.
מתחברים לצומת באמצעות SSH:
gcloud compute ssh NODE_NAME --location ZONEמחליפים את מה שכתוב בשדות הבאים:
-
NODE_NAME: שם הצומת שרוצים לבדוק. ZONE: האזור ב-Compute Engine שאליו שייך הצומת.
-
במעטפת, בודקים את הודעות הליבה מיומן המערכת של הצומת:
journalctl -kמנתחים את הפלט כדי להבחין בין סוגי האירועים:
- הגבלת משאבים ברמת הקונטיינר: רשומת היומן מכילה מונחים כמו
memory cgroup,mem_cgroupאוmemcg, שמציינים שנאכפה הגבלה של cgroup. - הפסקת פעולה ברמת המערכת: רשומת היומן היא הודעה כללית כמו
Out of memory: Killed process...בלי לציין cgroup.
- הגבלת משאבים ברמת הקונטיינר: רשומת היומן מכילה מונחים כמו
פתרון בעיות שקשורות לאירועי OOM
כדי לפתור אירוע של חוסר זיכרון, אפשר לנסות את הפתרונות הבאים:
- הגדלת מגבלות הזיכרון: זה הפתרון הכי ישיר. עורכים את קובץ המניפסט של ה-Pod כדי לספק ערך
resources.limits.memoryגבוה יותר שמתאים לשימוש השיא באפליקציה. למידע נוסף על הגדרת מגבלות, קראו את המאמר ניהול משאבים לפודים ולמאגרים במסמכי התיעוד של Kubernetes. - הוספה או התאמה של בקשות זיכרון: במניפסט של ה-Pod, מוודאים שהשדה
resources.requests.memoryמוגדר לערך ריאלי לשימוש טיפוסי. ההגדרה הזו עוזרת ל-Kubernetes לתזמן את ה-Pod לצומת עם מספיק זיכרון. - להגדיל את עומס העבודה באופן אופקי: כדי לפזר את עומס התנועה ולהפחית את העומס על הזיכרון בכל Pod, מגדילים את מספר הרפליקות. כדי ש-Kubernetes יבצע התאמה לעומס העבודה באופן יזום, כדאי להפעיל התאמה אופקית של קבוצות Pod לעומס.
- שינוי קנה המידה של הצמתים באופן אנכי: אם הרבה Pods בצומת קרובים למגבלות שלהם, יכול להיות שהצומת עצמו קטן מדי. כדי להגדיל את הגודל של הצמתים, מעבירים את עומסי העבודה למאגר צמתים עם יותר זיכרון. כדי ש-Kubernetes יתאים את קנה המידה של הצמתים באופן יזום, כדאי להפעיל התאמה אנכית של קבוצות Pod לעומס.
- אופטימיזציה של האפליקציה: בודקים את האפליקציה כדי לזהות ולפתור דליפות זיכרון, ולבצע אופטימיזציה של קוד שצורך כמויות גדולות של זיכרון במהלך עלייה חדה בתנועה.
- מחיקת עומסי עבודה בעייתיים: כמוצא אחרון לעומסי עבודה לא קריטיים, מוחקים את ה-Pod כדי להקל באופן מיידי על העומס באשכול.
המאמרים הבאים
אם לא מצאתם פתרון לבעיה שלכם במסמכים, תוכלו להיעזר בקבלת תמיכה, כולל עצות בנושאים הבאים:
- פתיחת בקשת תמיכה באמצעות פנייה אל Cloud Customer Care.
- קבלת תמיכה מהקהילה על ידי פרסום שאלות ב-StackOverflow ושימוש בתג
google-kubernetes-engineכדי לחפש בעיות דומות. אתם יכולים גם להצטרף לערוץ Slack#kubernetes-engineכדי לקבל תמיכה מהקהילה. - פתיחת דיווחים על בעיות או בקשות למאפיינים חדשים באמצעות הכלי הציבורי Issue Tracker.