Dockerfile אופטימלי: האצת הבנייה והקטנת האימג'

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

היכולות שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

אופטימיזציה של Dockerfile: מדוע אימג'ים גדלים והבנייה מאטה

גודל האימג' וזמן הבנייה כמעט אף פעם לא נקבעים לפי שורה אחת של Dockerfile — אותם מגדיר מודל השכבות. כל הוראה RUN, COPY או ADD יוצרת שכבה נפרדת, ומחיקת קובץ בשכבה הבאה אינה מקטינה אותו: הנתונים נשארים בשכבה הקודמת.

לכן apt-get update, התקנת חבילות ו-rm -rf /var/lib/apt/lists/* בשכבות שונות נותנים אימג' כבד מהצפוי, למרות שבתוך הקונטיינר לא רואים קבצים מיותרים.

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

סדר ההוראות חשוב לא פחות: אם תלויות מותקנות אחרי COPY של כל הפרויקט, כל שינוי בקוד המקור מבטל את השכבה שלהן, והחבילות מותקנות מחדש בכל קומיט.

בסיס כבד מוסיף לכך קומפיילרים, קובצי header ומערכת מלאה של כלים — כל זה נשאר באימג', למרות שלעבודה דרושים רק האפליקציה והספריות שלה. הפרדת הבנייה וההרצה למספר שלבים משאירה בתוצאה רק את מה שנדרש להפעלת התהליך.

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

מה נותן אימג' מסודר: מהירות בנייה וצפיות

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

הסדר ההפוך — קודם מניפסט התלויות, אחר כך הקוד — משאיר את השכבה הכבדה ללא שינוי, ובנייה מחדש אחרי שינוי בקוד המקור אורכת שניות במקום דקות.

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

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

יכולת שחזור הבנייה — אפקט נפרד. Docker נותן תוצאה זהה רק כשקלט זהה: אימג' הבסיס מוצמד למזהה בלתי משתנה, לא לתג צף, התלויות מקובעות, והבנייה מתבצעת בסביבה אחידה ולא במכונת המפתח.

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

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

אופטימיזציה של Dockerfile: השוואת גישות לבניית אימג'

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

מה אנחנו משווים

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

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

שלבי בנייה מחדש: ממדידת שכבות ועד העברת נוהל

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

  1. ביקורת בנייה. מפרקים את קובץ הבנייה הנוכחי ואת הלוג: אילו שכבות נבנות מחדש בעת שינוי שורת קוד אחת, היכן המטמון הולך לאיבוד, כמה זמן אורכת בנייה מלאה ובנייה חוזרת, וכמה שוקל האימג' ברג'יסטרי.
  2. מפת שכבות. מפרקים את האימג' לפי תדירות השינויים: חבילות מערכת, תלויות הפרויקט, קוד מקור, קונפיגורציה. מה שמשתנה לעתים רחוקות — למטה, מה שמשתנה — למעלה, כדי ששכבות העבודה ייעשה בהן שימוש חוזר ולא ייבנו מחדש.
  3. סדר ההוראות. מסדרים את השלבים לפי המפה הזו, מעתיקים רק קבצים נדרשים, לא את כל התיקייה. כלי בנייה ותלויות זמניות נשארים בשלב נפרד ולא נכנסים לאימג' הסופי.
  4. הגדרת הבנייה. כללים להחרגת מיותר, אימג' בסיס מינימלי, תלויות מקובעות, נקודת התקנה אחת שלהן — כדי שהאימג' לא יגדל מבנייה לבנייה.
  5. בדיקת האימג'. משווים גודל, מספר שכבות, היעדר כלי דיבוג וקובצי שירות, זמן בנייה על מטמון נקי ובבנייה חוזרת. בנוסף מריצים בדיקה לחבילות פגיעות והפעלת השירות בסביבה מבודדת.
  6. בדיקת חזרתיות. שתי בניות רצופות על מכונות שונות נותנות תוצאה זהה, בלי שלבים ידניים ותלויות מקומיות — אחרת הרווח לא יישאר.
  7. העברת נוהל. מוסרים קובץ בנייה, כללי החרגה, תיאור שכבות וקריטריוני קבלה, הוראה קצרה "איך לשנות ומה לבדוק" — כדי שהצוות יתחזק את התוצאה בעצמו.

מה מקבלים לידיים: קונפיגורציות, מפות, נהלים

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

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

כל זה נשאר במאגר שלכם ונקרא כהוראה, לא כהתכתבות במסנג'ר.

מקרה בוחן: הבנייה הואצה פי כמה, האימג' קטן פי שניים

הפרויקט — שירות פנימי עם הרחבות נייטיב ועץ תלויות בולט. בנייה לכל קומיט ארכה 18 דקות, והאימג' המוכן היה גדול פי 2.5 מנפח הקוד עצמו: לתוכו נכנסו קומפיילרים, מטמון מנהל החבילות ונתוני בדיקה.

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

מה שינו

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

מה קרה

בנייה בעת שינוי קוד — 4 דקות במקום 18, בשינוי קונפיגורציה בלבד — פחות מדקה. האימג' קטן פי 2.6, מספר השכבות ירד מ-30 ל-11, ולכן ההפצה לצמתים והפעלת הקונטיינר הואצו באותה מידה.

שיעור כשלי ההרצה ירד מ-12% ל-1%: סוג הכשלים "אי-התאמת ספריות" נעלם לגמרי, ונשארו רק שגיאות קונפיגורציה של השירות עצמו.

במה שונה בנייה רב-שלבית מבנייה חד-שלבית בפועל?

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

בנייה רב-שלבית מפרידה בין התפקידים האלה. שלב הבנייה מתקין כלים ומקמפל, והשלב הסופי עולה מבסיס מינימלי ומקבל את התוצאה דרך העברת ארטיפקטים בין שלבים — העתקה מהשלב הקודם.

לאימג' הסופי נכנסים רק הבינאריים הבנויים וסביבת ההרצה, ולכן הגודל מצטמצם לגודל הקוד, וכלים מיותרים לא חודרים לקונטיינר.

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

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

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

שאלות ששואלים לפני בנייה מחדש של אימג'ים

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

האם צריך לכתוב את הבנייה מחדש כולה?

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

איך לוודא שהבנייה מחדש לא שברה דבר?

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

מה לעשות עם הקונפיגורציה הישנה?

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

מי אחראי לבנייה אחרי ההעברה?

כללי הבנייה נמצאים במאגר לצד הקוד, לא בהתכתבות. לכל שכבה לא מובנת מאליה — הערה קצרה: לשם מה היא ומה יקרה אם מסירים אותה. הצוות שלכם עורך את הקובץ בעצמו, והמהנדסים שלנו מצטרפים בשינויים שנוגעים לאימג' הבסיס.

נדבר על המשימה: מה לשלוח להערכה ותוכנית עבודה

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

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

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