הגדרת containerd תחת Kubernetes: תצורה ופתרון תקלות

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

היכולות שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

כשלים של containerd באשכול: מה נשבר ולמה

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

הסיבה כמעט תמיד בחוסר סנכרון של ההגדרות, ולא בעדכון «גולמי». ב-containerd הגדרת הדרייבר של ניהול קבוצות תהליכים חייבת להתאים למה שמשתמש בו kubelet: אם אחד עובד דרך systemd והשני דרך cgroupfs, הפודים פשוט לא עולים.

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

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

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

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

מה נותנת סביבת הרצה מנוהלת לקונטיינרים

סביבת הרצה מוגדרת היטב באה לידי ביטוי прежде всего בזמן ההפעלה של הקונטיינרים. פרופיל containerd קובע כיצד נפרסות שכבות האימג', כמה חילוצים מתבצעים במקביל וכמה זמן חי המטמון בצומת.

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

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

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

בנפרד מוסר סוג של כשלים שנראים ספונטניים: הצומת עובר לסטטוס לא זמין, הפודים עוברים, והסיבה לא ניכרת ביומן. מאחורי זה עומדות בדרך כלל מגבלות על קלט/פלט דיסק, גידול לא מנוהל של מאגר האימג'ים ודליפות של snapshots של מערכת הקבצים. מבחוץ זה נראה כנפילת צומת, בפועל — כמה שורות בתצורה, שאחרי תיקונן כשלי הצמתים מפסיקים.

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

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

השוואה בין מצבי הפעולה של containerd והמגבלות שלהם

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

אפשרות הגדרה מתי מתאימה מגבלות
פלאגין מובנה ל-Kubernetes, הרצה דרך מרחבי שמות רגילים, אחסון שכבות של אימג'ים שירותים בצפיפות גבוהה לצומת, שחשוב להם זמן ההפעלה נדרשת מערכת קבצים עם תמיכה בשכבות; בידוד רק ברמת הליבה
אותו פלאגין, אך אחסון שכבות על דיסק כרכים ברשת ומערכות קבצים ללא תמיכה בשכבות יותר מקום לאימג'ים, עליית קונטיינר ארוכה יותר
ליבה מבודדת לכל קונטיינר קוד לא מהימן, ריבוי דיירים, דרישות אבטחה מחמירות צריכת CPU וזיכרון גבוהה יותר, חלק מקריאות המערכת אינן זמינות
שכבת תאימות עם Docker רק בזמן מעבר של מערך ישן נתיב מיושן, מתווך מיותר, פערים בעת עדכון צמתים
פרופילים נפרדים של הרצה באותו צומת עומסים מעורבים: שירותים לצד בניות ובידוד תצורה מורכבת יותר, משאבים מתוכננים בנפרד לכל פרופיל

היכן ההגדרה נתקלת במגבלות

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

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

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

סדר ההטמעה: מהאודיט ועד למסירה לתפעול

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

  1. אודיט של תצורת האשכול. מתעדים את גרסת containerd, הרכב הפלאגינים, מגבלות המשאבים, פרמטרים של איסוף זבל ולוגים, הגדרות של דרייבר ה-snapshots. פערים בין צמתים נראים בדרך כלל בדיוק כאן.
  2. תרשים של התצורה הרצויה. מסכמים פרופיל אחיד לכל הצמתים ומסמנים כל חריגה מהמצב הנוכחי: לשם מה היא נדרשת ומה היא תשפיע.
  3. הגדרת סביבת ההרצה בסטנד. מעבירים את הפרופיל לסביבת הבדיקות, בודקים תאימות עם הענף הנוכחי של Kubernetes, עם הכרכים הממונטים ועם אימג'י עומסי העבודה.
  4. בדיקה תחת עומס. מריצים זרם צפוף של יצירה ומחיקה של קונטיינרים, בודקים השהיות הפעלה, צריכת זיכרון ופיקים של קלט/פלט דיסק. את הדרדור תופסים לפני הפרודקשן, ולא אחריו.
  5. פריסה לפרודקשן. מעדכנים צמתים אחד אחד עם פינוי פודים והפסקות לבדיקת מדדים; אם יש חריגה, הצומת חוזר לפרופיל הקודם.
  6. מסירה לתפעול. מעבירים לצוות התורנות תצורות בניהול גרסאות, תרשים חזרה לאחור וניתוח של כשלים אופייניים, ומקיימים תרגול על אירוע אמיתי.

