הגדרת CI/CD לאפליקציות Java (Maven/Gradle)

בנייה ידנית של פרויקטי Java גוזלת שעות מהצוות, והשגיאות צצות רק בפרודקשן. אנו מגדירים CI/CD ל-Java מקצה לקצה: בנייה, בדיקות אוטומטיות ושחרור ללא צעדים ידניים. זמן הבנייה מצטמצם בחצי, ומספר הרולבקים פוחת פי שלושה.

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

שאלות נפוצות

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

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

בניית פרויקטי Java ב-CI/CD: איפה זה נשבר ולמה

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

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

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

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

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

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

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

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

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

חזרתיות — מה שמשפיע ישירות על שיעור הבניות המוצלחות.

אם הסביבה מוגדרת הצהרתית, ולא נבנית ידנית על שרת מסוים, „אצלי מקומית זה עובד” מפסיק להיות סוג בעיה נפרד: Gradle או Maven בקונטיינר עם גרסת JDK קבועה נותנים ארטיפקט זהה, ופער גרסאות נתפס בבנייה מטריציונית, ולא בסביבת הבדיקות לפני שחרור.

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

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

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

השוואה בין מצבי בנייה והרצת בדיקות

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

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

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

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

שלבי הגדרת CI/CD לאפליקציות Java

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

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

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

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

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

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

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

מקרה בוחן: קיצרנו את זמן הבנייה ומספר החזרות לאחור פי שלושה

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

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

מה שינינו

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

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

מה התוצאה

הבנייה המלאה התקצרה מ-40 ל-11 דקות, והזמן מקומיט לפרודקשן — מכמה ימים ל-45 דקות. שיעור החזרות לאחור ירד מ-18% ל-6%, והשחרורים במקום פעמיים בחודש יוצאים שלוש פעמים בשבוע ועוברים בלי פעולות ידניות.

במה שונה Maven מ-Gradle בהגדרת בנייה

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

מהירות הבנייה החוזרת תלויה במה שהכלי מחשיב כשונה. Gradle משווה קלטים ופלטים של משימות ובונה מחדש רק מודולים שנגעו בהם, לכן ב-CI/CD פרויקט Java עם עשרה מודולים לא מורץ במלואו על כל שינוי.

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

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

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

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

ספקות טכניים נפוצים לפני תחילת העבודה

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

האם אפשר לעבור לצינור חדש בלי לעצור שחרורים?

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

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

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

איך לבנות מונורפו — במלואו או לפי מודולים?

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

איך להפריד סביבות ולא לגרור קונפיגורציה לתוך הארטיפקט?

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

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

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

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

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