למה Docker ל‑AI/ML נשבר על GPU ומודלים גדולים
הכשל הנפוץ ביותר נראה כבר בהרצה הראשונה אצל הלקוח: אצלנו הקונטיינר מקבל גישה ל‑GPU, אצלו – לא. הסיבה בדרך כלל אינה בקוד, אלא בפער בין הדרייבר במארח לספריות בתוך האימג': סביבת ההרצה לא מעבירה את ההתקן, והמודל עובר בשקט למעבד. בנייה לא משוחזרת כזו עולה ימים של התכתבויות והתקנות מחדש.
הקו השני — בעיות קונטיינריזציה של GPU בגלל התנגשות גרסאות של דרייברים וספריות מחשוב. אימג' שנבנה מעל ענף CUDA חדש לא עולה במארח עם דרייבר ישן, ועדכון שלו שובר קונטיינר שכבר עובד. כל עוד התלויות לא מקובעות, כל סביבה חיה חיים משל עצמה, ו«אצלי זה עובד» מפסיק לומר משהו.
אימג'ים כבדים של מודלים מחמירים את התמונה. שכבת בסיס עם סט מלא של ספריות, משקלים, מטמון חבילות וכלי בנייה נשארים בארטיפקט הסופי — האימג' מתנפח, נפרס לאט על צומת חדש ומנפח את הרישום. כל ניסוי מוסיף עוד בנייה כזו, וכבר מפחיד למחוק אותה בלי השלכות.
הלאה — בנייה ידנית לכל מודל. Dockerfile נפרד לכל ארכיטקטורה, תלויות מותקנות ביד, שלבים משוחזרים מזיכרון המהנדס. העברת פרויקט הופכת לשחזור בעל פה, לא להעברת ארטיפקטים: אצל הקבלן ואצל הלקוח נוצרות סביבות שונות באותו קוד.
בסך הכול אלה לא אי‑נוחות קטנות, אלא אובדן זמן ישיר בכל הרצה ועדכון. להלן נפרט כיצד הכשלים האלה נסגרים ב‑Docker ל‑AI/ML — ברמת בניית אימג'ים, קיבוע תלויות וגישה למאיצים.
מה נותנות אופטימיזציית אימג'ים והאצת GPU בעבודה
אימג' עם מודל צובר תלויות מהר יותר מקוד: קובצי משקלים, ספריות האצה, חבילות מערכת. כל עוד הכול יושב בשכבה אחת, כל תיקון בונה אותו מחדש לגמרי — זמן בניית האימג' גדל בכל איטרציה, והרישום מתנפח. חלוקה לשכבות ומטמון לחלקים שמשתנים לעיתים רחוקות מאפשרים בנייה בדקות במקום בעשרות דקות.
האצת GPU בקונטיינרים נשענת על שלושה דברים: העברת התקנים, סביבת הרצה תואמת וגרסאות דרייברים מקובעות. אנחנו מוציאים אותם לשכבת בסיס ולא נותנים להם להתעדכן באופן שרירותי — הקונטיינר רואה את המאיץ באותה צורה בכל הסביבות, מפיתוח ועד סביבת הייצור. לחיבור עם NVIDIA ההעברה מופרדת לשכבה נפרדת: החלפת דרייבר לא שוברת את הבנייה כולה.
יכולת שחזור ההרצה נגזרת מאותו סדר. אופטימיזציה של Docker image כוללת קיבוע גרסאות ספריות, בקרה על אימג'י בסיס ופריסת תלויות כך שמשקלים כבדים יישמרו במטמון בנפרד מהקוד. אימג' שנבנה אתמול עולה היום בלי «אי‑התאמה של סביבות».
התוצאה מדידה: זמן הבנייה מתקצר פי כמה, גודל אימג' המודל יורד בלי אובדן פונקציונליות, שיעור ההרצות המוצלחות עולה, וסוג הכשלים בגלל אי‑התאמת גרסאות יוצא מהתמיכה.
בניית Docker ל‑AI/ML: השוואת גישות ומצבים
בניית אימג' לעומס AI שונה משירות רגיל: פנימה נכנסים ספריות GPU, קישורי דרייבר ומשקלי מודלים, והבסיס וסדר השכבות קובעים כמה זמן נמשכת בנייה מחדש ובאיזו תדירות נכשלת ההרצה על המאיץ. בפועל בוחרים בין ארבע סכמות — ולכל אחת יש השלכות.
| אפשרות | מתי מתאימה | מגבלות |
|---|---|---|
| אימג' בסיס יחיד | כמה מודלים על מחסנית משותפת, חשוב לחסוך במקום בצמתים | מתנפח: התלויות של כל המשימות בשכבה אחת, כל עדכון בונה הכול מחדש |
| אימג' נפרד לכל מודל | מודלים חיים במחזור משל עצמם ודורשים גרסאות שונות של ספריות | מספר הארטיפקטים וזמן התחזוקה גדלים, רישום בלי מדיניות שמירה מתמלא מהר |
| תוספת שכבות | מחסנית הבסיס יציבה, משתנים המשקלים וקוד ההסקה של המודל | יש לסדר את השכבות מהנדירות לתכופות, אחרת המטמון לא מנוצל מחדש והבנייה שוב גוררת הכול |
| אימג' צד שלישי מוכן | צריך לבדוק השערה בתוך שעות, ולא לבנות סביבה | הרכב לא שקוף, תלויות מיותרות בשכבות, קשה יותר לעבור בדיקת פגיעויות ולשחזר את הבנייה |
מעל לכך, על הבחירה מוטלות מגבלות של הרצת הקונטיינר עצמה: גישה למאיץ דרך העברת התקנים, אי‑התאמה של גרסת דרייבר במארח ובאימג', משקל המשקלים של המודל בשכבה ואיסור על הורדה מהרשת בסביבה סגורה.
לכן את הסכמה מתאימים לא לפי «טוהר» הפתרון, אלא לפי באיזו תדירות משתנים המודלים ועד כמה מחמירות הדרישות לשחזור. תארו את המשימה — נחזור עם הערכה ותוכנית.
סדר היישום: מביקורת הסביבה ועד למסירה
סדר העבודה נבנה כך שסביבת העבודה לא משתנה בלי לראות: קודם מתעדים את המצב הנוכחי, אחר כך בונים את סכמת הקונטיינרים, ורק לאחר מכן מעבירים את העומס.
כל שלב מסתיים בארטיפקט שאפשר לבדוק — בלעדיו לא עוברים לשלב הבא, ולכן הפער בין «אצלי זה עובד» ל«בשרת זה נופל» עולה בשלב ההגדרה, ולא ביום ההרצה.
- ביקורת הסביבה. עורכים מלאי של שרתים, דגמי מאיצים, גרסאות דרייברים וספריות, הרכב התלויות של כל מודל. בפלט — מפה: מה כבר נפרס בקונטיינרים, מה חי במארח ומה מתנגש זה עם זה.
- סכמת קונטיינרים. קובעים מספר אימג'ים, שכבת בסיס, סדר שכבות בהתחשב במטמון וכללי תיוג. משקלים כבדים של מודלים מוציאים מחוץ לאימג' — האימג' מפסיק לשקול ג'יגה‑בייטים ונבנה מחדש בדקות, ולא בעשרות דקות.
- גישה למאיצים. מגדירים העברת התקנים, הרשאות משתמש בתוך הקונטיינר ובידוד בין שירותים. בודקים שבעת הפעלה מחדש של המארח הקונטיינר עולה לבד ורואה את המאיץ בלי פקודות ידניות.
- בדיקה תחת עומס. מריצים תרחישי הסקה ואימון אמיתיים: בקשות מקבילות, תורים בשיא, התנהגות במחסור בזיכרון. מסתכלים לא על ממוצעים, אלא על זמני התגובה הגרועים ביותר.
- נוהל עדכון. מקבעים מדיניות גרסאות, שמירת הבניות האחרונות וסדר חזרה לגרסה קודמת. עדכון מפסיק להיות אירוע עם סיכון — והופך לפעולה שגרתית.
- מסירה לתפעול. מוסרים תיעוד לבנייה והרצה, מעבירים הדרכה לצוות, מפרקים כשלים אופייניים. בהמשך התמיכה מתנהלת לפי הנוהל שלנו או בכוחות המהנדסים שלכם.
קונפיגורציות, סכמות ונוהלים שתקבלו
העבודה לא מסתיימת בקונטיינר מורץ, אלא בסט ארטיפקטים שלפיו הצוות שלכם יוכל לבנות, לעדכן ולחזור בגרסאות בסביבה בלי ההשתתפות שלנו. להלן — מה בדיוק נשאר אצלכם לאחר המסירה.
- מפת אימג'ים: איזה אימג' לאיזו משימה — אימון, הסקה, הכנת נתונים — מה נמצא בשכבת הבסיס המשותפת, היכן מתחילה שכבת היישום ואילו תלויות מקובעות בחוזקה.
- סכמת פריסת מאיצים: אילו צמתים מספקים מאיצים, כיצד הם מתחלקים בין משימות, היכן נוצר מחסור בזיכרון, וכיצד מוגדרת העברת התקנים לתוך הקונטיינר.
- מדיניות גישה לרישום: תפקידים והרשאות, מי מפרסם ומי רק קורא, תוקף מפתחות, כללי שמירה של תגים אחרונים ואיסור על דריסה של מה שכבר פורסם.
- נוהל עדכון אימג'ים: תדירות בנייה מחדש של שכבת הבסיס, סדר הבדיקה לפני פרסום, תנאים ונוהל חזרה לתג הקודם.
- תיעוד בנייה: קובצי בנייה עם הערות, רשימת תלויות, נימוק לבנייה רב‑שלבית ולמה שהושאר בכוונה מחוץ לאימג'.
- הוראות תפעול: הפעלה ועצירה, בדיקת זמינות מאיצים, איסוף לוגים ומטריקות, פירוק תקלות אופייניות וסדר הפעולות בהן.
- סכמת סביבות: במה שונים סביבת הפיתוח, סביבת הבדיקות וסביבת העבודה, מה מועבר ביניהן ומה מוגדר בנפרד.
את הסט מלווים בפירוק עם הצוות שלכם — כדי שהחלטות בהמשך יתקבלו לפי מסמכים, ולא לפי הזיכרון של מהנדס מסוים.
מקרה בוחן: זמן הבנייה התקצר, כשלי GPU נעלמו
הפרויקט — אימון והעלאה של מודלים על כמה צמתים עם מאיצים, כמה צוותים ומאגר שרתים משותף. את הסביבה למשימה בנו ביד: העברת תלויות, בדיקת תאימות, הרצה. להלן — מה זה דרש לפני היישום ומה השתנה לאחר המעבר לסכמת אימג'ים אחידה.
מה היה
מתוך כארבעים בניות בחודש, מחציתן שוחזרו עם פערים: מקומית המודל התכנס, בשרת נפל בייבוא ספריות. קונטיינרים על Docker נבנו ביד ונשמרו מחוץ לרישום משותף — כל צומת חי עם סט תלויות משלו.
סעיף הפסדים נפרד — כשלי GPU: הדרייבר בצומת לא תאם את ציפיות האימג', והמשימה מתה בהתחלה, לאחר שכבר תפסה מקום בתור לאימון.
מה שינינו ומה נהיה
צמצמנו את הסביבות לאימג' בסיס אחד עם סט קבוע של ספריות ושכבות: אימג'ים יישומיים יורשים ממנו ונבדלים רק בקוד ובהגדרות. בדיקת התאימות לדרייבר הועברה לשלב הבנייה — אי‑ההתאמה מתגלה לפני הכנסת המשימה לתור, ולא באמצעה.
הבנייה אורכת דקות במקום שעות, יכולת שחזור ההרצות קרובה לשלמה, וכשלי GPU בהתחלה מצטמצמים למקרים בודדים. הרישום לא מתנפח: מדיניות שמירה מחזיקה מספר מוגבל של שכבות לתג, והחזרה לאימג' הקודם היא פקודה אחת.
במה שונה אימג' בסיס משותף מאימג' למודל אחד?
אימג' בסיס משותף הוא סביבה אחת עם סביבת הרצה, ספריות לחישוב על מאיצים וכלי עזר משותפים, שמעליה מותקן כל מודל. אימג' למודל הוא בנייה נפרדת עם בדיוק סט התלויות הנדרש למודל אחד. ההבדל מתבטא במקום שבו הצוות משלם: בזמן בנייה או בסיכון להתנגשות תלויות.
הראשון מרוויח מניצול חוזר של שכבות. שכבה עם ספריות מערכת ודרייברים נבנית פעם אחת ומכאן נלקחת מהמטמון לכל המודלים — בנייה מחדש לאחר תיקון קוד נמשכת דקות במקום עשרות דקות. סט בדיקות אחד ותרחיש פריסה אחד במקום כמה.
הצד השני — התנגשות תלויות. כאשר מודל אחד צריך ענף חדש יותר של ספרייה, פתרון הגרסאות שובר את הסביבה של המודל השכן: מודל שענה אתמול כראוי נופל בהתחלה או משנה תוצאה, והסיבה נמצאת בשכבה המשותפת. גם החזרה היא משותפת — כשמחזירים מודל אחד, אתם גוררים אחורה את הסביבה של כולם.
אימג' נפרד משתלם כשהמודל משתנה לעיתים קרובות, והתלויות מקובעות בחוזקה. קוד ומשקלים נמצאים בשכבה העליונה, הבסיס נשאר מהמטמון, והחזרה נקודתית: החלפת תג מחזירה את הגרסה הקודמת בלי לפגוע בשירותים השכנים. המחיר — גודל הרישום ומספר תרחישי הבנייה.
בחירת אסטרטגיית הבנייה מצטמצמת לשתי שאלות: באיזו תדירות מתעדכן המודל ועד כמה דרישותיו נפרדות מהאחרות.
בעדכונים נדירים ובמחסנית משותפת די באימג' בסיס אחד; בהעלאות תכופות ובפערים לוקחים שכבת בסיס דקה עם דרייברים ובונים מעליה אימג' למודל — הניצול החוזר של שכבות נשמר, והתנגשות התלויות נשארת בתוך שכבה אחת.
מגבלות, תאימות וספקות טכניים נפוצים
קונטיינר מיישר את סביבת היישום, אבל לא מבטל את השכבה שמתחתיו: גישה למאיץ, גרסת דרייבר בצומת ודרישות ליבה נשארות מחוץ לאימג'. לכן את צי המכונות צריך להחזיק בגרסת דרייבר מתואמת — אחרת אותו אימג' בחלק מהצמתים פשוט לא יראה את ההתקן. מטריצת התאימות מקבעים מראש ובודקים בצומת פיילוט, ולא ביום ההעלאה.
חלק מהדברים לא מתאחדים באופן עקרוני: חלוקת זיכרון בין תהליכים, רוחב פס אמיתי תחת מודל מסוים, התנהגות במחסור בזיכרון. כאן נשארת בדיקה ידנית בפרופיל עומס אמיתי — סינתטיקה מראה רק את התקרה, ולא את מצב העבודה.
ויש גבול ישימות: במקום שצריך גישה ישירה לחומרה או אימון על כמה צמתים עם אפיק משותף, חלק מהדברים נשארים בכנות על מכונה פיזית.
אצלנו המודלים כבר עובדים מחוץ לקונטיינר — להעביר הכול מחדש?
לא. מעבירים בשלבים: קודם הסביבה — תלויות, משקלים, נקודת כניסה, אחר כך השוואת התוצאה על אותן כניסות, ורק לאחר מכן מחליפים את התעבורה. ההרצה הישנה נשארת זמינה לחזרה, עד שהחדשה תעבור מחזור עומס מלא.
מה לעשות אם הצמתים בנויים על דרייברים שונים?
את האימג' בונים מתחת לגרסה המתואמת הנמוכה ביותר — NVIDIA שומרת על תאימות לאחור בתוך ענף ראשי. היכן שצומת מפגר, את הדרייבר מעדכנים לענף המשותף; אם זה בלתי אפשרי, מוציאים את הצומת מהמאגר למשימה הזו, ולא טלאים את האימג'.
נדון במשימה: סביבה, מודלים ודרישות הרצה
נדון במשימה באופן ענייני: כדי שהערכת העבודה תישען על עובדות ולא על ניחושים, נתחיל בנתוני פתיחה על הסביבה והמודלים. מספיק לשלוח את מה שכבר יש בהישג יד. לפי הנתונים האלה רואים היכן צוואר הבקבוק — בנייה, גודל אימג', תאימות דרייברים או שחזור בצומת אחר.
מה עוזר לחשב את העבודה במדויק:
- הרכב המודלים ומצב ההרצה: אימון, כיוונון עדין או רק הסקה;
- פרמטרי הסביבה: סוג ומספר המאיצים, זמינות דרייברים וסביבת הרצה;
- בניית Docker אימג'ים נוכחית ותלונות: זמן, גודל, הבדלים בין צמתים;
- רישום אימג'ים: היכן נשמר, למי יש גישה, כיצד מנקים גרסאות ישנות;
- סביבת הרצה: שרת אחד, ענן, אשכול, אורקסטרטור;
- עבודה עם נתונים וכרכים: מערכי נתונים, עותקים מקומיים של משקלים, ארטיפקטי אימון;
- מגבלות: סביבה סגורה, מדיניות אבטחה, נוהל עדכונים.
את השיחה הראשונה מנהל מהנדס שאחר כך יעשה את העבודה — בלי שחזור דרך מתווכים. מפרקים את סכמת ההרצה, נקודות מחלוקת והחלטות שכבר התקבלו, ולאחר מכן מקבעים מה לוקחים קודם.
נחזור עם הערכה ותוכנית יישום: שלבים, לוחות זמנים, אחראים, נקודות בדיקה וסדר חזרה לאימג' הקודם. השלב הבא — תארו את המשימה ושלחו נתוני פתיחה על הסביבה, את השאר נדייק בשיחה.







