NUMA awareness: מדוע השרת מאבד ביצועים
שרת רב-מעבדים מודרני הוא לא מכונה גדולה אחת, אלא כמה צמתי חישוב, שלכל אחד מהם זיכרון מקומי משל המעבד. כל עוד היישום פועל בגבולות צומת אחד, הגישה לזיכרון הולכת במסלול קצר, וההשהיה צפויה.
ברגע שתהליכון מתחיל לקרוא נתונים מצומת זר, הבקשה יוצאת דרך אפיק בין-מעבדי: ההשהיה גדלה פי כמה, ורוחב הפס מתחלק בין כל מי שפנה לאותו מקום.
הערמומיות היא שמערכת ההפעלה כברירת מחדל מקצה זיכרון "לפי צדק": הצומת של הדף נקבע לפי הפנייה הראשונה, והמתזמן רשאי להעביר את התהליך לליבה אחרת. כך נוצרת דעיכה תחת עומס שכמעט לא רואים בעמדת בדיקה — בעשרה חיבורים הכול חלק, ובשיא זמן התגובה עולה במדרגות.
מחפשים את הסיבה בקוד היישום, בדיסק או ברשת, בעוד האשם הוא טופולוגיית הזיכרון שאיש לא סיכם עליה מראש.
NUMA awareness הוא כיוונון מתואם של המתזמן, אמצעי הווירטואליזציה והיישום עצמו, שבו החישובים נשארים לצד הנתונים שלהם. התהליכונים מוצמדים לצמתים, הזיכרון מוקצה בצומת הנכון, והתעבורה הבין-מעבדית הופכת לחריג מודע ולא לרקע קבוע.
חלק נפרד של העבודה הוא הגירת דפים: יישור אוטומטי לפעמים עוזר, ולפעמים מרחיב עד אין קץ את אותם דפים בין הצמתים וצורך בדיוק את המשאב שביקשו לחסוך.
בהמשך נסביר כיצד אנו משיגים את הטופולוגיה האמיתית של שרת מסוים ומה משנים בתצורה כדי שהתגובה תפסיק להיות תלויה בצומת שאליו נפל התהליכון.
האפקט של כיוונון מיקום הזיכרון בשרת רב-מעבדים
האפקט של הכיוונון נראה לא לפי זמן תגובה ממוצע, אלא לפי הפיזור שלו ולפי התנהגות השירות בשיא. כל עוד התהליכים ודפי הזיכרון שלהם מפוזרים על כל הצמתים, חלק מהבקשות יוצאות לבקר הזיכרון המרוחק — הממוצע נשאר סביר, ולכן את הבעיה לא מבחינים בה זמן רב, ואילו הזנב של ההתפלגות חושף אותה מיד. להלן שלושה חתכים שלפיהם אנו מודדים את התוצאה.
פיזור זמן התגובה
כל עוד היישום מקבל זיכרון מכל צומת, השהיית הבקשה תלויה היכן המתזמן מיקם אותה — ומכאן הזנב הקרוע של ההתפלגות ואחוזון 95 שגבוה פי כמה מהחציון.
הצמדת תהליכים לצמתים ומדיניות הקצאת דפים מקומיים מהדקות את הפיזור: בקשות שקודם טופלו פי 3–5 לאט מהאחרות נכנסות למסדרון צר, ומדידות סמוכות מפסיקות לחרוג זו מזו.
יציבות ואחידות התהליכונים תחת עומס
התהליכונים מתפרסים על הליבות בתוך הצומת, וכל אחד עובד עם הזיכרון המקומי שלו — התחרות על האפיק הבין-שקעי מפסיקה להיות צוואר הבקבוק. רוחב הפס גדל באופן לינארי יותר: כשמכפילים את מספר המשימות במקביל, זמן התגובה גדל בצורה מתונה בהרבה, ולא פי כמה. עבור בעל השירות זה נראה כמו "השירות הפסיק לצנוח בשעות שיא", אף שהקוד של היישום לא השתנה.
סוג התקלות שהוסר
האפקט היקר ביותר — נעלם סוג התקלות שבו התהליך לא מקבל זיכרון מקומי: הצומת מבזבז משאב על דחיקת דפים, והבקשות ממתינות לא לחישובים אלא לגישה לזיכרון.
חלקם של כשלים אלו יורד לאפס, ואת הטופולוגיה והמדיניות אנחנו מקבעים בתצורה — המלצות היצרן של הפלטפורמה (Intel) למיקום זיכרון לפי צמתים מועברות לתבניות, כדי שכושר החיזוי של התגובה יישמר בעת גידול בעומס ובהוספת שירותים חדשים.
גישות ל-NUMA awareness: השוואת מצבים ומגבלות
בשרת רב-מעבדים, הגישה לזיכרון של השקע שלי זולה יותר מאשר לזיכרון של השכן, וההבדל מתגלה בהשהיות הזנב, ולא בתגובה הממוצעת. מי שמחלק את הדפים בין הצמתים — הליבה עם הקצאה אוטומטית או אתם ידנית — נקבע לפי המדיניות. קיימות ארבע אפשרויות ממשיות, מכישלון מוחלט לכייל ועד הצמדה קשיחה.
אנו משווים לפי הקריטריונים שבאמת מכריעים: כושר חיזוי התגובה, עמידות לגידול במספר התהליכונים, עלות התחזוקה והתנהגות בעת החלפת ציוד.
| אפשרות | מתי מתאימה | מגבלות |
|---|---|---|
| ללא כיוונון, הקצאה אוטומטית | עמדות בדיקה ושירותים ללא דרישות לזנב השהיות | המאזן מעביר דפים תחת עומס; התגובה נעה חזק יותר ככל שיש יותר תהליכונים |
| העדפה רכה של צומת | ערכת עבודה גדולה ומשותפת, תהליכונים מפוזרים על כל השקעים | מקומיות לא מובטחת: חלק מהפניות בכל זאת יוצאות לצומת מרוחק |
| הצמדת תהליכים לצמתים | מסדי נתונים, ברוקרים, מטמונים עם תגובה אחידה | נדרשות מדידות טופולוגיה ותחזוקה; בעת חוסר איזון בתהליכונים חלק מהליבות עומדות ללא שימוש |
| הצמדה קשיחה של תהליך וזיכרון | שרת ייעודי לשירות אחד הרגיש להשהיות | אין דרך חלופית: הצומת מלא — ההשמה נדחית, העברה לחומרה אחרת שוברת את התצורה |
סדר העבודה — ממצב חלש לקפדני: תחילה מדיניות רכה ומדידות, אחר כך הצמדת תהליכים, ורק לבסוף הצמדה קשיחה. אם מתחילים מהאחרונה, מתקבלת הבעיה ההפוכה: הצמתים אינם עמוסים מספיק, זיכרון פנוי קיים, אבל אינו זמין.
סדר ההטמעה: מתרשים הטופולוגיה ועד מסירת נוהל
ההטמעה מתבצעת בשלבים: כל שינוי מאושר במדידה ונשאר הפיך. מתחילים בסקר טופולוגיה — בלי מפה של צמתים, ליבות והצמדת התקני קלט-פלט כל תיקון הופך לניחוש. בהמשך מעבירים את המסקנות לתכנית המיקום, מכיילים, בודקים תחת עומס ומוסרים נוהל תפעול.
-
סקר טופולוגיה. אוספים מפה של צמתים וליבות, את הקשרים ביניהם, את הצמדת כרטיסי הרשת ובקרי הדיסקים. מתעדים מדידות בסיס: השהיית גישה לזיכרון מקומי ומרוחק, פיזור התהליכונים לפי צמתים.
-
תכנית מיקום. קובעים אילו שירותים מצמידים לצמתים באופן קשיח, אילו מקבלים העדפה בלבד, ואילו נשארים חופשיים. בנפרד מפרטים היכן נדרשת הפרדה — אחרת הצמדה רכה תהפוך בשקט לקשיחה.
-
כיוונון. מעבירים את התכנית לתצורה: כללים להצמדת תהליכים ולטיפול בפסיקות, הקצאת זיכרון, פרמטרים של המתזמן. כל שינוי — בשלב נפרד עם נקודת שחזור.
-
בדיקת עומס. מריצים פרופיל הדומה לזה שבעבודה: בקשות משולבות, פרצי שיא, רקע ממושך. מסתכלים לא רק על רוחב הפס הממוצע, אלא גם על השהיות הזנב.
-
ניתוח הסטיות. משווים למדידות הבסיס ומסבירים כל פער: היכן פעלה המקומיות, היכן ההצמדה צמצמה משאבים. כללים שנויים במחלוקת מסירים, ולא משאירים "ליתר ביטחון".
-
נוהל תפעול. מוסרים את מפת הצמתים, הכללים הפעילים להצמדה, רשימה של מה שאסור לשנות בלי למדוד מחדש, וסדר הבדיקה לאחר עדכון ליבה או החלפת חומרה.
מה הלקוח מקבל לידיו: תרשימים, פרופילים, נהלים
לאחר כיוונון NUMA awareness העבודה מסתיימת לא בדוח, אלא באוסף של חפצי אמנות שהצוות שלכם ממשיך לחיות איתם — בלעדינו. הכול נאסף למאגר התצורות ולתיעוד השרת, והמבנה והשמות מסוכמים עם המהנדסים שלכם בשלב המסירה.
- מפת טופולוגיה. תרשים של מיקום צמתי הזיכרון והליבות: רואים אילו ליבות פונות לזיכרון בצומת השכן והיכן אובד רוחב הפס. לפיו מתכננים את השמת השירותים החדשים בלי מדידות חוזרות.
- פרופילי הצמדה. ערכות מוכנות למסד הנתונים, למטמון ולמאזן: אילו תהליכים מוצמדים לאילו ליבות ובאיזה מספר תהליכונים. הפרופיל מיושם כתבנית, ולא נבנה מחדש.
- מדיניות גישה. מי רשאי לשנות את ההצמדה, אילו תיקונים דורשים הרצה בעמדת הבדיקה ומה אסור לגעת בו בשרת הייצור. סוגר תקלות מהסוג של "תיקנו ליבות בידיים".
- תיעוד השרת. דרכון הצומת: מצב לפני ואחרי הכיוונון, מדידות עוגן לזיכרון ולהשהיות, הקשר בין התצורה להרכב החומרה ולמצבי המערכת.
- נוהל תחזוקה. סדר העבודות המתוכננות: כיצד לשמור ולשחזר פרופילים, מה לבדוק לאחר החלפת חומרה, סדר קבלת שרת חדש לאותו פרופיל.
- סקריפטים לבדיקה. בדיקה אוטומטית שההצמדה יושמה ומחזיקה מעמד לאחר הפעלה מחדש של השירותים, עם דוח של עמוד אחד למהנדס התורנות.
מקרה בוחן: כיצד הסרנו דעיכת תגובה בשרתים עם שני מעבדים
הלקוח — שירות שבו בכל שרת עומדים שני מעבדים, והמסד פועל בניהול PostgreSQL. בעומס שיא בקשות בודדות החלו להגיב באיטיות של פי כמה מהאחרות: התהליכים פנו לנתונים בזיכרון של המעבד השכן, והתור לטיפול גדל מהר יותר מהעומס עצמו.
מה הראו המדידות
חלקן של הפניות לזיכרון מרוחק הגיע ל-34%, וההשהיות התרכזו בדיוק בבקשות האלה: פיזור זמן התגובה בין הפעולות המהירות ביותר לאיטיות ביותר נבדל בכמעט סדר גודל.
בעלייה לשיא השירות לא האט באופן אחיד, אלא החל להשליך חלק מהבקשות בשל חריגה מזמן ההמתנה — כלומר דעיכת התגובה נראתה כמו נפילה, ולא כמו ירידה הדרגתית.
מה שינו ומה קיבלו
הצמדנו את תהליכי המסד ואת מערך החיבורים לצמתי הזיכרון המקומיים, הגדרנו מדיניות מיקום דפים והשבתנו את ההגירה האוטומטית שבגללה דפים עברו בין המעבדים ברגע הכי פחות מתאים. בנוסף הפרדנו לצמתים שונים שירותים שהתחרו על אותו זיכרון.
חלקן של הפניות המרוחקות ירד ל-3-4%, פיזור זמן התגובה הצטמצם בכשש פעמים, ורוחב הפס בשיא גדל כמעט פי שניים — בלי החלפת ציוד. דעיכת התגובה בשיא, שלמענה הושק הפרויקט, אינה משוחזרת עוד לא בעמדת הבדיקה ולא בסביבת העבודה.
מה לעשות אם השרת כבר מכויל ידנית ובלי תיעוד?
תצורה מורשת היא כמעט תמיד רב-שכבתית: חלק מההצמדות מוגדרות בפרמטרי אתחול הליבה, חלק ביחידות systemd, וחלק בפקודות שמישהו פעם הריץ ידנית ולא תיעד.
הצעד הראשון — לא לשנות דבר, אלא לתעד את תכנית המיקום הנוכחית כפי שהיא: אילו תהליכים מוצמדים לצמתים, כיצד מפוזרות הפסיקות ומה הליבה באמת רואה, ולא מה שרשום בהערות ישנות.
החתך נעשה לפני כל תיקון ונשמר כאמת מידה להשוואה: מידע על הטופולוגיה והמשקלים הנוכחיים של הצמתים, רשימת הצמדות לפי תהליכים, הגדרות קבוצות מעבדים, פרמטרי אתחול הליבה. לצידם — מדדי מקומיות: חלקן של פניות לצומת ה"זר" וזמן הביצוע של משימות שאינן נכנסות לזיכרון המקומי. בלי אמת מידה זו אי אפשר אחר כך להוכיח שהשתפר, ולא סתם השתנה.
בנפרד מפרטים מה בדיוק פיצה הכיוונון הידני. פעמים רבות הוא ריפא את הסימפטום — חוסר איזון של פסיקות על צומת אחד או בחירה לא מוצלחת של המתזמן — ובביטול ההצמדות הפגם חוזר.
לכן הבדיקה לפני השינויים כוללת השוואה בין המתוכנן לבין הפועל: ייתכן שכלל אינו מיושם כלל אם התהליך מופעל מחדש מיחידה אחרת או אם הקונטיינר מתעלם מהמסכה המורשת.
התאימות לאחור נשמרת בזכות סדר העבודה: משנים שכבה אחת בכל פעם, מתחילים בפחות קשורה לעומס, ושומרים שחזור מוכן לחתך שנמדד. ראשון הוא צומת הפיילוט, שבו משווים את אותם מדדי מקומיות והשהיה תחת עומס בקרה. רק לאחר תוצאה יציבה מועבר הכלל לשאר המכונות.
בסופו של דבר התצורה הופכת מאוסף ניחושים ידניים למתועדת ומשוחזרת: תכנית המיקום מתועדת, כל שינוי עובר השוואה לאמת המידה, והחזרה למצב הקודם אפשרית בכל רגע.
ספקות טכניים: תאימות, סיכונים, מגבלות
הצמדת תהליכים לצמתי זיכרון מקומיים עובדת גם בשרת פיזי וגם בתוך מכונה וירטואלית: ההיפרביזור מפרסם לאורח את הטופולוגיה, וליבת האורח מעבירה אותה הלאה — למנהלי המשאבים ולקונטיינרים.
גבול התחולה פשוט: אם VMware או היפרביזור אחר מסתיר את הטופולוגיה או בעצמו מפזר את העומס על כל הליבות, הרווח בתוך האורח יהיה קטן בהרבה מאשר על "חומרה חשופה".
לכן אנחנו מתחילים בבדיקה: האם המערכת רואה כמה צמתים והאם המספור שלהם תואם את השקעים הממשיים — בעת אי-התאמה כל הצמדה מובילה תהליכים למקום שלא תוכנן.
ההשפעה על שירותים פעילים תלויה באופן הכיוונון. שינוי מדיניות הקצאת הזיכרון ברמת הליבה חל על תהליכים חדשים ואינו נוגע בקיימים, ואילו הצמדה דרך מנהל המשאבים דורשת הפעלה מחדש מדוקדקת — לשם כך מקצים חלון ומריצים פיילוט על צומת אחד באשכול, בהשוואת השהיות לפני ואחרי.
כל שינוי מבוצע באופן הפיך: הפרופיל מושבת בתיקון אחד של התצורה, והשירות חוזר למצבו הקודם בלי לבנות את הסביבה מחדש.
האם זה תואם וירטואליזציה וקונטיינרים?
כן, בתנאי שהטופולוגיה אינה מוסתרת. בקונטיינרים ההצמדה מוגדרת ברמת מנהל המשאבים ומורשת על ידי התהליכים; במכונות וירטואליות — רק אם האורח רואה צמתים נפרדים. אם הטופולוגיה מוסתרת, השיטה אינה ישימה ואנו אומרים זאת בפה מלא, ולא מעמידים פני מכיילים.
האם יהיה צורך לעצור שירותים פעילים?
לא תמיד. שינויים במדיניות ברמת הליבה נקלטים על ידי תהליכים חדשים, והשירות ממשיך לפעול תוך מעבר הדרגתי לתכנית החדשה. הפעלה מחדש נדרשת היכן שניהול הזיכרון עובר דרך מנהל המשאבים — עבודות כאלה מתכננים בחלון תחזוקה.
כיצד מבטלים שינויים אם יהיה גרוע יותר?
התצורה נשמרת במערכת ניהול גרסאות, והשחזור הוא החזרת הפרופיל הקודם והפעלה מחדש של השירותים שנפגעו. לפני העלאה לכל האשכול אנחנו משאירים מדידות עוגן, כדי להשוות את ההתנהגות לאחר השחזור ולוודא שחזרנו בדיוק למצב ההתחלתי.
נדון במשימה: שלחו תרשים שרתים ופרופיל עומס
כדי להבין מה ייתן NUMA awareness בדיוק בשרתים שלכם, אין צורך בגישה למתחם הייצור — די בנתוני מקור על החומרה ופרופיל העומס. ככל שהתמונה מלאה יותר, כך יש פחות הנחות בחישוב והמועדים מציאותיים יותר. הנה מה שמועיל לצרף לתיאור המשימה.
- פלט
lscpuו-numactl --hardwareמכל שרת — טופולוגיית הצמתים, פריסת הזיכרון לפי ערוצים, מרחקים בין הצמתים; - תכנית מיקום השירותים: מה חי בקונטיינרים, מה במכונות וירטואליות, היכן מופעלת הגירה וכיצד מוגדרות המכסות;
- פרופיל העומס — אילו תהליכים רגישים להשהיה, כיצד העומס משתנה לפי שעות, היכן השיאים וכמה זמן הם נמשכים;
- הצמדות נוכחיות ופרמטרי ליבה: קובצי unit עם CPUAffinity, מדיניות איזון NUMA, הגדרות המתזמן;
- מדדים תחת עומס: החמצות של זיכרון מקומי, חלקן של פניות לצומת מרוחק, זמן התגובה של השירות בשיא;
- תיאור אירועים: באיזה רגע התגובה גוברת, מה כבר ניסו ומה הועיל;
- חלונות תחזוקה ודרישות לשחזור — האם אפשר לאתחל צמתים, האם קיים כונן גיבוי חי.
שלחו את מה שיש: ערכה מלאה אינה הכרחית, את החסר נפרט בנפרד ונסביר כיצד לתעד. לאחר ניתוח התרשים ופרופיל העומס אנחנו בונים מודל של פיזור התהליכים והזיכרון, מסמנים היכן ההצמדה תיתן רווח והיכן תיתקל במגבלות, וחוזרים עם הערכה ותכנית — שלבים, אפקט צפוי בהשהיות ובניצולת, מועדי ביצוע. תארו את המשימה, ונדון מהיכן להתחיל.







