הערה: ראשית, במאמר זה נשתמש במושגים הלועזיים ל-Continuous Integration ו-Continuous Delivery. אמנם קיימים מונחים בעברית (ע״ע ויקיפדיה) אבל היות ואלו מונחים מקצועיים, מרבית האנשים משתמשים בהם באנגלית. את יתר המונחים נשתדל להציג באנגלית אבל גם להשתמש בחלופות העבריות שלהם.
אין מחסור בתוכן המסביר מהם Continuous Integration, Continuous Delivery ו-Continous Deployment. אבל לשם מה קיימים התהליכים האלו מלכתחילה?
חשוב להבין מהן הבעיות אותן פותרים באמצעות CI ו-CD כדי להשתמש בהם בצורה הנכונה. הבנה זו תאפשר לצוותים שלכם לשפר תהליכים, ולהימנע מהשקעת משאבים ברדיפה אחר מדדים ״מגניבים״ שאינם מביאים שום תועלת לתהליך עצמו.
אם בצוות שלכם עובדים מספר מפתחים על אותו ה-repository (או repo בקיצור), קיים branch ראשי (ענף ראשי) שמחזיק את הגרסה העדכנית ביותר של הקוד המפותח. מפתחים עובדים על דברים שונים ב-branch-ים שונים, ברגע שאחד מהם סיים עם השינוי שלו, הוא ידחוף או יאחד (merge) אותו לענף הראשי. בסופו של דבר, כל הצוות ימשוך את השינוי הזה ויעבוד על בסיסו.
התרחיש שנרצה להימנע ממנו הוא שגרסה פגומה (faulty commit) תמצא את דרכה לענף הראשי. במונח פגומה אנו מתכוונים לקוד שלא מתקמפל או כזה שנמצא באפליקציה שלא רצה או אינה יציבה. מדוע? לא בגלל שהאפליקציה תקולה או בגלל שכל הבדיקות חייבות לעבור ולהיות ירוקות. בכך איך בעיה, הרי תוכלו לא לפרוס (deploy) אותה ולהמתין לתיקון.
הבעיה היא שכל הצוות שלכם תקוע. כלל המפתחים שמשכו את הגרסה הפגומה יבזבזו 5 דקות בניסיון להבין למה היא לא עובדת. חלקם כנראה ינסו למצוא את ה-commit הספציפי שגרם לה, אחרים ינסו לתקן את הבעיה בעצמם במקביל למפתח הקוד התקול עצמו.
מדובר בבזבוז זמן עבור הצוות. החלק הגרוע ביותר הוא שמקרים חוזרים גורמים לחוסר אמון בענף הראשי ומעודדים כל מפתח לעבוד בנפרד.
מהות ה-Continuous Integration במניעת מצב שבו הענף המרכזי שבור, כך שהצוות לא תקוע ויכול לעבוד. זהו. לא מדובר במצב שבו כל הבדיקות עוברות בהצלחה כל הזמן והענף המרכזי ניתן לפריסה ל-Production בכל commit.
תהליך ה-Continuous Integration אינו תלוי בשום כלי ספציפי. תוכלו לוודא כי ה-merge שלכם לענף המרכזי פועל בצורה ידנית ולדאוג שהוא עובד מקומית, ורק לאחר מכן באמת לבצע איחוד ל-repo. כמובן שגישה כזו רחוקה מלהיות יעילה, ולכן CI ממומש באמצעות בדיקות אוטומטיות.
• שהאפליקציה תעבור build ותהיה ניתנת להרצה
• מרבית היכולות הקריטיות יעבדו בכל זמן נתון (לדוגמא הרשמה/login של משתמש, ויכולות עסקיות מרכזיות)
• שכבות משותפות של האפליקציה שכל המפתחים מתבססים עליהן עובדות בצורה יציבה. משמעות הדבר היא שקיימות בדיקות יחידה והן עוברות בהצלחה
בפועל, המשמעות היא שעליכם להשתמש ב-framework שמתאים לכם עבור בדיקות היחידה ולאבטח באמצעותן את החלקים המשותפים באפליקציה. לעיתים לא מדובר בהרבה קוד ואפשר לעשות כך בזריזות יחסית. בנוסף, עליכם להוסיף בדיקת עשן (smoke test) המוודאת שהקוד מתקמפל והאפליקציה עולה. נקודה זו חשובה במיוחד כאשר מדובר בטכנולוגיות המזריקות תלויות רבות כדוגמת Java Spring או .NET Core בפרויקטים גדולים, קל מאוד לטעות בתלויות כך שבדיקה שהאפליקציה עולה (לפחות) היא הכרח/חובה.
אם יש לכם מאות או אלפי בדיקות כנראה שאין חובה להריץ את כולן כנגד כל merge, מדובר בבזבוז זמן ומרביתן כנראה לא מוודאות יכולות שהן בגדר ״חוסמות״ לכל הצוות.
בחלקים הבאים נראה כיצד תהליך ה-Continuous Delivery עושה שימוש חיובי בבדיקות המרובות הללו.
כלים ובדיקות אוטומטיים הם בסדר גמור. אבל אם המפתחים שלכם רק מבצעים merge של ענפים ענקיים, עליהם הם עובדים במשך שבועות, הכלים והבדיקות לא יעזרו לכם. הצוות ישקיע כמות מכובדת של זמן בביצוע merge-ים של הענפים ותיקון חוסר התאמות בקוד שיצוצו בסופו של דבר. מדובר בזבוז זמן דומה לחסימה עקב גרסה פגומה.
ב-Continuous Integration לא מדובר בכלים, אלא בעבודה בפיסות קטנות ובאינטגרציה של הקוד החדש לענף המרכזי ומשיכה ממנו בתדירות גבוהה.
תדירות גבוהה משמעה לפחות על בסיסי יומי. פצלו את המשימה עליה אתם עובדים למשימות קטנות יותר, בצעו merge לעיתים קרובות ומשכו גם כן. בצורה זו, אין אדם שעובד בנפרד יותר מאשר יום או יומיים ולבעיות אין מספיק זמן לגדול כמו כדור שלג.
משימה גדולה לא צריכה להיות מוכלת כולה בענף אחד, בעצם אסור שתהיה. טכניקות לאיחוד עבודה שטרם הסתיימה לענף המרכזי מכונות ״branching by abstraction״ ו-“feature toggles”. תוכלו לקרוא עליהן במאמרים הבאים.
פשוט מאוד – קצר. טווח של 3 עד 7 דקות צריך להיות המקסימלי. לא מדובר במעבדים ומשאבים. מדובר ב-productivity של מפתחים. הכלל הראשון בפרודוקטיביות הוא מיקוד. עשו דבר אחד, סיימו איתו, עברו לדבר הבא.
החלפות הקשר (context switching) הן יקרות. מחקרים מראים שלוקח כ-23 דקות כדי להתמקד מחדש במשהו אחרי הפערה. דמיינו שביצעתם merge לענף שלכם ועברת למשימה אחרת. השקעתם 15-20 דקות להיכנס לעניינים, רגע אחרי שנכנסתם ל-zone ופתאום מגיעה התראת “build failed” מבילד ה-CI של המשימה הקודמת, תהליך בילד שלוקח 20 דקות. חזרתם לנסות ולתקן אותו, ביצעתם עדכון ועוד push. בקלות איבדתם יותר מ-20 דקות במעבר הזה.
הכפילו 20 דקות פעם או פעמיים ביום בכמות המפתחים בצוות שלכם... מדובר בבזבוז זמן גדול ויקר.
עכשיו, דמיינו שהמשוב מגיע תוך שלוש דקות, ושאתם יודעים שכך המצב. כנראה שלא הייתם מתחילים במשימה החדשה כלל. כנראה שהייתם עוברים על הקוד שלכם פעם נוספת, או אפילו עוברים על pull request (PR) בזמן ההמתנה. ההתראה אודות הכישלון הייתה מגיעה, הייתם מתקנים אותה ועוברים למשימה הבאה. זהו בדיוק המיקוד אותו התהליך שלכם צריך לאפשר.
שמירה על בילד CI קצר היא גם מעין פשרה. בדיקות הרצות זמן ארוך יותר או מספקות מעט ערך בהקשר ה-CI צריכות לעבור לשלב ה-CD. וכן, כישלונות בשלב הזה גם דורשות תיקון. אבל היות והן לא מונעות מאחרים לעשות את עבודתם, ניתן לקחת את התיקונים כ״משימה הבאה״ אחרי שתסיימו את מה שאתם כרגע עובדים עלו. פשוט כבו את ההתראות בזמן העבודה ובדקו מדי פעם. שמרו על מספר מינימלי של context switching.
בואו נתחיל בתיאום הפרשנות למושגים לפני שנמשיך. כאשר מדברים על Continuous Delivery מתכוונים ליכולת לפרוס כל אחת מגרסאות הקוד שלכם בכל זמן נתון. בפועל מדובר בגרסה האחרונה או זו שלפניה. לרוב, לא פורסים גרסאות בצורה אוטומטית כי אינכם חייבים בכך או עקב הגבלות במחזור חיי הפרויקט. אך ברגע שמתחשק למישהו לעשות כך, פריסה יכולה להיות מבוצעת במינימום זמן. המישהו הזה יכול להיות צוות הבדיקה/QA שרוצים לבדוק דברים בסביבות staging או pre-production. או אפילו כאשר הגיע הזמן לשחרר גרסה ל-production.
הרעיון ב-Continuous Delivery הוא להכין artefacts שהם קרובים ככל הניתן למה שהייתם רוצים להריץ בסביבה שלכם. אלו יכולים להיות קבצי jar או war אם אתם עובדים עם Java, קבצי הרצה אם מדובר ב-.NET, קבצי JavaScript או אפילו קונטיינרים שלDocker , כל דבר המקצר זמני פריסה (זאת אומרת, דברים שהכנתם מראש).
בהכנת artefacts אנו לא מתכוונים להפיכת קוד ל-artefacts. לרוב מדובר בכמה סקריפטים וכמה דקות של הרצה. הכנה משמעה:
הרצת כל הבדיקות האפשריות המבטיחות שהקוד הנפרס באמת יעבוד. הרצת בדיקות יחידה, בדיקות אינטגרציה, בדיקות end-to-end ואפילו בדיקות ביצועים אם יש באפשרותכם לבצע אוטומציה שלהן.
בצורה זו, מתאפשר לכם לסנן אילו גירסאות של הענף המרכזי שלי באמת מוכנות ל-production ואילו לא. חבילת הבדיקות האידאלית:
• מבטיחה שהפונקציונליות המרכזיות באפליקציה עובדות, אידאלית כולן
• מבטיחה שלא הוכנסה פגיעה רצינית בביצועי המערכת עם הגעת הגירסה החדשה שלכם למשתמשים רבים
• בדיקה ״יבשה״ (Dry Run) של שדרוגי מסד הנתונים הנדרשים על ידי הגירסה החדשה
החבילה לא צריכה להיות מאוד מהירה, חצי שעה עד שעה הם זמנים מתקבלים על הדעת.
השלב הבא הוא Continuous Deployment. פריסה של הגירסה העדכנית ביותר (והמוכנה ל-production) של הקוד שלכם, לסביבה כלשהי. אידאלית לסביבת production אם אתם סומכים על חבילת בדיקות ה-CD שלכם בצורה מספקת.
שימו לב שבתלות בהקשר, לא תמיד אפשרי או שווה להשקיע את המאמץ. Continuous Delivery לעיתים רבות מספיק כדי להיות פרודוקטיביים. בייחוד אם אתם עובדים ברשת סגורה ומספר סביבות מצומצם לפרוס אליהן. בנוסף, ייתכן שלוח הזמנים של שחרורי התוכנה שלכם (release lifecycle) מונע פריסות לא מתוכננות.
Continuous Delivery ו-Continuous Deployment (בוא נקרא להם CD מעתה) הם לא בעיות צוותיות. מדובר במציאת האיזון הנכון בין זמני הרצה, מאמצי תחזוקה ורלוונטיות של חבילות הבדיקות לסמן ש״הגירסה עובדת כצפוי״, ומדובר באיזון. אם הבדיקות שלכם רצות 30 שעות – זו בעיה. קראו את המאמר הזה כדי להבין כיצד נראית חבילת הבדיקות של מסד הנתונים של אורקל. אם אתם משקיעים כל כך הרבה זמן בשמירה על בדיקות עדכניות לקוד העדכני ביותר שלכם – זו בעיה שמעכבת את התקדמות הצוות וגם לא מהווה סימן חיובי. ואם חבילת הבדיקות שלכם כמעט ולא מבטיחה כלום... היא בעצם חסרת תועלת.
בעולם אידאלי היינו רוצים סט אחד של artefacts הניתנים לפריסה עבור כל commit לענף המרכזי. ניתן להבחין אם כך בבעיית scalability אנכית: ככל שנעבור מהר יותר מקוד ל-artefacts, כך נהיה מוכנים יותר לפריסת הגירסה העדכנית ביותר של הקוד.
עם Continuous Integration מדובר בבעיית scalability אופקית. אתם מעוניינים שהמפתחים שלכם יבצעו merge לעיתים קרובות כך שהבדיקות צריכות להיות מהירות. אידאלית תוך דקות בודדות כדי שהמפתחים ימנעו מ-context switching כל הזמן עם משוב אסינכרוני מתהליכי ה-build של ה-CI.
ככל שיש לכם יותר מפתחים, כך גדלה עוצמת המחשוב הנדרשת להרצה של בדיקות פשוטות (build ו-test) על גבי כל הענפים הפעילים.
בילד CI טוב:
• מבטיח שאין פיסת הקוד בענף המרכזי ששוברת יכולות בסיסיות של המערכת והמונעת מחברי צוות אחרי לעבוד
• מהיר מספיק כדי לספק משוב למפתחים תוך דקות, כדי למנוע context switching בין משימות
Continuous Delivery ו-Deployment, שתיהם מהווים בעיות scalability אנכיות, כאשר ישנה פעולה מורכבת למדי לביצוע.
בילד CD טוב:
• מבטיח שמספר גדול ככל האפשר של יכולות פועלות בנדרש
• מהיר ככל האפשר, אבל לא מדובר בעניין של מהירות. 30-60 דקות של בילד הן סבירות לגמרי.
תפיסה שגויה נפוצה למדי היא לראות ב-CD כבעיית scalability אופקית, בדומה ל-CI: ככל שעוברים מהם יותר מקוד ל-artefacts, כך עולה מספר ה-commit-ים שניתן באמת לעבד, וכל מתקרבים לתרחיש האידאלי. אבל אנו לא צריכים את זה, ייצור artefacts עבור כל commit מהר ככל האפשר הוא לרוב מוגזם. ניתן לגשת ל-CD על בסיס best effort: שימוש בבילד CD יחיד שפשוט בוחר את ה-commit האחרון כדי לוודא כאשר בילד מסויים מסתיים.
אל תטעו ותחשבו ש-CD הוא קל. הגעה למצב של ביטחון מספיק במערכת הבדיקות כדי להגיד שהתוכנה שלכם מוכנה לפריסה לרוב עובדת רק באפליקציות עם שטח פנים נמוך (כגון מערכות שבעיקרן הן API או בעלות ממשק מאוד פשוט). מאוד קשה להשיג זאת במערכת בעלת ממשק מסובך או מערכת מונוליטית גדולה.
הכלים והעקרונות לביצוע של CI ו-CD הם לרוב דומים, המטרות שונות מאוד.
Continuous Integration הוא איזון בין מהירות המשוב למפתחים והרלוונטיות של הבדיקות המבוצעות (build ו-test). אף פיסת קוד המונעת מחברי הצוות לעבוד לא מגיעה לענף המרכזי.
Continuous Delivery של Deployment משמעו הרצת בדיקות יסודיות ככל הניתן כדי לתפוס בעיות בקוד. שלמות הבדיקות היא הגורם החשוב ביותר. לרוב, היא נמדדת במונחים של code coverage או functional coverage של הבדיקות. תפיסת שגיאות בשלב מוקדם מונעת מקוד פגום להיפרש לסביבות כלשהן וחוסכת זמן יקר מצוות הבדיקות שלכם.
בנו את הבילדים של ה-CI וה-CD להשיג את היעדים האלו כדי לשמור על הצוות שלכם פרודוקטיבי. אין workflow מושלם, בעיות עלולות לצוץ מדי פעם. השתמשו בהן כשיעורים כדי לחזק את ה-workflow כאשר הן צצות.
הערה: ראשית, במאמר זה נשתמש במושגים הלועזיים ל-Continuous Integration ו-Continuous Delivery. אמנם קיימים מונחים בעברית (ע״ע ויקיפדיה) אבל היות ואלו מונחים מקצועיים, מרבית האנשים משתמשים בהם באנגלית. את יתר המונחים נשתדל להציג באנגלית אבל גם להשתמש בחלופות העבריות שלהם.
אין מחסור בתוכן המסביר מהם Continuous Integration, Continuous Delivery ו-Continous Deployment. אבל לשם מה קיימים התהליכים האלו מלכתחילה?
חשוב להבין מהן הבעיות אותן פותרים באמצעות CI ו-CD כדי להשתמש בהם בצורה הנכונה. הבנה זו תאפשר לצוותים שלכם לשפר תהליכים, ולהימנע מהשקעת משאבים ברדיפה אחר מדדים ״מגניבים״ שאינם מביאים שום תועלת לתהליך עצמו.
אם בצוות שלכם עובדים מספר מפתחים על אותו ה-repository (או repo בקיצור), קיים branch ראשי (ענף ראשי) שמחזיק את הגרסה העדכנית ביותר של הקוד המפותח. מפתחים עובדים על דברים שונים ב-branch-ים שונים, ברגע שאחד מהם סיים עם השינוי שלו, הוא ידחוף או יאחד (merge) אותו לענף הראשי. בסופו של דבר, כל הצוות ימשוך את השינוי הזה ויעבוד על בסיסו.
התרחיש שנרצה להימנע ממנו הוא שגרסה פגומה (faulty commit) תמצא את דרכה לענף הראשי. במונח פגומה אנו מתכוונים לקוד שלא מתקמפל או כזה שנמצא באפליקציה שלא רצה או אינה יציבה. מדוע? לא בגלל שהאפליקציה תקולה או בגלל שכל הבדיקות חייבות לעבור ולהיות ירוקות. בכך איך בעיה, הרי תוכלו לא לפרוס (deploy) אותה ולהמתין לתיקון.
הבעיה היא שכל הצוות שלכם תקוע. כלל המפתחים שמשכו את הגרסה הפגומה יבזבזו 5 דקות בניסיון להבין למה היא לא עובדת. חלקם כנראה ינסו למצוא את ה-commit הספציפי שגרם לה, אחרים ינסו לתקן את הבעיה בעצמם במקביל למפתח הקוד התקול עצמו.
מדובר בבזבוז זמן עבור הצוות. החלק הגרוע ביותר הוא שמקרים חוזרים גורמים לחוסר אמון בענף הראשי ומעודדים כל מפתח לעבוד בנפרד.
מהות ה-Continuous Integration במניעת מצב שבו הענף המרכזי שבור, כך שהצוות לא תקוע ויכול לעבוד. זהו. לא מדובר במצב שבו כל הבדיקות עוברות בהצלחה כל הזמן והענף המרכזי ניתן לפריסה ל-Production בכל commit.
תהליך ה-Continuous Integration אינו תלוי בשום כלי ספציפי. תוכלו לוודא כי ה-merge שלכם לענף המרכזי פועל בצורה ידנית ולדאוג שהוא עובד מקומית, ורק לאחר מכן באמת לבצע איחוד ל-repo. כמובן שגישה כזו רחוקה מלהיות יעילה, ולכן CI ממומש באמצעות בדיקות אוטומטיות.
• שהאפליקציה תעבור build ותהיה ניתנת להרצה
• מרבית היכולות הקריטיות יעבדו בכל זמן נתון (לדוגמא הרשמה/login של משתמש, ויכולות עסקיות מרכזיות)
• שכבות משותפות של האפליקציה שכל המפתחים מתבססים עליהן עובדות בצורה יציבה. משמעות הדבר היא שקיימות בדיקות יחידה והן עוברות בהצלחה
בפועל, המשמעות היא שעליכם להשתמש ב-framework שמתאים לכם עבור בדיקות היחידה ולאבטח באמצעותן את החלקים המשותפים באפליקציה. לעיתים לא מדובר בהרבה קוד ואפשר לעשות כך בזריזות יחסית. בנוסף, עליכם להוסיף בדיקת עשן (smoke test) המוודאת שהקוד מתקמפל והאפליקציה עולה. נקודה זו חשובה במיוחד כאשר מדובר בטכנולוגיות המזריקות תלויות רבות כדוגמת Java Spring או .NET Core בפרויקטים גדולים, קל מאוד לטעות בתלויות כך שבדיקה שהאפליקציה עולה (לפחות) היא הכרח/חובה.
אם יש לכם מאות או אלפי בדיקות כנראה שאין חובה להריץ את כולן כנגד כל merge, מדובר בבזבוז זמן ומרביתן כנראה לא מוודאות יכולות שהן בגדר ״חוסמות״ לכל הצוות.
בחלקים הבאים נראה כיצד תהליך ה-Continuous Delivery עושה שימוש חיובי בבדיקות המרובות הללו.
כלים ובדיקות אוטומטיים הם בסדר גמור. אבל אם המפתחים שלכם רק מבצעים merge של ענפים ענקיים, עליהם הם עובדים במשך שבועות, הכלים והבדיקות לא יעזרו לכם. הצוות ישקיע כמות מכובדת של זמן בביצוע merge-ים של הענפים ותיקון חוסר התאמות בקוד שיצוצו בסופו של דבר. מדובר בזבוז זמן דומה לחסימה עקב גרסה פגומה.
ב-Continuous Integration לא מדובר בכלים, אלא בעבודה בפיסות קטנות ובאינטגרציה של הקוד החדש לענף המרכזי ומשיכה ממנו בתדירות גבוהה.
תדירות גבוהה משמעה לפחות על בסיסי יומי. פצלו את המשימה עליה אתם עובדים למשימות קטנות יותר, בצעו merge לעיתים קרובות ומשכו גם כן. בצורה זו, אין אדם שעובד בנפרד יותר מאשר יום או יומיים ולבעיות אין מספיק זמן לגדול כמו כדור שלג.
משימה גדולה לא צריכה להיות מוכלת כולה בענף אחד, בעצם אסור שתהיה. טכניקות לאיחוד עבודה שטרם הסתיימה לענף המרכזי מכונות ״branching by abstraction״ ו-“feature toggles”. תוכלו לקרוא עליהן במאמרים הבאים.
פשוט מאוד – קצר. טווח של 3 עד 7 דקות צריך להיות המקסימלי. לא מדובר במעבדים ומשאבים. מדובר ב-productivity של מפתחים. הכלל הראשון בפרודוקטיביות הוא מיקוד. עשו דבר אחד, סיימו איתו, עברו לדבר הבא.
החלפות הקשר (context switching) הן יקרות. מחקרים מראים שלוקח כ-23 דקות כדי להתמקד מחדש במשהו אחרי הפערה. דמיינו שביצעתם merge לענף שלכם ועברת למשימה אחרת. השקעתם 15-20 דקות להיכנס לעניינים, רגע אחרי שנכנסתם ל-zone ופתאום מגיעה התראת “build failed” מבילד ה-CI של המשימה הקודמת, תהליך בילד שלוקח 20 דקות. חזרתם לנסות ולתקן אותו, ביצעתם עדכון ועוד push. בקלות איבדתם יותר מ-20 דקות במעבר הזה.
הכפילו 20 דקות פעם או פעמיים ביום בכמות המפתחים בצוות שלכם... מדובר בבזבוז זמן גדול ויקר.
עכשיו, דמיינו שהמשוב מגיע תוך שלוש דקות, ושאתם יודעים שכך המצב. כנראה שלא הייתם מתחילים במשימה החדשה כלל. כנראה שהייתם עוברים על הקוד שלכם פעם נוספת, או אפילו עוברים על pull request (PR) בזמן ההמתנה. ההתראה אודות הכישלון הייתה מגיעה, הייתם מתקנים אותה ועוברים למשימה הבאה. זהו בדיוק המיקוד אותו התהליך שלכם צריך לאפשר.
שמירה על בילד CI קצר היא גם מעין פשרה. בדיקות הרצות זמן ארוך יותר או מספקות מעט ערך בהקשר ה-CI צריכות לעבור לשלב ה-CD. וכן, כישלונות בשלב הזה גם דורשות תיקון. אבל היות והן לא מונעות מאחרים לעשות את עבודתם, ניתן לקחת את התיקונים כ״משימה הבאה״ אחרי שתסיימו את מה שאתם כרגע עובדים עלו. פשוט כבו את ההתראות בזמן העבודה ובדקו מדי פעם. שמרו על מספר מינימלי של context switching.
בואו נתחיל בתיאום הפרשנות למושגים לפני שנמשיך. כאשר מדברים על Continuous Delivery מתכוונים ליכולת לפרוס כל אחת מגרסאות הקוד שלכם בכל זמן נתון. בפועל מדובר בגרסה האחרונה או זו שלפניה. לרוב, לא פורסים גרסאות בצורה אוטומטית כי אינכם חייבים בכך או עקב הגבלות במחזור חיי הפרויקט. אך ברגע שמתחשק למישהו לעשות כך, פריסה יכולה להיות מבוצעת במינימום זמן. המישהו הזה יכול להיות צוות הבדיקה/QA שרוצים לבדוק דברים בסביבות staging או pre-production. או אפילו כאשר הגיע הזמן לשחרר גרסה ל-production.
הרעיון ב-Continuous Delivery הוא להכין artefacts שהם קרובים ככל הניתן למה שהייתם רוצים להריץ בסביבה שלכם. אלו יכולים להיות קבצי jar או war אם אתם עובדים עם Java, קבצי הרצה אם מדובר ב-.NET, קבצי JavaScript או אפילו קונטיינרים שלDocker , כל דבר המקצר זמני פריסה (זאת אומרת, דברים שהכנתם מראש).
בהכנת artefacts אנו לא מתכוונים להפיכת קוד ל-artefacts. לרוב מדובר בכמה סקריפטים וכמה דקות של הרצה. הכנה משמעה:
הרצת כל הבדיקות האפשריות המבטיחות שהקוד הנפרס באמת יעבוד. הרצת בדיקות יחידה, בדיקות אינטגרציה, בדיקות end-to-end ואפילו בדיקות ביצועים אם יש באפשרותכם לבצע אוטומציה שלהן.
בצורה זו, מתאפשר לכם לסנן אילו גירסאות של הענף המרכזי שלי באמת מוכנות ל-production ואילו לא. חבילת הבדיקות האידאלית:
• מבטיחה שהפונקציונליות המרכזיות באפליקציה עובדות, אידאלית כולן
• מבטיחה שלא הוכנסה פגיעה רצינית בביצועי המערכת עם הגעת הגירסה החדשה שלכם למשתמשים רבים
• בדיקה ״יבשה״ (Dry Run) של שדרוגי מסד הנתונים הנדרשים על ידי הגירסה החדשה
החבילה לא צריכה להיות מאוד מהירה, חצי שעה עד שעה הם זמנים מתקבלים על הדעת.
השלב הבא הוא Continuous Deployment. פריסה של הגירסה העדכנית ביותר (והמוכנה ל-production) של הקוד שלכם, לסביבה כלשהי. אידאלית לסביבת production אם אתם סומכים על חבילת בדיקות ה-CD שלכם בצורה מספקת.
שימו לב שבתלות בהקשר, לא תמיד אפשרי או שווה להשקיע את המאמץ. Continuous Delivery לעיתים רבות מספיק כדי להיות פרודוקטיביים. בייחוד אם אתם עובדים ברשת סגורה ומספר סביבות מצומצם לפרוס אליהן. בנוסף, ייתכן שלוח הזמנים של שחרורי התוכנה שלכם (release lifecycle) מונע פריסות לא מתוכננות.
Continuous Delivery ו-Continuous Deployment (בוא נקרא להם CD מעתה) הם לא בעיות צוותיות. מדובר במציאת האיזון הנכון בין זמני הרצה, מאמצי תחזוקה ורלוונטיות של חבילות הבדיקות לסמן ש״הגירסה עובדת כצפוי״, ומדובר באיזון. אם הבדיקות שלכם רצות 30 שעות – זו בעיה. קראו את המאמר הזה כדי להבין כיצד נראית חבילת הבדיקות של מסד הנתונים של אורקל. אם אתם משקיעים כל כך הרבה זמן בשמירה על בדיקות עדכניות לקוד העדכני ביותר שלכם – זו בעיה שמעכבת את התקדמות הצוות וגם לא מהווה סימן חיובי. ואם חבילת הבדיקות שלכם כמעט ולא מבטיחה כלום... היא בעצם חסרת תועלת.
בעולם אידאלי היינו רוצים סט אחד של artefacts הניתנים לפריסה עבור כל commit לענף המרכזי. ניתן להבחין אם כך בבעיית scalability אנכית: ככל שנעבור מהר יותר מקוד ל-artefacts, כך נהיה מוכנים יותר לפריסת הגירסה העדכנית ביותר של הקוד.
עם Continuous Integration מדובר בבעיית scalability אופקית. אתם מעוניינים שהמפתחים שלכם יבצעו merge לעיתים קרובות כך שהבדיקות צריכות להיות מהירות. אידאלית תוך דקות בודדות כדי שהמפתחים ימנעו מ-context switching כל הזמן עם משוב אסינכרוני מתהליכי ה-build של ה-CI.
ככל שיש לכם יותר מפתחים, כך גדלה עוצמת המחשוב הנדרשת להרצה של בדיקות פשוטות (build ו-test) על גבי כל הענפים הפעילים.
בילד CI טוב:
• מבטיח שאין פיסת הקוד בענף המרכזי ששוברת יכולות בסיסיות של המערכת והמונעת מחברי צוות אחרי לעבוד
• מהיר מספיק כדי לספק משוב למפתחים תוך דקות, כדי למנוע context switching בין משימות
Continuous Delivery ו-Deployment, שתיהם מהווים בעיות scalability אנכיות, כאשר ישנה פעולה מורכבת למדי לביצוע.
בילד CD טוב:
• מבטיח שמספר גדול ככל האפשר של יכולות פועלות בנדרש
• מהיר ככל האפשר, אבל לא מדובר בעניין של מהירות. 30-60 דקות של בילד הן סבירות לגמרי.
תפיסה שגויה נפוצה למדי היא לראות ב-CD כבעיית scalability אופקית, בדומה ל-CI: ככל שעוברים מהם יותר מקוד ל-artefacts, כך עולה מספר ה-commit-ים שניתן באמת לעבד, וכל מתקרבים לתרחיש האידאלי. אבל אנו לא צריכים את זה, ייצור artefacts עבור כל commit מהר ככל האפשר הוא לרוב מוגזם. ניתן לגשת ל-CD על בסיס best effort: שימוש בבילד CD יחיד שפשוט בוחר את ה-commit האחרון כדי לוודא כאשר בילד מסויים מסתיים.
אל תטעו ותחשבו ש-CD הוא קל. הגעה למצב של ביטחון מספיק במערכת הבדיקות כדי להגיד שהתוכנה שלכם מוכנה לפריסה לרוב עובדת רק באפליקציות עם שטח פנים נמוך (כגון מערכות שבעיקרן הן API או בעלות ממשק מאוד פשוט). מאוד קשה להשיג זאת במערכת בעלת ממשק מסובך או מערכת מונוליטית גדולה.
הכלים והעקרונות לביצוע של CI ו-CD הם לרוב דומים, המטרות שונות מאוד.
Continuous Integration הוא איזון בין מהירות המשוב למפתחים והרלוונטיות של הבדיקות המבוצעות (build ו-test). אף פיסת קוד המונעת מחברי הצוות לעבוד לא מגיעה לענף המרכזי.
Continuous Delivery של Deployment משמעו הרצת בדיקות יסודיות ככל הניתן כדי לתפוס בעיות בקוד. שלמות הבדיקות היא הגורם החשוב ביותר. לרוב, היא נמדדת במונחים של code coverage או functional coverage של הבדיקות. תפיסת שגיאות בשלב מוקדם מונעת מקוד פגום להיפרש לסביבות כלשהן וחוסכת זמן יקר מצוות הבדיקות שלכם.
בנו את הבילדים של ה-CI וה-CD להשיג את היעדים האלו כדי לשמור על הצוות שלכם פרודוקטיבי. אין workflow מושלם, בעיות עלולות לצוץ מדי פעם. השתמשו בהן כשיעורים כדי לחזק את ה-workflow כאשר הן צצות.
הודעתך לא התקבלה - נסה שוב מאוחר יותר
Oops! Something went wrong while submitting the form