למה הגדרת AWS ECR נשברת בפרודקשן
הגדרת AWS ECR בדרך כלל עוברת חלק, כל עוד רק מהנדס אחד נוגע במאגר של תמונות קונטיינרים ממכונה אחת.
ברגע שמתחברים אליו סוכני build, תחנות עבודה של מפתחים וקבלנים, מתחילה סטייה: אצל אחד התמונה הועלתה ידנית תחת תג latest, אצל אחר היא נבנתה מקוד שלא הועלה ל-commit, ובפרודקשן בסוף רץ ארטיפקט שאי אפשר לשחזר. פורמלית המאגר עובד, בפועל אף אחד לא יכול לומר מהיזה קומיט נבנה הריליס הנוכחי.
העלאה ידנית של תמונות שוברת שחזוריות מוקדם מכל. כל עוד התמונה נבנית במחשב נייד ונשלחת בפקודה אחת, בין "נבנה" ל"הועלה" נשאר חלון שבו הסביבה, התלויות ומשתני הסביבה מספיקים להתפצל. חזרה לריליס קודם בתרשים כזה הופכת לחיפוש — ייתכן שהתמונה הדרושה כלל לא נמצאת במאגר, כי דרסו אותה.
הרשאות גישה למאגר הן מקור הכשלים השני. מדיניות שניתנת בנדיבות, "כדי לא להפריע לעבודה", נותנת הרשאת כתיבה לכל מי שאמור רק לקרוא: תגים נדרסים על ידי buildים של אחרים, ואין במה לבדוק בדיעבד.
הקצה ההפוך — גישות שחולקו ידנית לפי אנשים: סוכן חדש או קבלן ממתין שבוע למפתח, והצוות חוזר להעלאה ידנית.
הנקודה השלישית — הצפת אחסון התמונות. בלי כללי lifecycle כל תג גורר אחריו שכבות, נפח המאגר גדל ליניארית, ובשלב כלשהו ה-build נופל בשליחה — הריליס נתקע ברגע הכי לא נוח.
נפרק איך זה נסגר: היכן שמים גבולות להרשאות, איך הופכים את ההעלאה לאוטומטית ומה מנקה את המאגר בלי לאבד את היכולת לחזור אחורה.
מה נותן מאגר תמונות מנוהל ב-ECR
מאגר מנוהל הוא לא מחסן של תמונות, אלא קו מתאר עם מדיניות גישה ואחסון. תפקידי IAM מפרידים pull לסביבות הרצה ו-push ל-pipeline build, ולכן סוכן ה-build פיזית לא יכול לדרוס תג שכבר נפרס ב-production.
בנוסף מפעילים אי-שינוי של תגים: ארטיפקט תחת תג נתון נשאר אותו דבר, וניסיון לפרסם אותו מחדש נדחה ברמת Amazon ECR.
כלל lifecycle מחזיק כמה תמונות אחרונות לכל תג ומנקה את השאר לפי גיל — המאגר מפסיק לגדול ליניארית, אבל חזרה לריליס קודם נשארת זמינה. כלל נפרד ל-buildים ביניים מוחק אותם אחרי יממה, כדי שענפי פיתוח לא תופסים מקום כמו ריליסים. בסופו של דבר האחסון גדל ביחס למספר הריליסים, לא למספר הקומיטים.
אוטומציה של ההעלאה מסירה שלבים ידניים: build, תיוג לפי מזהה קומיט, פרסום, עדכון תיאור השירות. זמן ההעלאה מצטמצם מעשרות דקות, שחציין תיאום תגים והחלפה ידנית, לכמה דקות של תהליך צפוי.
חזרה אחורה הופכת להחלפה לתג אי-שינוי קודם, ולכן זמן ההתאוששות אחרי תקלה נמדד בדקות ולא תלוי במי מהמהנדסים בתורנות.
שחזוריות של builds סוגרת סוג נפרד של כשלים — פער בין ארטיפקטים בסביבות שונות. אותה תמונה לפי digest עוברת בדיקות ב-staging ומגיעה ל-production, ולא נבנית מחדש "בערך מאותו קוד". חלק הפעולות הידניות יורד, ואיתו נעלמות תקלות בגלל גורם אנושי בשחרור.
אפשרויות פרסום תמונות: השוואת גישות
שיטת הפרסום קובעת מה יגיע ל-staging ב-rollout הבא והאם ניתן לחזור למצב הקודם. תג נוח כהפניה קריאה, אבל בעת דריסה שני מצבי מערכת שונים מצביעים על אותו שם — כבר אי אפשר להשוות בין builds. הפניה למזהה אי-שינוי קושרת את ה-rollout לתוכן תמונה מסוים, ולכן החזרה אחורה היא שחזורית.
הבחירה השנייה — מאגר אחד או הפרדה לפי סביבות. במאגר משותף הרשאות גישה וכללי אחסון מופרדים לפי prefixes של שמות, וטעות בכלל אחד פוגעת בכל קווי המתאר בבת אחת. מאגרים נפרדים נותנים מדיניות ניקוי עצמאית: בקו פרודקשן אוסרים דריסת תגים, בטסטים חותכים את העודף באגרסיביות.
| אפשרות | מתי מתאימה | מגבלות |
|---|---|---|
| פרסום לפי תג | הרצות ידניות, בדיקות מהירות | דריסת תג שוברת שחזוריות והשוואת builds |
| פרסום לפי מזהה אי-שינוי | rollout פרודקשן, ביקורת, חזרה אחורה | לא קריא ידנית, נדרשת משמעת ב-pipeline build |
| מאגר אחד לכל הסביבות | קו אחד, צוות קטן | הרשאות וכללי ניקוי משותפים, טסטים משפיעים על קו הפרודקשן |
| מאגרים מופרדים לפי סביבות | כמה קווים, דרישות לבידוד גישה | כפילות תמונות, קשה יותר לסנכרן כללים |
| מדיניות ניקוי אוטומטית | ריליסים קבועים, הרבה ענפים | בלי הגדרה מדויקת נמחקת תמונה שנדרשת לחזרה אחורה |
בפועל משלבים את האפשרויות: קו פרודקשן מפורסם רק לפי מזהה אי-שינוי, המאגרים מופרדים לפי סביבות, ומדיניות הניקוי מחזיקה את ה-buildים האחרונים לכל תג. המאגר מפסיק לגדול ללא שליטה, והחזרה לגרסה קודמת נשארת זמינה.
איך אנחנו בונים את ההטמעה: שלבי העבודה
את העבודה מתחילים במלאי, לא מדף חלק: מאגר AWS ECR בדרך כלל כבר קיים, אבל הרשאות ניתנו ביתר, תמונות מצטברות בלי הגבלה, ומפתחות גישה מונחים במשתני build. מביאים את זה לסכמה שצוות הלקוח מתחזק בעצמו — סדר השלבים זהה גם לקו חדש וגם לפירוק של מאגר בירושה.
-
ביקורת הרשאות גישה והסכמה הנוכחית. בודקים אילו repositories ותגים באמת קיימים, מי מפרסם תמונות ומאיפה מגיעים פרטי הזיהוי. בפלט — מפה של הרשאות עודפות, כפילויות ומקומות בלי רוטציה.
-
תכנון סכמת המאגר. מפצלים את האחסון לפי סביבות וצוותים, קובעים כללי שמות לתמונות ותגים. הסכמה נשארת בתיאור התשתית, ולא בהגדרות ידניות בקונסול.
-
הגדרת מדיניות גישה. תפקידים עם הרשאות מינימליות מספיקות: כתיבה — רק מ-pipeline ה-build, קריאה — ממוקדת ל-staging. מפתחות ארוכי טווח מחליפים בפרטי זיהוי זמניים עם רוטציה.
-
מדיניות אחסון תמונות. מחיקה אוטומטית של builds ישנים תוך שמירת תגי ריליס אחרונים, סריקת חולשות, הגנה על תגי ריליס מדריסה. המאגר מפסיק לגדול, והחזרה לגרסה קודמת נשארת זמינה.
-
בדיקה בקו טסטים. מריצים פרסום, rollout, חזרה אחורה וקריאת תמונה מסביבה שכנה. מוודאים שההרשאות לא רחבות מהנדרש ושכתיבה מחשבון זר נדחית.
-
מסירת התשתית. מוסרים תיאור של המאגר, התפקידים והמדיניות כקוד, ובנוסף נוהל עבודה עם תגים. עוברים עם הצוות על פעולות טיפוסיות: repository חדש, גישה לקבלן, חזרה אחורה של ריליס.
ערכת תיעוד, מדיניות וסכמות מאגר
אחרי ההטמעה לצוות נשארת לא ערכת פקודות מהיסטוריית צ'אט, אלא ערכה עובדת — קונפיגורציות, מדיניות ומסמכים שלפיהם מהנדס חדש יחזור על התהליך בלי המחבר. כל הארטיפקטים מנוהלים בגרסאות ב-repository ועוברים review כמו קוד.
- תפקידי IAM ומדיניות גישה — הרשאות לפי עקרון המינימום הנדרש: פרסום מה-pipeline, קריאה מסביבות הרצה ופעולות ניהול מופרדים לתפקידים שונים, ובנוסף גבולות מפורשים בין פרויקטים.
- סכמת שמות לתמונות — repository לכל שירות, תג אי-שינוי לריליס, תגים נפרדים לסביבות בדיקה; לפי השם רואים מיד מה ומאיפה נפרס.
- נוהל ניקוי — כללי lifecycle עם ספים: כמה תמונות אחרונות להחזיק לכל תג, מתי למחוק builds ביניים ובאיזו תדירות לבחון מחדש את הספים.
- קונפיגורציית המאגר כקוד — repositories, בדיקות בעת העלאה והצפנה מתוארים בקבצים ומיושמים אוטומטית, בלי עריכות ידניות בקונסול.
- הוראות למהנדסים — איך מפרסמים תמונה, איך חוזרים לריליס קודם, מה עושים כשפרסום נכשל ואיך מבקשים הרחבת הרשאות.
- סכמת קשרים ל-pipeline ולסביבות — היכן נבנית התמונה, לאן מפרסמים, מי קורא אותה ואילו בדיקות עוברות בכל שלב.
- סדר ביקורת — מי ובאיזו תדירות בוחן מחדש את המדיניות, כדי שההרשאות לא יצטברו וזמני האחסון יתאימו לדרישות.
את הערכה מוסרים ל-repository שלכם עם הסבר קצר — מהנדס תורן יתמודד עם המאגר בלי לפנות אלינו.
מקרה בוחן: קיצרנו את זמן ההעלאה והסרנו שלבים ידניים
הלקוח — צוות מוצר שבנה תמונות ופרסם אותן ל-AWS ECR ידנית: מהנדס תייג build, העלה אותו וערך קונפיגורציית סביבה לפני כל שחרור. ב-12–14 פרסומים בשבוע זה נתן עד 14 פעולות ידניות במחזור ריליס — וכמעט כל סטייה שנייה בפרודקשן נבעה מכך שתג לא נכון נכנס לסביבה.
מה היה לפני
הפרסום ארך 25–30 דקות: build, המתנה להעלאה, בדיקה ידנית שהתמונה באמת נכנסה למאגר. המאגר גדל בלי כללים — תמונות הצטברו בכל התגים, כולל builds ביניים של ענפים, ואיתור הארטיפקט הרלוונטי הפך למעבר סריקה. חזרה ל-build קודם הגיעה לשעה, כי התמונה הדרושה לא הייתה בהישג יד.
מה שינינו ומה יצא
הפרדנו הרשאות: סוכן ה-build קיבל רק פרסום ל-repositories מסוימים, המהנדס — קריאה וחזרה אחורה, הפרודקשן — רק משיכת תמונה. כללי lifecycle נקשרו לסכמת התגים: תגי ריליס אי-שינוי נשמרים לאורך זמן, builds ביניים מנוקים לפי כמות וגיל. את הפרסום הטמענו ב-pipeline — התג מוצב אוטומטית, האדם רק מאשר שחרור.
הפרסום הצטמצם ל-5–7 דקות, פעולות ידניות בריליס נשארו שתיים: אישור ובדיקת מדדים. נפח המאגר קטן פי שלושה תוך שמירה מלאה על היסטוריית הריליסים, והחזרה אחורה מתבצעת בהחזרת התג הקודם תוך דקות ספורות. חלק השחרורים שהוחזרו בגלל ארטיפקט שגוי ירד לאפס.
במה שונה פרסום לפי תג מפרסום לפי hash
תג הוא הפניה ניתנת לשינוי ל-manifest: app:release היום מצביע על build אחד, ומחר ה-pipeline משייך אותו לאחר. התג בעצמו לא מבטיח שאותו קוד ייפרס כמו אתמול — ומכאן נובעים הפערים בין staging לפרודקשן.
ה-hash (digest) של ה-manifest מחושב מתוכן התמונה, ולכן אי אפשר לשכתב אותו בלי לשים לב: תחת מזהה אחד תמיד נמצאות אותן שכבות ואותה קונפיגורציה. אי-השינוי של הארטיפקט מובטח על ידי מיעון לפי תוכן, ולא על ידי הבטחה לא לעשות שימוש חוזר בתג.
ההבדל בולט בחזרה אחורה של ריליס. פריסה לפי hash היא פעולה דטרמיניסטית: מחזירים מזהה שנקבע קודם ומקבלים בדיוק את התמונה שעבדה לפני התקלה, בפעולה אחת ובלי ניחושים. עם תגים החזרה אחורה הופכת לחקירה: צריך לזכור איזה תג הצביע על מה, ולקוות שלא שייכו אותו מחדש ולא מחקו אותו בניקוי.
הסכמה העובדת היא כזו: ה-build קורה פעם אחת, התמונה מקבלת תג קריא לאדם לצורכי ניווט, וה-hash נרשם ב-manifest הפריסה. לפרודקשן ול-staging מגיע ה-hash, והתג נשאר תווית לאנשים. אם ב-repository מופעל אי-שינוי תגים, דריסת מזהה שפורסם נחסמת ברמת המאגר, והחלפת גרסה מקרית נחתכת טכנית.
בזכות זה הסביבה משוחזרת: לפי manifest הפריסה מרימים את אותה גרסה בכל רגע ובכל staging, ותמונה ישנה לבדיקת השערה נמצאת לפי hash גם אם התג שלה זז מזמן.
שאלות נפוצות על גישות ומחזור חיים של תמונות
גישות וניקוי המאגר מפחידים בכך ש"ישברו ריליס עובד". בפועל שתי הפעולות מוטמעות בלי downtime: כלל lifecycle קודם מריצים ב-preview — רואים אילו תגים ייפלו למחיקה — והתפקידים ניתנים בנפרד לקריאה ולכתיבה. פיצול המפתח הוא לפי איזה סימן מנקים ואיך נותנים הרשאות בלי לפתוח יותר מדי.
תגי ריליס מסומנים כאי-שינוי, ולכן למחיקה נכנסים רק builds ביניים ותמונות בלי תג שגילן עולה על התקופה שנקבעה.
repositories נפרדים לפי שירות מונעים מצוות אחד לפגוע בתמונה של צוות אחר, והתקרה על מספר תמונות ב-repository מפסיקה להיות בעיה: רשומות של builds חדשים עוברות, כי ישנות מתפנות אוטומטית.
נפעיל ניקוי — האם הוא לא ימחק תמונה שנדרשת לחזרה אחורה?
לא, אם הכלל נבנה לפי תגים ולא לפי תאריך. הסכמה "להחזיק N התמונות האחרונות לכל תג" שומרת ריליסים קודמים, ולמחיקה נכנסים רק builds ביניים ותמונות בלי תג שאף rollout לא מפנה אליהן.
איך לתת גישה לקבלנים חיצוניים בלי למסור מפתחות לכל המאגר?
תפקיד לקריאת תמונות בתוספת הרשאה למשוך אותן מ-repository מסוים — בלי הרשאות מחיקה ושינוי. לגישה מחשבון זר מוסיפים מדיניות ברמת ה-repository: הקבלן מתחבר עם חשבון משלו, אין צורך להעביר לו מפתחות, והגישה מבוטלת בשורה אחת.
מה יקרה להעלאה שכבר עובדת?
כלום: שירותים שרצים ממשיכים לעבוד מה-cache המקומי של הצמתים. ניקוי והחלפת הרשאות נוגעים רק למאגר, ולכן מוטמעים בשלב נפרד, לא יחד עם ריליס.
נדבר על המשימה ונחזור עם הערכה ותוכנית
כדי לחזור עם הערכה ותוכנית, ולא עם מילים כלליות, בשיחה הראשונה צריך מינימום נתונים: איך בנוי ה-build עכשיו, מי מפרסם תמונות ואילו סביבות נתמכות. מספיק לתאר את המשימה במילים שלכם — את השאר נשלוף בשאלות.
מדייקים לפני חישוב זמנים:
- כמה שירותים ו-repositories במאגר, מי ובאיזו תדירות מפרסם תמונות;
- איך ניתנות גישות כעת: חשבונות אישיים, מפתחות משותפים או תפקידים עם הגבלת הרשאות לפי סביבות;
- איך בנוי pipeline ה-build: במה בונים, באיזה רגע התמונה מגיעה למאגר, האם יש staging ביניים;
- דרישות אחסון: כמה דורות של תמונות להחזיק, האם נדרשים תגים אי-שינוי לריליסים ובאיזו תדירות לנקות;
- מדיניות אבטחה: סריקת חולשות, איסור פרסום ממכונות מקומיות, יומן פעולות והפרדת הרשאות בין צוותים;
- תרחישי חזרה אחורה: כמה מהר צריך להחזיר גרסה קודמת ומי עושה זאת;
- מגבלות: נהלים פנימיים, חלונות עבודה ומי מצדכם מקבל את התוצאה.
לפי התשובות מרכיבים את היקף העבודה על המאגר, מפרקים אותו לשלבים ומציינים זמני הטמעה — בנפרד מה שמהנדסים שלנו עושים, בנפרד מה שנשאר בצד שלכם. כך באמצע הפרויקט לא מופיעים סעיפים "לא צפויים".
תארו את המשימה — נחזור עם הערכה ותוכנית.







