הגדרת Docker Compose לסביבת פיתוח משוחזרת

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

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

שאלות נפוצות

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

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

Docker Compose לפיתוח מקומי: היכן זה נשבר

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

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

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

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

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

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

מה נותן תיאור דקלרטיבי של סביבת השירותים

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

הזמן עד ההרצה הראשונה מתקצר מימים של המתנה לסביבה מוגדרת ל-20–30 דקות, והתקנות ידניות על מכונות מקומיות יוצאות מהתהליך כמעט לחלוטין.

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

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

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

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

דרכי הרצת הסביבה: השוואת מצבים

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

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

אפשרות מתי מתאימה מגבלות
הרצה מקומית של שירותים עד 5–7 שירותים, צוות של עד 10 אנשים, נדרשת עבודה ללא רשת משאבי מכונת העבודה, גרסאות האימג'ים מתפצלות בין המשתתפים
סביבה משותפת מרוחקת הרבה שירותים כבדים, נדרשים נתונים אחידים לבדיקות השהיית רשת, מצב נתונים משותף, השבתת הסביבה עוצרת את כולם
מצב פיתוח היברידי חלק מהשירותים משתנים לעיתים קרובות, חלק יציבים נדרשים כתובות והרשאות מתואמות, האבחון מסובך יותר
התקנה ידנית של שירותים ניסויים חד-פעמיים, שירות אחד-שניים אינו משוחזר אצל אחרים, מתחיל חדש מבזבז ימים על הגדרה

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

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

סדר היישום: מהאודיט ועד ההעברה לצוות

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

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

מארז הארטיפקטים: קונפיגורציות, מפות, נהלים

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

  • קובצי תיאור הסביבה — המניפסט הראשי של Docker Compose, תבנית משתנים עם ערכי ברירת מחדל בטוחים וקובץ נפרד של דריסות מקומיות שאינו נכנס ל-commit.
  • מפת הקשרים בין השירותים — אילו קונטיינרים רואים זה את זה, היכן עוברים הווליומים, אילו פורטים פתוחים החוצה, מי ובאיזה סדר עולה וממי הוא תלוי.
  • מדיניות גישה לסודות — מה מועבר דרך משתני סביבה, מה נמשך ממאגר סודות, ומה לעולם לא נכנס לריפו; כללי החריגים וסדר הסקירה שלהם.
  • הוראות למפתח חדש — מהשכפול ועד ההרצה הראשונה והרצת הבדיקות, עם רשימת כשלים שגרתיים והסימנים לזיהוים.
  • נוהל עדכון קונפיגורציות — מי רשאי לשנות את הקובץ הבסיסי, איך שינוי עובר בדיקה, ואיך מבצעים rollback אם הסביבה הפסיקה לעלות.
  • מערך בדיקות אוטומטיות — ולידציה של תחביר ותאימות הקונפיגורציה בענף הראשי, כדי שתיאור שבור לא יתפזר בין מכונות הצוות.
  • מפת הווליומים והנתונים — מה נשמר בין הפעלות מחדש, מה נמחק, ואיך מחזירים את הסביבה למצב נקי לבדיקה ניתנת לשחזור.

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

קייס: איך הסרנו פערי סביבות בצוות

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

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

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

מה השתנה במספרים

  • עליית הסביבה: היה 40–60 דקות, הפך ל-6–8 דקות בפקודה אחת, בלי לקרוא וויקי.
  • שיעור הריצות שנפלו בגלל פער בין סביבות: היה בערך כל ריצה שלישית, הפך למקרים בודדים.
  • הכנסת מהנדס חדש: היה כעבודת יום שלם, הפך ל-40–50 דקות עד לשירות הראשון שרץ.
  • כשלים מהסוג של "השירות עלה לפני מסד הנתונים" הפסיקו לשחזר: הסדר נקבע על ידי תלויות ובדיקות מוכנות, ולא על ידי משמעת.

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

מה לעשות אם הסביבה כבר נבנית ידנית

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

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

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

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

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

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

ספקות טכניים ומגבלות הגישה

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

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

מה לעשות אם חלק מהשירותים לא ניתן להרים מקומית?

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

איך להעביר סודות והרשאות לצוות?

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

עד כמה הסביבה תובענית מבחינת משאבי המכונה?

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

נדון במשימה ונחזור עם תוכנית והערכה

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

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

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