קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

27 באפריל בכנס שביתה 2019, כחלק ממדור "DevOps", ניתן הדוח "שינוי גודל אוטומטי וניהול משאבים ב-Kubernetes". זה מדבר על איך אתה יכול להשתמש ב-K8s כדי להבטיח זמינות גבוהה של היישומים שלך ולהבטיח ביצועים שיא.

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

לפי המסורת, אנו שמחים להציג סרטון הדיווח (44 דקות, הרבה יותר אינפורמטיבי מהמאמר) והסיכום הראשי בצורת טקסט. ללכת!

בואו ננתח את נושא הדוח מילה אחר מילה ונתחיל מהסוף.

קוברנט

נניח שיש לנו מכולות Docker על המארח שלנו. בשביל מה? כדי להבטיח חזרה ובידוד, אשר בתורם מאפשרים פריסה פשוטה וטובה, CI/CD. יש לנו הרבה רכבים כאלה עם מכולות.

מה Kubernetes מספקת במקרה זה?

  1. אנחנו מפסיקים לחשוב על המכונות האלה ומתחילים לעבוד עם הענן, מקבץ מכולות או תרמילים (קבוצות של מיכלים).
  2. יתר על כן, אנחנו אפילו לא חושבים על תרמילים בודדים, אלא מנהלים יותרоקבוצות גדולות יותר. כגון פרימיטיבים ברמה גבוהה הרשו לנו לומר שיש תבנית להפעלת עומס עבודה מסוים, והנה מספר המופעים הנדרש להפעלתו. אם נשנה את התבנית לאחר מכן, כל המופעים ישתנו.
  3. עם API הצהרתי במקום לבצע רצף של פקודות ספציפיות, אנו מתארים את "מבנה העולם" (ב-YAML), שנוצר על ידי Kubernetes. ושוב: כשהתיאור ישתנה, גם התצוגה בפועל שלו תשתנה.

ניהול משאבים

CPU

תן לנו להפעיל nginx, php-fpm ו-mysql על השרת. לשירותים האלה יהיו למעשה אפילו יותר תהליכים הפועלים, שכל אחד מהם דורש משאבי מחשוב:

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)
(המספרים בשקופית הם "תוכים", הצורך המופשט של כל תהליך לכוח מחשוב)

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

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

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

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

בהקשר של הדו"ח, אנחנו מעוניינים רק קבוצות, כי קבוצות בקרה הן חלק מהפונקציונליות של קונטיינרים (Docker וכו') שמיישם ניהול משאבים. תהליכים המשולבים לקבוצות, כפי שרצינו, הם קבוצות בקרה.

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

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)
(אני חוזר על כך שכל המספרים הם ביטוי מופשט לצורך במשאבים)

יחד עם זאת, למעבד עצמו יש משאב סופי מסוים (בדוגמה זה 1000), שאולי יחסר לכולם (סכום הצרכים של כל הקבוצות הוא 150+850+460=1460). מה יקרה במקרה הזה?

הקרנל מתחיל להפיץ משאבים ועושה את זה "בהגינות", נותן את אותה כמות משאבים לכל קבוצה. אבל במקרה הראשון יש יותר מהנדרש (333>150), ולכן העודף (333-150=183) נשאר במילואים, שגם הוא מתחלק שווה בשווה בין שני מכולות אחרות:

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

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

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

בואו נסתכל על המקרה של חוסר משאבים במיכל השני (php-fpm). כל משאבי המכולה מחולקים באופן שווה בין תהליכים. כתוצאה מכך, תהליך המאסטר עובד היטב, אך כל העובדים מאטים ומקבלים פחות ממחצית ממה שהם צריכים:

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

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

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

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

גישה זו נקראת מכסות קשות (קשה להגביל). בואו נזכור את זה פשוט כמו גבולות. עם זאת, אם אתה מחלק מגבלות לכל המכולות, מתעוררת בעיה: mysql נסע לאורך הכביש ובשלב מסוים הצורך שלה במעבד הסתיים, אבל כל שאר התהליכים נאלצים להמתין עד למעבד לְהִתְבַּטֵל.

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

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

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

