למה CI/CD Python מתפרק על תלויות
התרחיש הקלאסי: מפתח מריץ בדיקות מקומית, הכול ירוק, הענף נכנס לצינור המשותף — ובניית התלויות נופלת בהתקנת החבילות. הסיבה אינה בקוד, אלא בסביבת המפתח: היא הצטברה במשך חודשים ואינה מתוארת באף קובץ של הפרויקט.
המקור הראשון — היעדר סט גרסאות נעול. אם תלויות מוצהרות בטווחים, וקובץ הנעילה אינו מתעדכן בכל שינוי, CI/CD Python מתקין את מה שנמצא במאגר ברגע ההרצה. שתי הרצות על מכונות שונות נותנות עצי תלויות שונים, וקונפליקט גרסאות חבילות צף כבר בענף של מישהו אחר.
השני — ערבוב כלים. pip, Poetry ו-Conda פותרים תלויות באופן שונה: Conda מושך בניות בינאריות מהערוצים שלו, pip — מהמאגרים, Poetry נועל עץ משלו. חבילה אחת שהותקנה בעקיפין ממנהל אחר הופכת את הבנייה לבלתי ניתנת לשחזור.
שורה נפרדת של כאב — גרסת המפרש: פער בין המכונה המקומית לבין האימג' בצינור משנה את כללי פתרון התלויות ושובר חבילות בינאריות שנבנו לגרסה אחרת.
המחיר של חוסר יציבות כזה — לא רק בנייה שנפלה. נפילת בנייה בשלב ההתקנה אוכלת זמן של כל הצוות, מרגילה להתעלם מהרצות אדומות ומעבירה את חיפוש השגיאה האמיתית ליום השחרור. נפרק כיצד זה נסגר.
מה משתנה בצוות אחרי בנייה ושחרור מסודרים
האפקט מצינור מסודר נראה במספרים יומיים: כמה זמן לוקחת הבנייה, כמה זמן לוקח החזרה לשחרור הקודם, איזה חלק משלבי השחרור נעשה ביד. להלן ארבעה שינויים שהלקוח מבחין בהם כבר בשבועות הראשונים, בלי פרויקט נפרד של "הטמעת מדדים".
בנייה — דקות במקום עשרות דקות
מטמון תלויות ושכבות, אימג' בסיס נעול, הפרדה בין התקנת חבילות להרצת בדיקות — הבנייה מפסיקה להוריד ולקמפל שוב ושוב את אותו הדבר. המפתח מקבל משוב על הקומיט שלו, ולא לקראת סוף היום, והתיקונים מפסיקים להצטבר בהמתנה להרצה.
שחזוריות בכל הסביבות
Poetry עם קובץ נעילה, גיבובי חבילות ואימג' אחד למכונה המקומית, לסביבת הבדיקות ולסביבת הייצור מסירים פערים של "אצלי זה עובד". סט התלויות זהה בכל מקום, לכן התקלה משוחזרת מקומית, ולא רק בשרת חמש דקות לפני השחרור.
חזרה לשחרור הקודם — דקות
ארטיפקט נבנה פעם אחת ומקודם בין סביבות בלי בנייה מחדש: לסביבת הייצור נכנס בדיוק מה שעבר בדיקות. אם השחרור לא הלך כשורה, החזרה היא מעבר לתג הקודם, ולא בנייה מחדש וחיפוש הענף הנכון בחיפזון.
מחלקת כשלים שנעלמת
פערי סביבות, תלות שנשכחה, גרסת ספרייה לא תואמת, שלב שדולג ידנית — הכשלים האלה נתפסים בשלב הבדיקות ולא מגיעים למשתמשים. חלק הפעולות הידניות בשחרור יורד, ואיתו נעלמת השונות: התרחיש מבוצע באופן זהה גם בידי אדם וגם לפי לוח זמנים.
דרכים לבניית סביבות לפרויקטי Python
ב-Python אין נעילת סביבה מובנית: המפרש אחד, וסט הספריות בענף המערכת משותף לכל הפרויקטים. די להרים ליד שירות שני עם גרסה מג'ורית לא תואמת של תלות משותפת — וזה שכבר עובד נופל.
סביבה וירטואלית נותנת בידוד, אבל לא שחזוריות: בלי נעילת גרסאות, בנייה על מכונה אחרת תמשוך תלויות טרנזיטיביות חדשות והתנהגות הקוד תשתנה.
הבחירה במנהל תלויות קובעת מה בדיוק נכנס לבידוד ובאיזו מידה זה משוחזר בבניית חבילות.
| אפשרות | מתי מתאים | מגבלות |
|---|---|---|
venv + pip + requirements.txt |
שירותים קטנים, תלויות מפורטות ידנית | אין נעילת גרסאות טרנזיטיביות — התקנה חוזרת נותנת סט אחר |
pip-tools |
כבר יש requirements.txt, צריך נעילת גרסאות |
רשימות ישירות וטרנזיטיביות מנוהלות בנפרד, קונפליקטים נפתרים בבנייה מחדש |
| Poetry / PDM | ספריות ושירותים עם קבוצות תלויות לבדיקות ולתיעוד | אלגוריתם פתרון גרסאות משלו, פערים מול pip בבניית ארטיפקטים |
| Conda | סטאק מדעי ותלויות בינאריות מחוץ ל-PyPI | ערוצים כבדים, הסביבה מצטמצמת בקושי לאימג' מינימלי |
| אימג' קונטיינר כסביבה | שירותים שבהם סביבת הבנייה וה-runtime חייבות להיות זהות | בלי מטמון שכבות זמן הבנייה גדל פי כמה בכל תיקון קוד |
באופן מעשי הבחירה נעשית בשני צירים: מי פותר קונפליקטים של גרסאות — pip או מנגנון משל של המנהל, והיכן עובר גבול הסביבה — בתיקייה וירטואלית או באימג'.
לשירות שעולה כקונטיינר, סביר לנעול גרסאות בקובץ נעילה ולהתקין תלויות בשכבה נפרדת: אז תיקון קוד לא גורר ספריות, ואי-התאמת גרסאות צפה בבנייה, לא בזמן ריצה.
שלבי הטמעה: מסקירת מאגרים ועד מסירה
סדר העבודות זהה לכל סטאק: קודם להבין מה כבר יש במאגרים, ורק אחר כך לבנות צינור. דילוג על הביקורת מוביל לכך שהבנייה עולה בפרויקט אחד ומתפרקת בשאר, ופער סביבות צף כבר בפריסה. להלן הרצף שבו אנחנו עוברים את ההטמעה.
- ביקורת מאגרים. בודקים את מבנה הפרויקטים ואת מה שמצהיר על התלויות: requirements.txt עם גרסאות מדויקות, pyproject.toml עם Poetry או סביבת Conda נפרדת. מסמנים היכן הסביבה נבנית ביד וכמה זמן זה גובה מהצוות.
- סכמת צינור. מתאמים שלבים והסדר שלהם: הכנת סביבה, בדיקות קוד, בדיקות, בניית ארטיפקט, פרסום, אספקה לסביבות. לכל שלב מיד מגדירים מה נחשב לשגיאה חוסמת.
- הגדרת סביבות. מביאים את גרסת המפרש והרכב התלויות לצורה אחידה, מפעילים מטמון חבילות בין הרצות — בנייה חוזרת לא מושכת הכול מאפס.
- הרצת בדיקה. מפעילים את הצינור על מאגר או ענף אחד, מודדים את זמני השלבים ומחפשים נפילות על ריק: תלויות צפות, היעדר בידוד לבדיקות.
- בדיקת קבלה. עוברים תרחישים קריטיים ללקוח: נפילת בדיקות חוסמת אספקה, חזרה לארטיפקט הקודם, בניות מקבילות לא מפריעות זו לזו.
- מסירה והדרכה. מוסרים את סכמת הצינור, הוראות לתיקון שלבים וניתוח כשלים טיפוסיים, עוברים עליהם עם הצוות — בהמשך התמיכה מתבצעת בכוחות עצמם.
קונפיגורציות, סכמות ותקנונים: מה נשאר בפרויקט
צינור מוגדר בלי תיעוד חי עד העזיבה הראשונה של המהנדס שהרכיב אותו: אחרי חודש כבר אף אחד לא זוכר למה התלויות נעולות דווקא כך ומה יקרה בחזרה לאחור. לכן יחד עם בנייה עובדת מוסרים ערכת ארטיפקטים למאגר הפרויקט ולבסיס הידע של הצוות — לפיהם התהליך משוחזר בלי המחבר.
- קבצי קונפיגורציה של הצינור עם הערות: היכן שלבי הבנייה והבדיקות, אילו תנאי הרצה, מה נשמר במטמון בין הרצות.
- קבצי ניהול תלויות לכלי הנבחר (pip, Poetry או Conda): גרסאות נעולות, הפרדת סטים לפיתוח ולשחרור.
- סכמת סביבות: אילו קונטורים קיימים, מה נפרס בכל אחד, במה שונים הפרמטרים, איך הסביבה עולה מאפס.
- מדיניות גישה לסודות: היכן נשמרים מפתחות, מי מנפיק אותם, איך מתבצעת רוטציה ומה מובטח שלא נכנס ללוגים.
- תקנון שחרור: המסלול מענף לסביבת ייצור, מי מקבל החלטה, באילו נקודות אפשר לחזור לאחור ומה חוסם שחרור.
- הוראות למהנדס תורן: כשלי בנייה טיפוסיים, סדר פעולות בנפילת הרצה ובחזרה לאחור, נקודות הסלמה.
הערכה מסירה תלות בהסכמות בעל פה: מהנדס חדש נכנס לפרויקט לפי מסמכים, והבודק רואה מי ועל סמך מה שינה הרשאות ושחרורים. התקנון וסכמת הסביבות נשארים מסמכי עבודה — מתקנים אותם יחד עם הקוד, ולא משחזרים אחרי תקלה.
מקרה בוחן: צינור שחרור לצוות מבוזר
בצוות מבוזר בנייה של שירות אחד ארכה בין 18 ל-25 דקות, והזמן הזה כמעט לא היה תלוי בהיקף התיקונים.
הסיבה לא הייתה בבדיקות: חצי מהשלב אכלה התקנת תלויות, שכל קונטור פתר בדרכו — שירותים משכו גרסאות חדשות מהמאגר המשותף, חלק מהפרויקטים נבנו עם Poetry, וקונטור האנליטיקה חי בסביבת Conda נפרדת. בסוף התקבל העיקר: הבילדר והמכונה של המפתח נתנו סטים שונים של חבילות.
מה שינו
התלויות אוחדו למקור אמת אחד: גרסאות נעולות בקובץ נעילה, ההתקנה ממנו ולא מטווחים. מטמון שכבות מוגדר כך שבנייה מחדש של הסביבה מופעלת רק בשינוי הקובץ הזה, והבנייה עצמה ב-GitLab CI רצה באימג' בלתי משתנה, הצמוד לרוויזיה מסוימת של הקוד. הבדיקות פוצלו לסט מהיר לכל שינוי ולהרצה מלאה לפני שחרור.
לפני: כ-20 דקות לבנייה, כל שחרור רביעי נשבר — פער סביבות, נפילה בשלב ההתקנה, תיקונים ידניים בשרת. אחרי: הבנייה נכנסת ל-6–7 דקות, שחרור אחד מעשרים נשבר, וחזרה לאחור מחזירה את האימג' הקודם בדקות במקום פירוק ידני.
במה בניית ספרייה שונה מבניית שירותים?
ספרייה ושירות נבדלים לא בתוכן המאגר, אלא במה שנחשב לתוצאה של הבנייה. אצל ספרייה זה חבילה בלתי משתנה שמתפרסמת בריג'יסטרי פרטי וממשיכה לחיות בתלויות של אחרים. אצל שירות — אימג' שנפרס בסביבה מסוימת ונשאר לעבוד שם חודשים. מכאן מתפצלות הדרישות למספור גרסאות, פרסום ארטיפקטים וחזרה לאחור.
בניית ספריות נבדקת במטריצה: כמה גרסאות מפרש, שני מנהלי התלויות הנתמכים, ובנפרד — התקנת החבילה מהארטיפקט הבנוי, לא מהמקורות. סכמת מספור הגרסאות כאן סמנטית וקשיחה: שינוי ששובר תאימות לאחור חייב להעלות את החלק המג'ורי. צוותים אחרים תלויים בחבילה, והם מתעדכנים לפי לוח הזמנים שלהם, לא בו-זמנית איתכם.
פרסום ארטיפקטים לספרייה — פעולה בלתי הפיכה במשמעותה: גרסה אחת יוצאת פעם אחת, דריסה אסורה, אחרת לשתי בניות שונות יהיה אותו שם. אם בגרסה שפורסמה נמצאה שגיאה, מוציאים גרסת patch חדשה; החזרה לאחור מתבצעת בצד הצרכן, שמקבע את המספר הקודם בקובץ התלויות שלו.
בניית שירותים פותרת משימה אחרת: לקבל אימג' משוחזר שמתנהג אותו דבר בסביבת בדיקות ובקונטור העבודה. כאן בודקים הפעלת אפליקציה, מיגרציות של סכמת נתונים, קריאת קונפיגורציה וסודות, התנהגות תחת עומס.
מספור הגרסאות נקשר למצב המאגר ולתג השחרור, לא למספר החבילה — חשוב לתת מעקב "איזו רוויזיה של הקוד עובדת כרגע".
חזרה לאחור של שירות ושל ספרייה — גם כן פרוצדורות שונות. אי אפשר להחזיר חבילה בריג'יסטרי: אפשר רק לסמן גרסה בעייתית כלא ראויה, ופרויקטים תלויים יעבירו את הקיבועים שלהם. שירות חוזר לאחור במעבר לאימג' הקודם — מהר, אבל רק אם מיגרציות הסכמה תואמות לקוד הקודם. לכן שינויים בנתונים מכינים כך ששלב שחרור אחד יאפשר עבודה של שתי הגרסאות.
ספקות טכניים לפני תחילת העבודה
שאלות שצפות לרוב לפני תחילת העבודה נוגעות לא לכלים, אלא לסיכונים: מה יקרה לצינור שכבר עובד, איך נפתרים קונפליקטים של תלויות ומה קורה אחרי המסירה. להלן תשובות קצרות לכל אחד.
יש לנו כבר צינור בנייה מוגדר — נצטרך לכתוב הכול מאפס?
לא. קודם עורכים מיפוי: אילו סביבות, מאיפה נמשכות תלויות, היכן שלבים כפולים. בדרך כלל רוב הגדול מועבר לשימוש חוזר — משתנים רק שלבי התקנת התלויות והמטמון. המעבר נעשה בשלבים, כדי שהענף הראשי יישאר עובד.
מה לעשות אם תלויות של שני שירותים מתנגשות בגרסאות?
מפרידים אותן לסביבות מבודדות ומקבעים זוגות לא תואמים במטרות בנייה נפרדות. קונפליקט שאי אפשר להסיר בעדכון ממוקם בשירות אחד ולא חוסם בנייה של האחרים. ניתוח קונפליקטים נעשה לפי עץ התלויות, לא לפי ניסוי וטעייה.
אפשר להסתפק בלי מאגר חבילות משלנו?
כן, אבל עם הסתייגויות: בכל בנייה מושכים ממאגרים חיצוניים ושומרים מטמון מקומי. מאגר משלנו כדאי להרים כשצריך חבילות סגורות, שליטה קפדנית בגרסאות או בנייה בלי גישה לרשת חיצונית. ההחלטה מתקבלת לפי מספר השירותים ודרישות הבידוד.
איך מובטחת שחזוריות הבנייה?
נועלים גרסאות מדויקות, לא טווחים, ושומרים קובץ נעילה לצד המקורות. הבנייה רצה בסביבה נקייה, עם אימג'י בסיס מקובעים. זה נותן תוצאה זהה במכונת המפתח ובסוכן הבנייה, ומאפשר לחזור על שחרור אחרי חודשים.
מי מתחזק את הצינור אחרי המסירה?
מוסרים קונפיגורציות, סכמות שלבים ותיאור כשלים טיפוסיים עם סדר פעולות. צוות הלקוח יכול להפעיל את הצינור בעצמו — התיעוד מיועד לכך. אם צריך תמיכה, נשארים בליווי: מתקנים כשלי בנייה, מעדכנים תלויות, מפתחים שלבים לשירותים חדשים.
נדבר על המשימה ונחזור עם סכמה ותוכנית
כדי לחזור עם סכמת צינור, ולא עם מילים כלליות, מספיק לנו דיון אחד על המשימה. אנחנו מפרקים איך הקוד נבנה ומתפרסם כיום, היכן לרוב נופלות הבדיקות ואילו שלבים לפני שחרור גרסה נעשים ביד.
מכם צריך מינימום נתוני פתיחה:
- מאגר או המבנה שלו: ענפים, תגים, סדר שחרור גרסאות;
- קבצי תלויות — מה בשימוש: pip, Poetry או Conda, האם יש מראות חבילות פרטיות;
- רשימת סביבות ובמה הן נבדלות בהרשאות ובמשתני סביבה;
- נקודות תורפה בחודשים האחרונים: בניות שנפלו, שלבים ידניים, חזרות לאחור;
- אילוצים: רשתות סגורות, דרישות הרשאות ותקנוני אבטחה;
- זמן בנייה ושחרור רצוי — מספרים שאתם מחשיבים לסבירים.
בתגובה אנחנו שולחים סכמת צינור שלמה: שלבים, תנאי הרצה, מטמון תלויות, הפרדת בדיקות ופרסום ארטיפקטים. אליה — הרכב עבודות עם גבולות "מה נכנס ומה לא", תוכנית עבודה לפי שלבים ואומדן לוחות זמנים בימי עבודה, בנפרד להגדרה ולהעברה לתפעול. אם בשלב כלשהו אנחנו רואים סיכון, אומרים עליו לפני ההתחלה, ולא באמצע.
תארו את המשימה בצורה חופשית — נחזור עם אומדן ותוכנית.







