תמלול הפרק (לחצו לפתיחה)
קונטיינרים - ה-Instance של ה-Image
אהלן, אני אורן מהייטקיסטים בדרכים וברוכים הבאים להרצאה השלישית על דוקר. בהרצאה הקודמת למדנו על Image, למדנו מה זה Image ולמדנו איך יוצרים אותו. היום נלמד על Container (קונטיינר), שזה למעשה ה-Instance של ה-Image, זה Image במצב ריצה. דיברנו בהרצאה הקודמת על זה ש-Image הוא בעצם Read-only, אין יכולת לכתוב אליו ואין יכולת לשנות אותו. אם כמה אנשים הורידו את אותו אימג' מ-Docker Hub או מכל רפוזיטורי אחר והם מחזיקים עכשיו את האימג' אצלם, אין להם שום יכולת לשנות את האימג', אפשר רק להשתמש בו.
אז כשאנחנו מריצים מהאימג' הזה קונטיינר בזמן ריצה, אנחנו בכל זאת רוצים לכתוב דברים ואנחנו רוצים שתהיה לנו אפשרות לשנות דברים בזמן ריצה. אז מה שקורה, מעבר לשכבות של האימג' שמגיעות איתו, נוצרת שכבה אחת אקסטרה, שכבה נוספת שהיא לא Read-only אלא היא Writable – שכבה שאפשר לכתוב אליה. בזמן ריצה, בכל פעם שאנחנו מגיעים למצב שאנחנו רוצים לשנות קובץ מסוים או משהו שנמצא בשכבות האלה של האימג', ומכיוון שאנחנו לא יכולים לשנות את המקור, ה-Docker Engine מעתיק את הקובץ מהשכבה של האימג' אל השכבה האקסטרה הזאת (השכבה שנוצרת בזמן ריצה עבור הקונטיינר), ואנחנו בעצם כותבים אל העותק של הקובץ.
ה-Docker Engine בנוי בצורה הזאת בתוך ה"ברזלים" – ברמות שלא ניכנס אליהן – הוא בנוי ככה שהוא נותן לנו את התחושה כאילו שאנחנו בעצם משנים את השכבות המקוריות, כשבעצם אנחנו כותבים לשכבה של הקונטיינר בלבד. אם אנחנו נעצור את הקונטיינר ונהרוג אותו, אנחנו נמחוק את השכבה הזאת שנוצרה ונישאר עם האימג' המקורי עצמו.
בזמן שה-Docker Engine מריץ קונטיינר, הוא נותן לו הרגשה כאילו שהוא רץ לבד על מערכת ההפעלה, למרות שיכולים להיות עשרות ומאות קונטיינרים על אותו מחשב. הקונטיינר מקבל הרגשה כאילו שהוא היחיד שמשתמש בזיכרון, בפורטים ובכל משאבי המחשב. נוצרת לו מעין אשליה של בידוד.
איך זה מרגיש להיות קונטיינר?
ה-Docker Engine נותן לקונטיינר להרגיש מיוחד:
CPU (מעבד): הקונטיינר מרגיש שהוא מקבל 100% ממשאבי המעבד. הוא לא יודע שהוא בעצם קיבל רק חתיכה מהעוגה.
פורטים (Ports): מבחינת הקונטיינר, כל טווח הפורטים חשוף עבורו. יכול להיות שיש הרבה אפליקציות על המחשב שמשתמשות בפורט 8080, אבל זה לא מעניין אותו – מבחינתו הוא היחיד בעולם ולכן הוא יכול להשתמש בפורט הזה או בכל פורט אחר.
זיכרון (RAM): אם הקונטיינר קיבל נתח מסוים, נניח 2GB, מבחינתו זה כל הזיכרון שיש על המחשב.
נניח שיש לנו שלושה קונטיינרים על המחשב ששלושתם יוצאים משכבת הבסיס של לינוקס. אם נעשה Login וניכנס לתוך כל אחד מהם, אנחנו נראה מערכת קבצים (File System) זהה לחלוטין. לכל אחד יהיה את ה-Root Folder שלו, כאילו שהוא מחשב בפני עצמו.
מה אפשר לעשות עם קונטיינר שרץ?
בגלל שזה קובץ קול, פחות חשוב לי ה"איך" והפקודות הספציפיות, אלא שתבינו את היכולות:
ניהול Life Cycle (מחזור חיים): אפשר להריץ, לעצור, למחוק, להשהות (Pause) ולבצע פעולות שמשנות את מצב הקונטיינר.
עבודה מול האפליקציה: אפשר להריץ אפליקציות אחרות (קונטיינרים או אפליקציות רגילות) שיעבדו מולו, או לפנות אליו עם קריאות REST דרך כלי כמו Postman.
ניטור: להסתכל על הלוגים שהקונטיינר כותב, לבדוק כמה משאבים הוא צורך, ולראות את מיפוי הפורטים שלו.
תחזוקה: אפשר לעשות Log-in (בעזרת פקודת exec) לתוך הקונטיינר ולסייר בתיקיות שלו כאילו התחברנו למחשב מרוחק.
בריאות (Health): לבדוק אם הקונטיינר בריא (Healthy) או לא.
קונפיגורציה: לראות את משתני הסביבה (Environment Variables) ואת ה-Volumes (שמירת ה-State) שלו.
Container Life Cycle - מצבי הרצה:
Created: הדוקר אנג'ין יצר את הקונטיינר אבל עוד לא הריץ אותו.
Starting / Running: הקונטיינר בתהליך עלייה או רץ.
Paused: הקונטיינר "מוקפא".
Removing: בתהליך מחיקה.
Exited vs Dead: מצב Exited אומר שהקונטיינר סיים את הריצה שלו (בין אם בגלל תקלה או כי הוא פשוט סיים את המשימה שלו בהצלחה). לעומת זאת, Dead זה קונטיינר שניסה לסיים את הריצה שלו בצורה מסודרת (Gracefully) ולא הצליח.
קונטיינרים ומיקרו-סרוויסים (Microservices):
אי אפשר לדבר על דוקר בלי להזכיר מיקרו-סרוויסים. בעבר כתבו אפליקציות כ"מונוליט" (Monolith) – קובץ אחד ענק שעושה הכל. היום הגישה היא לפרק את זה להרבה אפליקציות קטנות שכל אחת עושה פעולה אחת מוגדרת.
הקשר ביניהם הדוק: כדי לקבל את כל היתרונות של קונטיינרים, כדאי לעבוד במיקרו-סרוויסים. וכדי לנהל מיקרו-סרוויסים ביעילות, כדאי להריץ אותם כקונטיינרים. זה מאפשר לנו לעשות Integration Tests (בדיקות אינטגרציה) בקלות: אם אני רוצה לבדוק את Service A, אני פשוט מרים לוקלית אצלי את הקונטיינרים של Service A יחד עם חמשת-עשרת הסרוויסים שהוא מדבר איתם, ומריץ את הבדיקה ב"סביבה" מלאה תוך שניות.
אבל מה קורה כשאנחנו רוצים להריץ 10-20 קונטיינרים בבת אחת? האם נכתוב docker run עשרים פעם בטרמינל? זה כבר חומר למחשבה להרצאה הבאה.