ל-cgroup יש שתי הגדרות - בעצם אלו שני "פיתולים" פשוטים המאפשרים לך לקבוע:

  1. משקל למיכל (בקשות) הוא מניות;
  2. אחוז מזמן המעבד הכולל לעבודה על משימות מיכל (מגבלות) הוא מכסת.

איך למדוד מעבד?

ישנן דרכים שונות:

  1. מהו תוכים, אף אחד לא יודע - אתה צריך לנהל משא ומתן בכל פעם.
  2. ריבית ברור יותר, אבל יחסי: 50% משרת עם 4 ליבות ועם 20 ליבות הם דברים שונים לגמרי.
  3. אתה יכול להשתמש באלה שכבר הוזכרו משקולות, שלינוקס מכירה, אבל הם גם יחסיים.
  4. האפשרות המתאימה ביותר היא למדוד משאבי מחשוב ב שניות. הָהֵן. בשניות של זמן מעבד ביחס לשניות של זמן אמת: ניתנה שנייה אחת של זמן מעבד לכל שנייה אמיתית - זוהי ליבת מעבד אחת שלמה.

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

ניקח בחשבון דוגמה פשוטה עם שרת עם 3 ליבות CPU, שבו שלושה פודים יקבלו משקלים (500, 1000 ו-1500) המומרים בקלות לחלקים המקבילים של הליבות המוקצים להם (0,5, 1 ו-1,5).

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

אם אתה לוקח שרת שני, שבו יהיו פי שניים יותר ליבות (6), ומניחים שם את אותם תרמילים, ניתן לחשב בקלות את התפלגות הליבות על ידי הכפלה של 2 (1, 2 ו-3, בהתאמה). אבל רגע חשוב מתרחש כאשר מופיע פוד רביעי בשרת הזה, שמשקלו, מטעמי נוחות, יהיה 3000. הוא לוקח חלק ממשאבי המעבד (חצי מהליבות), ועבור הפודים הנותרים הם מחושבים מחדש (בחצי):

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

Kubernetes ומשאבי CPU

ב-Kubernetes, משאבי CPU נמדדים בדרך כלל ב מיליאדרקס, כלומר 0,001 ליבות נלקחות כמשקל הבסיס. (אותו דבר בטרמינולוגיה של Linux/cgroups נקרא שיתוף מעבד, אם כי, ליתר דיוק, 1000 מיליקורות = 1024 שיתופי מעבד.) K8s מבטיח שהוא לא מציב יותר פודים בשרת מאשר יש משאבי CPU עבור סכום המשקולות של כל הפודים.

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

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

עבור פוד אתה יכול לציין הן בקשות (מתזמן CFS) והן מגבלות (זוכרים את הרמזור?):

  • אם הם מצוינים שווים, אז לתרמיל מוקצה מחלקה QoS מובטח. מספר הליבות הזה שתמיד זמינות לו מובטח.
  • אם הבקשה קטנה מהמגבלה - מחלקה QoS מתפרץ. הָהֵן. אנו מצפים מתרמיל, למשל, ישתמש תמיד בליבה אחת, אך ערך זה אינו מגבלה עבורו: לפעמים pod יכול להשתמש ביותר (כאשר לשרת יש משאבים פנויים לכך).
  • יש גם מחלקה QoS המאמץ הטוב ביותר - הוא כולל את אותם תרמילים שעבורם הבקשה לא צוינה. משאבים ניתנים להם אחרון.

זיכרון

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

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

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

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

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

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

  • הערך הנמוך ביותר oom_score_adj עבור תרמיל - -998 - אומר שצריך להרוג תרמיל כזה אחרון, זה מובטח.
  • הגבוה ביותר - 1000 - הוא המאמץ הטוב ביותר, תרמילים כאלה נהרגים קודם.
  • כדי לחשב את הערכים הנותרים (מתפרץ) יש נוסחה, שמהותה מסתכמת בעובדה שככל שתרמיל ביקש יותר משאבים, כך יש סיכוי נמוך יותר שהוא ייהרג.

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

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