תצורות, תרשימים ונהלים שנמסרים בסיום

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

  • קבצי תצורה של האשכול — הצהרות לכל סביבה בנפרד, עם ציון מפורש של מה עובר בירושה מהשכבה הבסיסית ומה נדרס במקום; תיקונים מבוצעים רק דרך מערכת ניהול הגרסאות.
  • תרשימי הקשרים בין השירותים — מה עם מה מדבר, דרך אילו נקודות כניסה עוברת התעבורה, היכן עוברים גבולות האמון ואילו צמתים הופכים לנקודת כשל יחידה.
  • כללים להגבלת הרשאות — תפקידים ותחומי גישה לשירותים ולאנשים, איסור על הרשאות מיותרות כברירת מחדל, סדר מתן גישה מוגברת זמנית ושלילתה האוטומטית.
  • תיעוד הסביבה — הרכב הצמתים, הענף העדכני של ה-runtime (כולל containerd), כתובות של מאגרי אימג'ים ומערכות לוגים, סדר חיבור צמתים חדשים.
  • נהלי עדכון — רצף הפריסה, בדיקת תאימות לפני היישום, תנאי עצירה וחזרה מדורגת למצב הקודם.
  • תרחישי אבחון לכשלים אופייניים — מה בודק התורן כשהשירות לא זמין, כשהצומת תקוע, כשחסר מקום לאימג'ים — עם פקודות ופלט מצופה.
  • רשימת בדיקות קבלה — אילו בדיקות חייבות לעבור בירוק בכל סביבה לפני המסירה, ומי מתעד את התוצאה.

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

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

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

בשעות השיא 12–18% מהצמתים היו במצב NotReady, החזרת צומת למצב עבודה ארכה עד 40 דקות, וזמינות השירותים ירדה ל-99.3%.

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

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

מדדים לפני ואחרי

מדד לפני אחרי
צמתים ב-NotReady בשיא 12–18% 0
החזרת צומת למצב עבודה עד 40 דק׳ 6 דק׳
הפעלת האפליקציה 25 שנ׳ 4 שנ׳
זמינות השירות 99.3% 99.95%

את השינויים יישמו בקבוצות של 3–5 צמתים, התצורה מנוהלת בגרסאות יחד עם המניפסטים — jeśli המדדים חורגים, הקבוצה חוזרת לפרופיל הקודם. במשך חודש של עומסי שיא לא היה אף כשל מפל אחד: צמתים יצאו משירות רק בתחזוקה מתוכננת.

במה containerd שונה מסביבות הרצה חלופיות

<cite>containerd</cite> — דמון נפרד ש-kubelet קורא לו דרך ממשק סביבת ההרצה (CRI): מוריד את האימג', מכין את מערכת הקבצים השורשית ומפעיל את התהליך באמצעות סביבה ברמה נמוכה, runc.

Docker Engine בנוי אחרת — אותו containerd בפנים, אבל מעליו נוספו בניית אימג'ים, שורת פקודה, כרכים ורשת משתמשים.

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

הקריטריון לבחירה פשוט: CRI-O — כשצריך סביבה רזה ככל האפשר, containerd — כשצריך snapshotters שונים, מרחבי שמות לדיירים וסביבות חול מבודדות.

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

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

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

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

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

האם אפשר ליישם תיקונים על אשכול חי?

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

מה עושים אם הגרסאות של הצמתים התפצלו?

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

איך לוודא שהשינויים תקינים?

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

איך מחזירים את התצורה לאחור?

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

נדבר על המשימה: אודיט תצורה ותוכנית עבודה

נדבר על המשימה: אודיט תצורה ותוכנית עבודה

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

  • קובץ התצורה הפעיל של containerd ופלט של הפרמטרים הנוכחיים. ממנו רואים מה נדרס ידנית ומה נשאר בערכי ברירת המחדל.
  • שיטת יצירת הצמתים: תבניות אימג', סקריפטים של התקנה או תצורת פרוביז'נינג. כך ברור היכן התיקונים ייקלטו והיכן יתפצלו בהרחבה הבאה.
  • תיאור הסימפטום עם ציון זמן ולוגים מלאים של הצומת בחלון הכשל — הם נותנים יותר מאשר סיפור בעל פה.
  • הרכב הסביבה: מספר הצמתים והקבוצות שלהם, האם הפרופילים של מכונות הניהול ומכונות העבודה שונים, כמה סביבות מתוחזקות.
  • מידע על הרג'יסטרי של האימג'ים: האם נעשה שימוש במטמון מקומי, כיצד מאורגנת האימות, והאם יש הגבלות על הוצאה.
  • מגבלות: חלון תחזוקה, דרישות לחזרה לאחור, מדיניות אבטחה והפרדת הרשאות.
  • מה כבר ניסיתם ובאיזו תוצאה — זה מייתר שלב של אבחון חוזר.

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