Namespaces ו-cgroups - הבסיס שמאחורי קונטיינרים בלינוקס¶
מי שהתחיל עם Docker לרוב חושב עליו כעל טכנולוגיה חדשה ומיוחדת. אבל האמת היא ש-Docker לא המציא שום דבר ברמת הקרנל - הוא פשוט עוטף בממשק נוח שני מנגנונים ותיקים יחסית שכבר קיימים בקרנל לינוקס - namespaces ו-cgroups. הבנה שלהם היא ההבדל בין להשתמש בקונטיינרים לבין להבין באמת מה קורה מתחתיהם.
הבעיה - איך מבודדים תהליכים בלי מכונה וירטואלית שלמה¶
מכונה וירטואלית מבודדת לגמרי מהמארח שלה - יש לה קרנל משלה, זיכרון משלה, כמעט הכל נפרד. זה בטוח מאוד, אבל גם כבד - כל מכונה וירטואלית צורכת משאבים משמעותיים ודורשת דקות להעלות. קונטיינר, לעומת זאת, משתף את אותו קרנל עם המערכת המארחת, ובכל זאת מרגיש לתהליך שרץ בתוכו כאילו הוא לגמרי לבד במערכת משלו. איך זה אפשרי? namespaces הם התשובה.
namespaces - לכל תהליך העולם שהוא רואה¶
namespace הוא מנגנון בקרנל שמאפשר "לחתוך" משאב מערכת מסוים, ולתת לקבוצת תהליכים תצוגה נפרדת ומבודדת שלו, שלא תלויה במה שקורה בשאר המערכת. יש כמה סוגי namespace, וכל אחד מבודד משאב אחר:
- PID namespace - מבודד את רשימת התהליכים. תהליך בתוך קונטיינר יכול לראות את עצמו כ-PID 1, בדיוק כמו תהליך init- על מערכת שלמה, בלי לראות בכלל את שאר התהליכים על המארח.
- Network namespace - מבודד ממשקי רשת, כתובות IP, וטבלת ניתוב. לכל קונטיינר יכולה להיות "כרטיס רשת" משלו, נפרד לגמרי מהמארח ומקונטיינרים אחרים.
- Mount namespace - מבודד את עץ הקבצים שהתהליך רואה, כך שקונטיינר יכול לראות מערכת קבצים משלו, שונה לגמרי ממה שהמארח רואה.
- UTS namespace - מבודד את שם המארח, כך שלקונטיינר יכול להיות hostname משלו.
השילוב של כל ה-namespaces האלה יחד הוא מה שנותן לתהליך בתוך קונטיינר את התחושה שהוא רץ על מערכת שלמה משלו, למרות שהוא בעצם רק תהליך רגיל שרץ על אותו קרנל בדיוק כמו כל תהליך אחר במערכת.
cgroups - מגבילים כמה כל תהליך יכול לצרוך¶
בעוד ש-namespaces פותרים בעיה של בידוד תצוגה, cgroups, ראשי תיבות של control groups, פותרים בעיה שונה - הגבלת משאבים. בלי cgroups, קונטיינר בודד יכול תיאורטית לצרוך את כל הזיכרון או כל כוח העיבוד של המחשב, ולהרעיב את כל שאר הקונטיינרים והתהליכים שרצים לצדו.
cgroups מאפשר לקבץ תהליכים יחד, ולהגדיר להם מגבלות ברורות - כמה זיכרון מקסימלי מותר להם להשתמש, כמה אחוז ממשאבי המעבד מוקצה להם, וכמה תעבורת דיסק מותרת. כשתהליך בתוך קבוצה כזו מנסה לחרוג מהמגבלה, הקרנל עוצר אותו או מגביל אותו, בהתאם להגדרה.
Docker בעצם רק עוטף את שניהם¶
כשאתם מריצים קונטיינר עם Docker, מה שקורה מאחורי הקלעים הוא בדיוק הצירוף הזה - Docker יוצר עבור התהליך שלכם namespaces חדשים כדי לבודד אותו, ומגדיר עבורו cgroup כדי להגביל את המשאבים שהוא יכול לצרוך. כל הפקודות הידידותיות של Docker, כמו docker run- והדגלים שלה שקובעים כמה זיכרון להקצות, הן בסך הכל ממשק נוח מעל השניים האלה.
זו הסיבה שקונטיינר עולה תוך שברירי שנייה, בהשוואה למכונה וירטואלית - אין קרנל חדש שצריך לעלות, יש רק תהליך רגיל שמקבל תצוגה מבודדת ומגבלת משאבים.
לסיכום¶
namespaces ו-cgroups הם שני מנגנוני ליבה נפרדים בלינוקס, אחד שאחראי על בידוד ואחד שאחראי על הגבלת משאבים, וביחד הם מה שהופך קונטיינרים לאפשריים בלי הכבדות של מכונה וירטואלית שלמה. הבנה שלהם עוזרת להבין לא רק איך Docker עובד, אלא גם למה בידוד קונטיינרים, בניגוד למכונה וירטואלית, לעולם לא מושלם לגמרי - כי בסופו של דבר, כולם עדיין חולקים את אותו קרנל.
רוצים להעמיק עוד בתהליכים ובמנגנוני הקרנל של לינוקס? יש לנו קורס לינוקס בסיסי מלא בחינם שבונה את הידע הזה שלב אחרי שלב.
יש לכם שאלה על קונטיינרים או מנגנוני קרנל בלינוקס? בואו לדבר בדיסקורד.