בסך הכל

כל פוד ב- Kubernetes נתון requests и limits - שני הפרמטרים למעבד וזיכרון:

  1. בהתבסס על בקשות, מתזמן Kubernetes עובד, שמפיץ פודים בין שרתים;
  2. בהתבסס על כל הפרמטרים, מחלקת ה-QoS של התרמיל נקבעת;
  3. משקלים יחסיים מחושבים על סמך בקשות CPU;
  4. מתזמן CFS מוגדר על סמך בקשות CPU;
  5. OOM killer מוגדר על סמך בקשות זיכרון;
  6. "רמזור" מוגדר על סמך מגבלות מעבד;
  7. בהתבסס על מגבלות זיכרון, מגבלה מוגדרת עבור cgroup.

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

באופן כללי, תמונה זו עונה על כל השאלות לגבי האופן שבו מתרחש החלק העיקרי של ניהול המשאבים ב-Kubernetes.

קנה מידה אוטומטי

K8s אשכול-autoscaler

בואו נדמיין שכל האשכול כבר תפוס ויש ליצור פוד חדש. בעוד שהתרמיל אינו יכול להופיע, הוא נתקע במצב ממתין ל. כדי שהוא יופיע, נוכל לחבר שרת חדש לאשכול או... להתקין cluster-autoscaler, שיעשה זאת עבורנו: להזמין מכונה וירטואלית מספק הענן (באמצעות בקשת API) ולחבר אותה לאשכול , לאחר מכן יתווסף הפוד .

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

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

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

שקול אשכול של 3 שרתים עם Deployment. יש לו 6 פודים: עכשיו יש 2 לכל שרת. משום מה רצינו לכבות את אחד השרתים. לשם כך נשתמש בפקודה kubectl drain, איזה:

  • יאסור לשלוח פודים חדשים לשרת זה;
  • תמחק פודים קיימים בשרת.

מכיוון ש-Kubernetes אחראית על שמירה על מספר התרמילים (6), זה פשוט ישחזר אותם בצמתים אחרים, אך לא על זה המושבת, מכיוון שהוא כבר מסומן כלא זמין לאירוח פודים חדשים. זהו מכונאי בסיסי עבור Kubernetes.

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

עם זאת, גם כאן יש ניואנס. במצב דומה, עבור StatefulSet (במקום Deployment), הפעולות יהיו שונות. עכשיו יש לנו כבר אפליקציה stateful - למשל שלושה פודים עם MongoDB, באחד מהם יש איזושהי בעיה (הנתונים נפגמו או שגיאה אחרת שמונעת מהפוד להתחיל נכון). ושוב אנחנו מחליטים להשבית שרת אחד. מה יקרה?

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

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

בשורה התחתונה: על מנת שהתנועה (ולמעשה, היצירה מחדש) של פודים תפעל בצורה נכונה עם שחרור האשכול, עליך להגדיר את PodDisruptionBudget.

קנה מידה אופקי

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

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

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

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

קנה מידה אוטומטי וניהול משאבים ב-Kubernetes (סקירה כללית ודוח וידאו)

איך עושים זאת מבחינה טכנית - אוספים מדדים וכו'. - דיברתי בפירוט בדוח על ניטור ו-Kubernetes. והעצה העיקרית לבחירת הפרמטרים האופטימליים היא לְנַסוֹת!

יש שיטת USE (רווית שימוש ושגיאות), שמשמעותו היא כדלקמן. על סמך מה זה הגיוני לבצע קנה מידה, למשל, php-fpm? בהתבסס על העובדה שהעובדים אוזלים, זהו ניצול. ואם נגמרו העובדים ולא יתקבלו קשרים חדשים, זה כבר רווי. יש למדוד את שני הפרמטרים הללו, ובהתאם לערכים יש לבצע קנה מידה.

במקום מסקנה

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

סרטונים ושקופיות

סרטון מההופעה (44 דקות):

הפעל וידאו

הצגת הדו"ח:

נ.ב.

דיווחים נוספים על Kubernetes בבלוג שלנו:

מקור: www.habr.com

הוספת תגובה