2016-12-17

אז... אתם רוצים Continuous Deployment? (חלק ב')

בפוסט הקודם דיברנו על המוטיבציה ל CI/CD/CDD, על הציפיות והסיבות לאמץ את המתודולוגיות הללו, ותיארנו בפירוט מהו CI (הרעיון מעט שונה ממה שמקובל לחשוב).

בפוסט זה ארצה לפרט יותר על CD - ואולי גם כאן הרעיונות נבלעו תחת תפיסה פופולארית פשטנית.





Continuous Delivery (בקיצור: CD)


זהו הרעיון שקוד המערכת יהיה כל הזמן במצב ניתן לדילוור ("shippable state").

המונח CD התחיל להיות נפוץ בעקבות הספר הפופלארי של ג'ז האמבל ודייויד פארלי (למעלה באמצע).
המחברים לא המציאו את הרעיונות, אלא נתנו תוקף ומסגרת לתהליכים שהיו בשימוש ע"י חברות כמו Yahoo, אמזון, פייסבוק, גוגל, ועוד.

התרומה הראשונית של תנועת ה CD הייתה להחזיר את רעיון ה CI למרכז המפה, לא CI כסיסמה - אלא הבנה מעמיקה יותר של רעיון ה CI.

רבים בתעשייה הכריזו על עצמם כמיישמי CI - גם כאשר היישום לא היה יותר מ Build Server (או "CI Server") שמבצע Build בצורה תדירה. האינטגרציה עדיין נותרה אירוע של סוף ספרינט או רחוק מכך.
האם זו בורות, או פשוט גיוס של עוד מונח מקצועי לשיפור את המוטיבציה של העובדים ("תראו אותנו, אנחנו עושים CI, איזה מעולים!!")? - קשה לי לומר.

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

גם CD, באופן טבעי, הוא מוצהר יותר מאשר נעשה בפועל. זהו הטבע של התעשייה.


הצעד החשוב השני שתנועת ה CD עשתה היא להרחיב את מעגל האיטרציות ה SCRUM-י.
יישום נפוץ של סקראם (או מתודולוגיות אג'ייל דומות) בארגונים היה שצוותי הפיתוח עבדו באיטרציות קצרות - אך השלבים בתהליך שלפני הפיתוח (תכנון / אפיון מוצר) והשלבים שבאים אחריהם (בדיקות ו Release) - נותרו תהליכים ארוכים ולא גמישים.

ג'ז האמבל קרא למצב הזה Water-SCRUM-Fall, והוא מתואר בתרשים הבא:

מקור: ג'ז האמבל - http://www.slideshare.net/jezhumble/21-century-software

מי לא מכיר את המצב הזה? אני מניח שרובנו...


CD בה להרחיב את תחומי ה SCRUM (ליתר דיוק: האיטרציות הקצרות והתכופות) בארגון, ולכלול בתוכן את ה QA וה Release  מצד אחד, אך גם תכנון המוצר - מצד שני.

DevOps Culture - הוא אחד הרעיונות שצצו משם וקיבלו תשומת-לב מיוחדת. הרעיון שבמקום שיהיה "קיר" בין פיתוח ו Operations: פיתוח מייצרים משהו ו"זורקים" ל Operations להתקין בפרודקשיין - הקבוצות ישתלבו זו בזו.
במקום שיהיה ביניהם ניגוד אינטרסים מובנה (פיתוח לחוץ לפתח עוד פיצ'רים, Operations "אחראים" על יציבות המערכת ורוצים כמה שפחות שינויים) - הם עובדים בשיתוף פעולה, יד-ביד, למען מטרה משותפת.
האחריות למה שקורה בפרודקשיין - היא משותפת.

רבים בתעשייה לא הבינו את המסר בצורה נכונה: הם שינו את שם התפקיד מ "אנשי Operations" ל "אנשי DevOps" - והמשיכו בשכנוע-עצמי (או אולי: שכנוע-האחר) שהם התקדמו בצד משמעותי ליישום CD, או בכלל ברמה התהליכית...
זכרו: DevOps זה לא תפקיד - זוהי תרבות.


הספר Continuous Delivery יצא ב 2010, עוד לפני עידן ה SaaS. כלומר: לפני ש SaaS היה הצורה המקובלת לדלוור תוכנה. הספר מדבר בעיקר על Packaged Software - כזו שהלקוח מוריד את קובצי ההתקנה ומתקין בעצמו.

Case Study שג'ז האמבל אוהב להזכיר הוא הטמעת CD במחלקת המדפסות של חברת HP - כיצד לבצע CD ל firmware (הקוד שרץ בתוך המדפסת). זו דוגמה טובה לקוד שניתן לבנות אותו כל יום - אך עדכונים ישוחררו ללקוחות רק אחת לכמה חודשים (או אפילו פחות מכך).



יישום CD בצד הרעיוני


מדד-מפתח באימוץ של CD בארגון הוא ה Lead Time, אותו יש לשפר כל הזמן:


הזמן מרגע שאיש Product ביקש פיצ'ר, או מהזמן שנפתח באג - ועד שהוא מוכן וארוז לשימוש. ארוז ("Shippable") הכוונה שיש לנו את קובצי ההתקנה של המערכת, והם שמישים, בדוקים, ובאיכות טובה.

ההתמקדות ב Lead Time מספקת אופטימיזציה בארגון לתהליכים רבים: תקשורת, תעדוף, Build מהיר, handover ל QA, ותהליכי בדיקות אוטומטיים, ועוד. אם היינו מתמקדים רק ב Process Time, או רק בחלק הפיתוחי של ה Process Time - אולי הפיתוח היה עובד מהר יותר, אבל הפיצ'ר עדיין היה "תקוע":
"הפיצ'ר מוכן, אבל הוא עדיין מחכה רק ל QA/התקנה" או "אנחנו מוכנים להתחיל, אבל מחכים ל Product/עיצוב" - הוא תירוץ שמתכנתים מרגישים נוח איתו, אך הוא לא מספק את צורכי הביזנס.

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




עוד הבחנה ש CD מתמקד בה היא שהזמן שמושקע בפיתוח מתחלק לחלק נראה, ולבתי-נראה:



כשאנחנו עסוקים כמעט כולנו בפיתוח פיצ'רים, ולא מקדישים זמן לפיתוח הארכיטקטורה ותהליך ה CD שלנו - הזמן הישיר והעקיף שהולך ל Negative Value הולך וצומח - עד שאנחנו כמעט ונעצרים מהיכולת לפתח ערך חיובי.

Technical Debt, כפי שאנו יודעים, זהו סיבוך בקוד, שינויים חשובים ב Design שנדחו, קיצורי דרך לא אלגנטיים - כל אלה גורמים להכנסת כל קוד חדש / שינויי קוד להיות קשים וארוכים יותר. מן "מס בלתי נראה".

"בלתי נראה"? המפתח שעובד על שינויי הקוד מרגיש אותו היטב, אך קשה לשקף אותו לשאר הארגון.


הנה השיפורים שהושגו, בחניכה של ג'ז האמבל במחלקת ה Firmware (מדפסות) של HP:



האם CD מציע לנו עולם שבו 80% מהזמן מוקדש לכתיבת קוד חדש ופיצ'רים? - לא ולא. יש הרבה עבודת אוטומציה ואחזקה. אבל, במקרה הנ"ל הצליחו להגדיל פי 8* - את הזמן שגוף הפיתוח משקיע בפיתוחים חדשים ("Innovation").
ההערה למטה בלבן מסבירה ש 23% מהזמן של הל המפתחים הושקע בכתיבת אוטומציה. כמעט חצי מהזמן שהושקע בכתיבת קוד שנותן ערך לללקוח.

* טוב, זהו Case Study, ויש כל אינטרס להציג את התמונה בצורה אופטימית ככל האפשר. בואו נניח ששיפרו את הזמן שמושקע בקוד חדש - רק פי 4-5 בפועל.



יישום CD בצד הטכני


המפתח ליישום CD מההיבט הטכני הוא בניית ה Deployment Pipeline - הצינור בו מעבירים קוד ממערכת ה Source Control - לפרודקשיין.

כל שינוי ב Source Control מפעיל Trigger שגורם לבנייה של תוצר הקומפליציה. במקרה של פיתוח בענן (נניח AWS) - מדובר כנראה בבניה של ה AMI (קרי Amazon Machine Image) או Container, ממנו ניתן "להרים" מכונה בפרודקשיין בפקודה פשוטה. בשל הפופולאריות של Containers, אתייחס אליהם - אם כי ב Gett אני רגיל ביום-יום לעבוד עם AMIs.

נניח שאנו עובדים ב Github ויש לנו branch בשם master עליו מפתחים, ו branch בשם release שמתאר את מה שרץ בפרודקשיין.
חשוב להדגיש: הכוונה היא לא שנבצע merge ל release פעם בשבוע-שבועיים (או לסירוגין: בסוף ה Sprint) ואז יבנה ה Container. זה יהיה Automated Delivery - ולא Continuous Delivery!
הכוונה היא שבכל שינוי ב master (ואם יש כמה כאלה בטווח של דקה, אז אחת לכמה דקות) - ייבנה Container, הוא יופעל בסביבת ה Staging - ועליו המפתחים וה QA יבדקו את הקוד. סביבת ה Staging אמור לשקף את מצב הפיתוח הרגעי, כך שברגע שאנו מתכננים להשיק ("Ship") - יש לנו קוד במצב טוב.


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

זו הנקודה בה CD מסתמך על יישום של CI:
עלינו להוכיח שהקוד אכן מוכן לדילוור באיכות הרצויה, ועל כן אנו מעבירים אותו בסדרה של "מבחנים" - אלו הן התחנות השונות ב Deployment Pipeline. ככל שמבחן הוא מהיר יותר להרצה - הוא יהיה במיקום מוקדם יותר ב Deployment Pipeline. כלל שמבחן הוא יעיל יותר (סביר יותר שיתפוס בעיות) - הוא יהיה גם כן מוקדם יותר ב Deployment Pipeline.
כלומר: אנו מתחילים עם מבחנים זולים (= מהירים) ובעלי סיכוי גבוה לתפוס תקלות, למשל "Smoke Test" (= "מדליקים את המכשיר ורואים שלא יוצא עשן"), וככל שמתקדמים מגיעים למבחנים יקרים (= אטיים) יותר, ושבודקים יותר מקרי-קצה.

עדיין חשוב מאוד הכלל שאומר: שמירת ה Pipeline עובד היא בעדיפות עליונה. על הארגון לעזוב הכל ולטפל ב Pipeline שבור - ממש כמו שמטפלים ב Production.

זכרו: הטיפול ב Deployment Pipeline הוא משני בדחיפות רק ל Production.

התהליך שב Pipeline רץ כל הזמן, סביבות הבדיקות השונות אמורות לעבוד מסביב לשעון, לאורך כל הספרינט - ולא רק בסוף הספרינט (זו המשמעות של Continuous). ונכון: יהיו כמה גרסאות של שירותים במערכת (אני מניח שיש לכם Microservices) - וחלקן לא יגיע ל Production (אם בשל תקלה בבדיקות, או בשל כך שגרסה מתקדמת יותר "עקפה" אותה והגיעה ל Production לפניה).


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



נקודות חשובות:
  • את ה Isolated Testing Environment - מרימים לכל Service לדקות בודדות של בדיקה. לא אכפת לנו להפעיל כמה וכמה כאלו במקביל. וירטואליזציה או קונטיינריזציה - היא פלטפורמת ההרצה הטבעית לסביבה הזו.
  • ה Integration Environment היא סביבה יחידה - בה ניתן לראות את התנהגות כל השירותים במצב ה dev שלהם ביחד. 
    • ה Integration Environment דומה ככל האפשר לפרודשיין:
      • התקנה מבוצעת באותו האופן: clusters, מכונות נפרדות, DB נפרדים, קונפיגורציה - כמו בפרודקשין.
      • ה Data מגיע מפרודקשיין, אך עובר ניקוי למידע פרטי (שלא צריך להיות נגיש לעובדים), או מיותר (למשל: טבלאות audit גדולות).
    • לכאורה זו סביבה שעלולה להיות מאוד לא יציבה (גרסאות שונות ומשונות רצות ביחד), אבל עם התבגרות ה pipeline היציבות שלה אמורה להשתפר משמעותית:
      • קוד מגיע אליה לאחר שעבר כבר סבב בדיקות.
      • תאימות לאחור היא תנאי מחייב.
      • עדיף מאוד לבצע deploy ב immutable deployment (על מכונה "נקייה" ולא "מלוכלכת") - containers או AMIs מאוד עוזרים כאן. אחרת - חשוב מנגנון reset לסביבה, והוא כנראה יופעל מפעם לפעם.
      • כאשר יש התנגשות בין שירותים - אפשר להחזיר אחד השירותים לגרסה קודמת, אבל השאיפה היא להציע מהיר תיקון ולהתקדם קדימה. Rollback הוא פשרה.
  • ה Staging Environment מאפשרת לבצע בדיקות אינטגרציה בין שירות נתון - למצב שיש כרגע בפרודקשיין.
    • לא נדיר להגיע לסט בדיקות שהרצת כל הבדיקות שבו אורך שעות רבות (ראיתי גם יממה ויותר) - ועל כן לא מריצים את כל הבדיקות. חשוב לדעת לכל שירות איזה subset של הבדיקות הוא מספיק טוב. כלל אצבע שאני מכיר הוא לחתור ללא יותר מ 10 דקות של בדיקות. כלומר: ייתכן וסך כל הבדיקות אורכות שעתיים או שלוש (כי אנו מקפידים על שיפורי הזמנים: מסירים בדיקות לא יעילות, ועובדים לקצר בדיקות ארוכות) - אך אנו יודעים לסמן לכל שירות איזה סט של בדיקות שנכנס ב-10 דקות הוא ה 80/20.
      • לפני שינויים רגישים במערכת - אפשר לסמן ל pipeline להריץ את הסט המלא של הבדיקות. שייקח שלוש שעות. אני מדבר על אירוע שאמור לקרות פעם בחודש בערך. אם השינויים שאנו מבצעים בקוד הם קטנים - אז הם לא כ"כ מסוכנים.
    • סביבת Staging יחידה תיצור, מן הסתם, תור ארוך ב pipeline - כי מפתחים רוצים להכניס שינויים כל הזמן. בכדי להשיג מקביליות של עבודה - יהיו כמה סביבות Staging שכאלו: 2, 3, 5, 10 - תלוי בגודל הארגון. בדיקות מהירות מאפשרות לנו להחזיק בפחות סביבות Staging.
    • ה Staging Environment דומה ככל האפשר לפרודקשיין:
      • התקנה מבוצעת באותו האופן: clusters, מכונות נפרדות, DB נפרדים, קונפיגורציה - כמו בפרודקשין.
      • ה Data יהיה לרוב Data שהוכן מראש לבדיקות האוטומציה.

רק לאחר שכל התהליכים האוטומטים הסתיימו (Isolated + Stating Environments), והתאפשרו בדיקות ידניות (של מפתחים / QA) על סביבת ה Integration (או Staging - לבקשתם) - ניתן לבצע את ההחלטה "לשחרר ל Production".


אם אתם זוכרים את הפוסט הקודם, כך זה נראה בצורה הפשטנית:



ה Manual Trigger מגיע לאחר בדיקות שהן ידניות (כמובן שמישהו יכול להחליט לדלג עליהן במקרים מסוימים ולאשר ללא בדיקה). הוא לא נמצא שם סתם.

הנה תיאור קצת יותר מפורט של ה Pipeline שהראתי למעלה:


ליד כל שלב רשמתי זמן שנחשב סביר לתהליך. סה"כ, גם Pipelines יעילים אורכים כחצי שעה-שעה מרגע ה push לגיט - ועד לפעולה ב Production.

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

על כן נוסף שלב נוסף ב Pipeline בשם Rollback Test. הוא הולך בערך כך:
  • מתקינים את השירות בגרסה N (בצורה isolated) ומבצעים DB migration.
  • מבצעים Rollback: מתקינים את השירות מחדש בגרסה N-1 (ללא rollback ב DB).
  • מבצעים בדיקות Service/API של השירות בגרסה N-1 - לראות שהוא עובד בצורה תקינה.
כל התהליך הזה קורה במקביל לשאר התהליך והוא לא אמור להיכשל. אבל אם הוא נכשל - מונעים את היציאה של השירות הזה ל Production (עד שמתקנים את התאימות לאחור).

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

ה Pipeline אמור להשתנות לאורך הזמן ולהתאים את עצמו לצורכי הארגון: לפעמים להוסיף שלבים - ולעתים להסיר ממנו שלבים.


בהקמת ה Pipeline, וגם לאורך חייו, הארגון ישאל את עצמו שאלות שונות:
  • האם את הבדיקות הראשונות של המיקרו-שירות, לבצע במכונה בודדת (עם stubs) - או האם להרים landscape שלם? מישהו צריך לשלם על מכונות ה landscape - וזה ייקח יותר זמן.
  • כמה אנו מוכנים לחכות לבדיקות? יותר בדיקות = יותר בטחון, מצד שני - פחות יעילות וזמן תגובה לשינויים ב Production?
  • היכן לעשות בדיקות ידניות? בסביבת אינטגרציה - ואז לגלות בעיות אינטגרציה מוקדם יותר, או בסביבת Staging? ואז להגביר את הסיכוי למנוע את הבעיה הבאה בפרודקשיין? זו לא רק שאלה של עדיפות פילוסופית: פריון עבודה מול יציבות הפרודקשיין - זו שאלה כמה מנגנוני ה Rollback והבדיקות השונות יעילים, והיכן יש לפצות אותם?
  • האם להשקיע ב Load Tests כחלק מה Pipeline - או האם להסתמך על Canary Tests (בהמשך) עבור Load?
  • האם לבצע הרצה של כל הבדיקות האפשריות מדי פעם (על תצורה שהגיעה לפרודקשיין), גם אם סט הבדיקות לוקח שעות רבות? (פעמים רבות - עושים זאת).
  • ועוד ועוד...

שווה לציין באזז עכשווי בשם Pipeline as Code. מכיוון שה Pipeline הופך ונעשה מורכב, התחילה גישה בה ניתן "לתכנת" אותו בשפת תכנות ולא רק בקונפיגורציה.

את המונח Pipeline as Code ייתכן ותשמעו מהפרסומים של Jenkins 2.0 - שלכאורה עבר שינוי מהותי בכדי לתמוך ביכולות הללו. בפועל הוצגה מערכת Plugins שעדיין רחוקה מהאידאל של Pipeline as Code. מקביליות בהרצת בדיקות עדיין מבוססת בעיקרה על Plugins ואיננה יכולת ליבה של המערכת - וכך גם השגת הגמישות ב Pipeline עצמו. ניתן לומר (באופן מעט מרושע) ש Jenkins 2.0 היא מוצר Pipeline by Plugins.

Jenkins הוא עדיין כנראה הכלי הנפוץ ביותר לבניית Deployment Pipeline, אם כי כלים כמו Concourse CI או goCD ו gomatic - נחשבים למתאימים יותר לבניית ה Deployment Pipeline. ייתכן והגיע זמנו של Jenkins לפנות את כס המלכות.



עוד כמה רעיונות של CD שכדאי להכיר


Infrastructure as Code / Everything as Code - מכיוון שחשוב שהקונפיגורציה של המערכות תהיה זהה בין סביבות ה Staging וה Production, ומכיוון שחשוב שהקמת סביבה לפי אותה קונפיגורציה נתונה יהיה תהליך אוטומטי, מהיר, ואמין - הרעיון הוא שכל הגדרות המערכת, או מה שגורם להן יהיה מנוהל כמו קוד, ב Version Control System - אם אותה בקרה (למשל: pull requests) ותהליכים שמוכיחים את עצמם עבור קוד.
אלו חדשות ישנות כבר - אני מניח.


Blue/Green Deployment - הרעיון בו שחרור גרסה חדשה של שירות מבוצע באופן הבא:


תמיד מחזיקים ב Production שתי סטים של מכונות (או clusters): סט כחול וסט ירוק. כל סט מספיק גדול בכדי לשרת את כל התעבורה ב production בעצמו.
סט אחד תמיד יהיה עם גרסה N, והסט השני - עם גרסה N-1.

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

עכשיו פשוט נורה ל Load Balancer להעביר את כל התעבורה ל Set הכחול. זה תהליך של שניות - בד"כ. בקשה שכבר התחילה תסתיים במכונה הירוקה, אבל הבקשה הבאה - תגיע לסט המכונות הכחולות.

הכל טוב? הסט הכחול הפך להיות הסט הפעיל.
יש בעיה? ניתן לבצע Rollback בשניות ולהחזיר את התעבורה לסט הירוק.


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


Canary Release - הרעיון הזה הוא וריאציה של Blue/Green Deployment. 
הרעיון הוא לבצע את ההתקנה באותו האופן (נניח שהגרסה החדשה שהתקנתי היא על סט המכונות הכחולות), אבל להעביר רק כמות מזערית של תעבורה לסט הכחול: במקום 100% מהתעבורה - להעביר רק 1% מהתעבורה.

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

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


Dark Deployment - הרעיון כאן הוא שאין חובה ש Deployment יהיה גם בעצם Release.
פיצ'רים רבים במערכת - ניתן לשחרר אותם עם Feature Flag כבוי. הפיצ'ר הוא Deployed, אך לא Released.

למה לעשות את זה? בזמן הזה QA או המפתחים יכולים לבדוק את הפיצ'ר בפרודקשן, בסביבה הכי אמיתית עם תעבורה אמיתית. אפשר לעשות beta testing למשתמשים נבחרים - ולהכניס בפיצ'ר שיפורים, ורק כשהוא מוכן, בדוק, ויציב - לשחרר אותו.

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


סיכום


שוחחנו על CD לא מעט - זה נושא גדול, שנוטה להעסיק את הארגון רבות.
עדיין לא שוחחנו על איך מתחילים בהטמעה של CD בארגון שלא רגיל לכך, ולא על Continuous Deployment: האם זה באמת רק דילוג על שלב ה manual tests?


שיהיה בהצלחה!



2016-11-16

אז... אתם רוצים Continuous Deployment?

נתקלתי בארגונים שרצו לעשות Continuous Delivery (או Deployment, או אפילו Integration) משום שרצו להידמות לארגונים מצליחים אחרים. הייתה מן מחשבה מעורפלת שאם נעשה את "זה" - דברים יסתדרו טוב יותר, כל זאת ללא התעמקות אמיתית בהבנת התמורות הצפויות ממהלך שכזה.
המחשבה כנראה הולכת כך: "CD זה טוב => אנחנו רוצים להיות טובים יותר => בואו נעשה CD".
(ייתכן, כמובן, והייתה התעמקות - אך פשוט לא נחשפתי אליה.)

Continuous Delivery היא כבר שאיפה סטנדרטית: כמעט כולם רוצים להיות שם. כ"כ הרבה נמצאים "בתהליך...". כרגיל...

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






הפשט


בדומיין שאנו מדברים עליו יש 3 פרקטיקות דומיננטיות, שנהוג לפרש אותן בערך כך:
  • Continuous Integration (בקיצור CI) - יש לנו שרת Build עם בדיקות אוטומטיות. כל כמה זמן (לילה?) - כל הבדיקות רצות ואנו מגלים אם יש בעיות בקוד - ואז מתקנים.
  • Continuous Delivery (בקיצור CD) - יש לנו שרת Build אוטומטי, שגם אורז את הקוד ל Delivery. למשל: בונה את ה ISO (אם אנו מפיצים את המערכת על DVD - פחחח) או Docker Image - שניתן להטמיע בפרודקשיין (SaaS או On-Premises).
  • Continuous Deployment (בקיצור גם CD, לצורך הפוסט אשתמש בקיצור CDD) - כמו Continuous Delivery, אבל ההחלטה לשלוח ל Production היא אוטומטית. כל קוד שנכנס למערכת - מגיע ל Production. לכאורה, תוספת קטנה לתהליך.

התיאור הזה הוא נכון בגדול - אך גם שטחי למדי.


נכון, אך פשטני משהו...
מקור: notafactoryanymore.com



מוטיבציה - אז למה לנו CD עכשיו...?


CD אמור לשפר כמה מדדים במערת, הנה העיקריים:
  • צמצום אי-ודאות - היכולת לשחרר קוד מהר יותר, מספקת לנו פידבק מהיר ותדיר יותר מהשטח. ״לפגוע בקרקע בתדירות גבוהה יותר״. (רלוונטי בעיקר ל Continuous Deployment)
  • פישוט תהליכי ה Integration / Deploy - ארגונים רבים נאבקים לסיים גרסאות בזמן, ובאיכות טובה. CD היא דרך לשפר את התהליכים, בכמה צורות.
  • יציבות, או Uptime של המערכת בפרודקשיין.
איזה מדד הכי חשוב? - תלוי בארגון.


אני רוצה להרחיב מעט יותר על המוטיבציות:

צמצום אי-ודאות / סיכון

כשמנהלים פרויקט או מוצר בארגון, תמיד יש לנו חוסר ודאות מסוים: לפעמים הוא בינוני, ולפעמים - גדול.
  • חוסר ודאות מה הלקוחות צריכים -לפעמים הם בעצמם לא יודעים, ופשוט יש לראות כיצד הם משתמשים במוצר.
  • חוסר ודאות לגבי התוצאות של פעולות שנעשות - האם המעבר ל NoSQL DB יספק את ה Scalability הצפוי. האם ה Design החדש של מודול Y יעמוד יפה בשטף הדרישות העתידיות?
  • חוסר ודאות לגבי הבנת תוכנית הפעולה והתיאום בין אנשינו - כאשר האנשים שעובדים איתנו לא מבינים את אותה התוכנית - הם עלולים למשוך לכיוונים מנוגדים.
שווה לציין שהרעיון של Continuous Deployment קיבל תנופה גדולה מתנועת ה Lean Startup, שם צמצום מחזורי הלמידה והפידבק הם ערך מרכזי. הדרישה ל CDD, ע״פ המתודולוגיה, אמורה להגיע כיעד עסקי מה CEO - זה לא עניין ״פנימי״ של ה R&D...

במקום להיות ״נביאים״, או ״יודעי-כל״, תהליך ה CDD מאפשר לנו לנסות בשטח.
הפיצ'ר של אמזון של "אנשים שקנו את X קנו גם את Y", נדחה ע"י אנשי המוצר כ"מטופש" - ובארגון אחר היה נקבר לעד.

התרבות הארגונית של אמזון, שגובתה בעזרת יכולות CDD, אפשרה למפתח שמאוד מאמין במשהו לבצע A/B test (על אוכלוסיה קטנה) - ולנסות להוכיח את צדקתו. כל אחד כמעט - יכול להציע ולהתניע ניסוי.
הניסוי הנ״ל הראה גידול של כ 3% במכירות (נתון מרשים מאוד!) - מה שהסיר מייד כל התנגדות, והפך אותו בן רגע לפיצ׳ר רצוי ע"י הפרודקט.

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

CDD, אם כן, בשילוב A/B Tests ותרבות ארגונית מתאימה - מספקת Empowerment לעובדים, ויכולה להביא רעיונות חכמים שלהם - לידי יישום.

מיחסים ל CDD גם שיפור בשביעות הרצון של המפתחים, שכבני-אדם, גם הם צמאים לפידבק מהיר. מה יותר כיף: לפתח פיצ׳ר במשך שנה - ואם לראות אותו עובד (אם בכלל), או לראות שינוי בפרודקשיין כל שבוע-שבועיים? להרגיש את העשייה בצורה מתמשכת?



פישוט תהליכי ה Integration / Deploy

כשהחברה גדלה, תהליכי ה Integration וה Deploy הם לא פעם - כאב ראש רציני!

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

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

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

אין כאן קסם:

בתהליכי Integration/Deploy תקופתיים (נאמר: פעם בחודש) - משקיעים המון.
וגם בתהליכי CD - משקיעים המון.

התמורה היא שב CD הסיכויים להגיע לתהליך מוצלח - הם טובים יותר. במיוחד כאשר המערכת שלנו מחולקת למודולים נפרדים (למשל: microservices) ואז ניתן לייצר pipeline נפרד ומותאם לכל חלק במערכת (sub-system).


מקור: http://www.mindtheproduct.com


יציבות

לאחר מעבר מוצלח ל CD/CDD, המערכות נוטות להיות יציבות יותר.
זה לכאורה פרדוקס: שינוי בפרודקשיין הוא סיכון - והנה אנחנו הולכים לעשות הרבה יותר כאלה. במקום שינוי בחודש, אולי כמה שינויים כל יום!
מצד שני, כשהשינויים הם מאוד קטנים - הסיכון מאוד קטן, יכולת האיתור של תקלות משתפר פלאים, וכמו שאומרים: Practice Makes Perfect. הרבה אימון בתהליך מאפשר להשתפר בצורה מהירה יותר.

מודל היציבות של CD הוא MTTR (קרי: Mean Time To Resolve) על פני מודל של MTBF (כלומר: Mean Time between Failures). יהיו לנו יותר תקלות במספר - אך הן יתוקנו מהר, וסה״כ ה downtime שלנו יהיה נמוך יותר.

שאלה מפורסמת ב Lean היא: "כמה עבודה עליכם לעשות בכדי לבצע Deploy של שורת קוד בודדת במערכת?" (כלומר: נזניח את תוכן השינוי, ואז - מהי התקורה על כל deploy של גרסה?). התרגיל הזה ידוע גם כ Single Line of Code DevOps Kata.

בארגונים ללא CD, מדובר בד"כ על כמה ישיבות, עבודה ידנית של ביצוע רגרסיות ומעבר על התוכן, ואף כמה פעולות ידניות של איש Operations שמלווה את ה deploy. כמה שעות עבודה, וכמה דקות של פחד (במקרה הטוב).

אם אתם רוצים לעשות deploy כל יום, זה נראה כחוסר-יעילות, ולכן ה"Best Practice" שהיה מקובל במשך שנים הוא להפחית את מספר ה releases, או לפחות למתן אותו - נאמר, פעם או פעמיים בחודש, ולא יותר.

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

הגישה של CD נקראת: Bring the Pain forward (כמו באג'ייל).
אם זה כואב, עשו את זה בתכיפות רבה יותר - עד שתשתפרו וזה כבר לא יכאב.

בפועל, באימוץ CD אתם הולכים "לשבור דברים" כ"כ הרבה פעמים - כך שתוך כדי אתם צפויים לפתח יכולות Recovery טובות במיוחד ולשפר מאוד את ה MTTR. כמובן שחשוב מאוד לשבור דברים קטנים, ולהיות עם יכולת recovery כמעט מיידית, למשל: לסגור את ה feature flag או לחזור ל deployment הקודם תוך שניות (ע"י blue/green deployment - פרטים בהמשך).

המערכת לא תהיה יציבה יותר כי היא "תשבר פחות".
אם תציבו לארגון ה R&D מדד פנימי של "מינימום תקלות" - קרוב לוודאי שתהרגו את תהליך ה CD.

המדד צריך להיות: תוך כמה זמן אנו מתקלים תקלה, end-to-end: זיהוי התקלה עד אימות התיקון.

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






Continuous Integration (בקיצור: CI)


זהו הרעיון שקוד המערכת יהיה כל הזמן במצב תקין ("working state").

בניגוד למיתוס מקובל, המשמעות איננה שעליכם להתקין ולהגדיר מערכת של Jenkins או Cruise Control.
זכרו: הכלי - הוא החלק השולי. התהליך - הוא העיקר.

העיקרון הוא שאינטגרציה של קוד ("merge") הוא הכלי האולטימטיבי לזהות שבירות במערכת - ולכן עליכם לבצע אינטגרציה כל הזמן.

כפי שאמרו זאת מייסדי ה Extreme Programming (שהיו הראשונים לאמץ את הפרקטיקה בצורה רחבה):
"אם אינטגרציה של קוד היא טובה - עשו אינטגרציה של קוד כל הזמן."

המדד המקובל ליישום CI הוא:
כל מפתח, מבצע בכל יום עבודה - לפחות פעם אחת merge של הקוד שלו ל branch המרכזי. (ועדיף: חמש פעמים)


(רמז: Jenkins לא גורם לזה לקרות.)

שימו לב: זה לא שכל מפתח מושך את ה branch המרכזי (נקרא לו לצורך הפוסט: master) ועושה merge לתוך ה feature branch. זה פחות מועיל / מלמד על שברים. העניין הוא לבצע merge לתוך ה master בכל יום.

Feature Branches? לא כ"כ מומלצים, אלא אם אתם מקפידים לבצע merge ל master.
זה מחייב את המפתחים לחשוב ביחידות קטנות, לפרק את הפיצ'ר לשלבים קטנים (שזה טוב - בפני עצמו), ולדאוג כל הזמן שהקוד הנכתב - לא שובר את המערכת.

מה קורה עם קוד שמשנה חלק גדול במערכת? איך אפשר לעשות merge כאשר הקוד לא "מוכן"? - פשוט הכניסו feature flag, ואל תדליקו אותו עד שהקוד מוכן. תחשבו איזה יופי יהיה להדליק את הפיצ'ר רק עבור המשתמש שלכם בפרודקשיין - ולבדוק איך הפיצ'ר עובד בלייב עם traffic/data אמיתי!

העיקרון החשוב השני של CI הוא:
כאשר ה Build נשבר - לתקן אותו זו עדיפות עליונה של הארגון. לפני כל דבר אחר.

יעד מקובל הוא תיקון תוך 10 דקות - לרוב ע"י rollback.
אפילו יותר טוב: בדקו את ה merge בצד, ורק לאחר שהצליח - בצעו את ה merge בפועל. השיטה הזו עובדת יותר טוב כאשר יש code bases קטנים (למשל: microservices) ואין race condition על merges.

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

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


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


בדיקות יחידה (Unit-Tests) או אינטגרציה (Integration-Tests) הגיעו לתהליך ה CI מאוחר יותר - והפכו לחזות עיקר התהליך. אין ספק: הן חשובות מאוד, ומועילות מאוד. מה היה קודם לכן? קומפליציה + בדיקת המערכת end-to-end (שאורכת זמן רב, ותופסת רק חלק מסוים מהבעיות).

האם בדיקות אוטומטיות טובות מספיקות, ויכולות לייתר את הצורך ב merges תכופים?
אם אתם רואים שאצלכם זה עובד - אדרבא.

בד"כ השבירות בקוד (גם אם הן נתפסות ע"י Unit tests) מגיעות אחרי merges של קוד - ואתם לא רוצים "לחטוף אותן" בסוף הספרינט - חשוב לבצע אינטגרציה כל הזמן.

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

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


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


CI הוא האב הרוחני של CD, ולרוב מגיע איתו ביחד.
האם חובה ליישם CI לפני שמתחילים לעבוד על CD? - נדבר על זה בהמשך...



סיכום


בפוסט זה ניסיתי להסביר מה הציפיות הסבירות מ CI/CD/CDD - תהליך חשוב שלאט הולך ומוטמע באמת בעוד ועוד חברות. אימוץ תהליכים אלו הם לרוב מאמץ ארגוני אינטנסיבי של חודשים - אבל גם השיפור עשוי להיות משמעותי למדי.

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


שיהיה בהצלחה!