2019-04-10

בחירות בתוכנה: איך לבחור נכון?

הנה דילמה יומיומית, שבטח אתם מכירים:

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

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


האם נכון ״להרכין את הראש״, ולהכניס את הפיצ׳ר תוך יצירת עיוות במערכת?

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





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

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



איך בוחרים?


כמו בבחירות חשובות אחרות, גם כאן יש הרבה גורמים משפיעים שלא את כולם ניתן ״להחזיק בראש״ ולשקלל.

את איכות הבחירה - נוכל לקבוע הרבה פעמים רק זמן מה בדיעבד אחרי שהבחירה נעשתה. אאוץ!

"אם מדובר במודול קריטי של בקרה על כור גרעיני - אסור להתפשר בכלום על האיכות!" היא דוגמה מלומדת ששמעתי כמה פעמים בהקשר זה. מעולם לא פגשתי במתכנת שפיתח מודול קריטי של מערכת שתלויים בתקינותה חיים רבים. למתכנתים הללו יש כנראה guideline דיי ברור. מה איתנו, כל שאר 99.999% מאנשי התוכנה?

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

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

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

עיוותים במודל של המערכת הם לא סתם לא-אסתטיים. הם מדרדרים את המערכת למקום בו שינויים הופכים קשים יותר וההתקדמות - אטית יותר ומרובת-תקלות. אף אחד חוץ מהמהנדסים לא יחוש בהתדרדרות ויבין אותה - עד שכבר נהיה ב Technical Debt עמוק ומשמעותי.


אז מה עושים?
איך אתם לוקחים החלטה שכזו?

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

התשובה היא כמובן: It depends.

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

זה תלוי ברוח הארגון, וב DNA שלו: ישנם ארגונים, גם לא קטנים, המקדשים את ההתקדמות והיכולת לבצע שינויים, גם במחיר סיכונים מסוימים. "Move fast and break things".
הערה: אני פותח פה פתח ל Self-Suggestion לקורא שרוצה להימנע מעימותים ופשוט יאמר לעצמו "אצלנו ב DNA הארגוני רוצים רק פיצ'רים - אז אני אזרום עם ה DNA הארגוני". אני לא רוצה לעודד סוג כזה של מחשבה - אלא שתחשבו מה נכון לארגון שלכם באמת. עד כמה הדחיפה לפיצ'רים באמת עובדת טוב, וכמה זמן פיתוח באמת "שוקע" בגלל עיוותים במערכת.

זה תלוי בגודל העיוות: אי אפשר לכמת עיוות, אבל אפשר לומר ש"לשמור גם אובייקט Y בטבלה שנועדה לשמור אובייקטים מסוג X" [א] הוא ככל הנראה עיוות חמור יותר מלהחזיר null או 1- בכדי לחוות על מצב לא-תקין ב API (פעם זה היה best practice, אפילו).

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


חטאים...

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

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

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


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

חשוב שיהיה לכם יחס טוב בין Signal / Noise. אם על כל ארבע אזהרות שלכם - רק אחת מתממשת, הייתי אומר שזה המקום לצמצם קצת באזהרות. לעצמי אני אומר שלא הייתי רוצה לעלות על שלוש אזהרות לכל אחת שמתממשת. זהו כמובן Trade-off - בחרו לעצמכם את היחס שמתאים לכם - אבל זה בהחלט יחס שיש לחשוב עליו ולקחת אותו בחשבון.



סיכום


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

אז מה אני מציע לעשות כאשר יש דילמה בין הוספת פי'צר למערכת במחיר עיוות למבנה המערכת?

  • זכרו שלא סתם מוסיפים את העיוות הזה. הוא כנראה פותר משהו מאוד קונרטי - ולכן רוצים לעשות אותו. 
    • אם זה לא המצב, אלא סתם חוסר הבנה - ברור שאין טעם ליצור סתם עיוותים במערכת. אתם בעד החברה שאתם עובדים בה, לא?!
  • זכרו שבתיאור השפעות העיוות אתם "חוזים את העתיד" בכמות ההשפעה השלילית שתהיה לו. דאגו לבצע כיול-עצמי של ההערכות שלכם. אם אתם רואים שאתם מעריכים אסונות (מאטפורה: תאונה קשה לרכב), אבל בפועל זה נגמר בחוסר אסתטיות (מטאפורה: שריטה קלה לרכב שבקושי רואים) - כיילו את עצמכם, והיו יותר שמרנים בהערכות.
  • זכרו גם ששמירה על ההיגיון במבנה המערכת הוא באחריותכם! פתרון קל הוא לזוז הצידה בכל פעם שנעשה עיוות במערכת - למה להתעמת עם אנשים שיש עליהם לחץ לדלוור פיצ'ר?
  • כאשר אתם מזהים עיוות שנוצר, אל תשמרו את זה לעצמכם. העלו את זה. בסלאק, בשיחה, כדרך אגב - או כנקודה חשובה. חשוב לתת הזדמנות טובה לאנשים מסביב, גם מנהלים ואנשי ביזנס, לקלוט את הסימנים להתדרדרות ההתאמה בין המבנה המערכת לצרכים המשתנים (שזה תהליך טבעי). זה יקל עליכם בעתיד לבצע שינויים - וסביר יותר שתזכו לתמיכה.
  • פעלו כציידים, ולא כניצודים. צייד מתכונן, וכשמגיע עדר באפלו לשטח שלו - הוא יכול לנצל את הרגע ולצוד היטב. הניצוד - מופתע ומגיב בבהלה.
    • אתם רוצים שתהיה לכם רשימה של עיוותים גדולים במערכת והדרכים לצמצם / לתקן אותם - בצורה יעילה. כשתצוץ הזדמנות ("אוי - התפנה שבוע עבודה") תוכלו לנצל אותה ("אוי! זה זמן מצוין לטפל בבעיה של ה<משהו>").
  • כאשר תיקנתם עיוות, ושיפרתם באופן משמעותי אזור במערכת או אופן העבודה איתו - פרסמו את זה. וודאו שהשינוי ניכר. זה ייתן לכם ולאחרים יותר מוטיבציה להמשיך ולאושש את המערכת. לצמצם ולטפל בעיוותים.

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


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



----

[א] נתקלתי במקרה כזה כי רצו לחסוך יצירה של טבלה בבסיס הנתונים (?!). כמה שבועות מאוחר יותר הבינו שזה היה חיסכון דבילי (לא קיבלו את דעתי הראשונית) - ויצרו לאובייקט Y טבלה משלו.

[ב] נקודתית, בשעתו - לא יודע כיצד הוא עכשיו.


2019-03-16

קוברנטיס: Deployments ו ReplicaSets


בפוסט הקודם על קוברנטיס, הגדרנו Pod - יחידת ההרצה הקטנה ביותר בקוברנטיס.
סיימנו בכך ש Pod שנוצר בגפו, מה שקרוי ״Naked Pod״ - ואיננו בעל שרידות גבוהה: אם הוא קרס, או ה node שבו הוא רץ קרס / נסגר - קוברנטיס לא יחדש אותו.

בפוסט הזה נציג סמנטיקה מרכזית נוספת בעבודה עם קוברנטיס: ה Deployment - המכמיסה בתוכה סמנטיקה בשם ReplicaSet אותה נסביר גם כן. הסמנטיקות הללו הן מה שמאפשרות להגדיר/לעדכן pods כך שיהיו resilient. זוהי תכונה קריטית שאנו מקבלים מקוברנטיס, ולא היינו מקבלים מ Dokcer לבדו.

בואו נתחיל.




Resilient Pods



בכדי ליהנות מיכולת מתקדמות של קוברנטיס כגון Self-Healing ו Auto-Scaling - עלינו להימנע מהגדרה של naked pods, ולהשתמש בסמנטיקה בשם ReplicaSet.

ReplicaSet הוא המקבילה של AWS Auto-Scaling-Group, מנגנון המוודא שמספר ה Pods שרצים:
  • לא יורד ממספר מסוים (Auto-Healing)
  • עולה בצורה דינאמית כאשר יש עומס על ה Pods (למשל: CPU utilization מעל 60%) - עד גבול מסוים שהגדרנו, ויורד בחזרה - כאשר העומס חולף.
למי שעבד עם שירותי-ענן, הצורך הזה אמור להיות ברור כשמש.

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


תרשים קונספטואלי ולא מדויק. נדייק אותו בהמשך.
מה Deployment מוסיף על ReplicaSet? במחשבה ראשונה נשמע שאנו זקוקים רק ל ReplicaSet.
ובכן, בואו ניקח תסריט מאוד נפוץ ויומיומי: עדכון Pod לגרסה חדשה יותר. ביצענו שינוי במיקרו-שירות הרץ כ Pod, ואנו רוצים לעדכן את הקוד שלו.
כפי שאנחנו זוכרים, צורת העבודה (המומלצת) עם קוברנטיס היא הגדרה דקלרטיבית:
  • אנו מגדירים מצב רצוי
  • קוברנטיס דוגם כל הזמן את המצב המערכת, ואם יש פעם בין המצב הנוכחי למצב הרצוי - היא פועלת לסגירת הפער. 
נניח שיש לנו 3 Pod replicas של מיקרו-שירות, הרצים כולם בגרסה 11 (למשל: build number). אנו רוצים לעדכן אותם לגרסה 12.

אם פשוט נעדכן את קוברניס שאנו רוצים את ה Pod replicas בגרסה 12, עלול לקרות המקרה המאוד-לא-רצוי הבא:
  1. קוברנטיס רואה שלא רוצים יותר את גרסה 11 - הוא מכבה את כל ה Pod replicas בגרסה הזו.
  2. לא טוב! מרגע זה אנחנו ב downtime.
  3. קוברנטיס רואה שרוצים את גרסה 12 - הוא מתחיל להריץ Pod replicas של הגרסה הזו.
  4. אבוי! בגרסה 12 יש באג בקוד, והמיקרו-שירות לא מצליח לרוץ.
  5. קוברנטיס יוצר Log מפורט וברור של הבעיה - אבל עד שלא נטפל בה בעצמנו - אנחנו ב Full Downtime.

זה בהחלט לא תסריט שאנו רוצים שיהיה אפשרי בסביבת Production!


Deployment, לשמחתנו, מוסיף את היכולות / האחריות הבאה:
  • ביצוע סדר הפעולות בצורה נכונה - כך שתמיד יהיו מספיק Pods שרצים.
  • בחינת (probing) ה Pod replicas החדשים - ו rollback במקרה של בעיה.
    • למשל: בדוק שה pod replica החדש שעלה הוא תקין (ע״פ תנאים מסוימים, מה שנקרא Readiness check) - לפני שאתה מעלה עוד pod replicas מהגרסה החדשה או מוריד עוד ישנים.
    • ישנן מגוון הגדרות בכדי לשלוט בהתנהגות המדויקת.

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




קונספט ארכיטקטוני מרכזי בקוברנטיס הוא ה Controllers, שהם בעצם מעין Modules או Plug-Ins של סביבת הניהול של קוברנטיס (ה Control Plane). ה Controllers מאזינים ל API Server הפנימי של קוברנטיס אחר שינויים על ה Resource הרלוונטי להם (Deployment, Service, וכו') או שינויים בדרישות - ואז מחילים את שינויים נדרשים.

בעיקרון ה Controllers רצים ב Reconciliation loop (כמו event loop) שנקרא כך בגלל שכל פעם שיש אירוע (שינוי שנדרש) הם מבצעים פעולה על מנת "ליישר קו" (reconcile) וכך, לעתים בצעדים, ה loop דואג שכל הזמן המצב בשטח יגיע במהרה למצב שהוגדר. מדי כמה זמן הם בודקים את סה"כ ההגדרות ומגיבים לפער - אם קיים. כלומר: גם אם event מסוים פוספס / לא טופל - תוך זמן קצר ה controller יגלה את הפער וישלים אותו.


בניגוד להפשטה המוצגת בתרשים למעלה, Controllers לעולם לא יתקשרו ישירות אחד עם השני (אחרת: הם לא יהיו Pluggable). כל קריאה היא ל API Server, למשל "צור משאב מסוג Replication", שבתורו יפעיל אירוע מתאים שייקלט ע"י ה Replication Controller ויוביל לפעולה.

אנחנו נראה בהמשך את ההגדרות של ה Deployment ואת ה template המדובר.

הנה תיאור דינאמי של תהליך ה deployment:



ה Deployment ישמור את ה replica set הקודם (ועליו כל ההגדרות), על מנת לאפשר תהליך של Rollback.
כמובן שההתנהגות המדויקת של שלב ה Mid (ליתר דיוק: סדרת שלבי ה mid) תלויה מאוד באסטרטגיית ה Deployment שנבחרת.

אני מניח שאתם מכירים את 2 האסטרטגיות המרכזיות, המקובלות בכלל בתעשייה: Rolling deployment ו Blue/Green deployment.
קוברנטיס לא תומך היום (בעת כתיבת הפוסט) ב Blue/Green deployments כיכולת-ליבה (אם כי יש מדריכים שמראים כיצד ניתן להשיג זאת, על בסיס יכולות קיימות של קוברנטיס - הנה דוגמה לאחד).

בכלל, כל נושא ה deployments הוא GA רק מגרסה 1.9 (תחילת 2018). כיום קוברנטיס תומך רק באסטרטגיות: "Recreate" (אסטרטגיה סופר פשוטה, בה יש downtime בהגדרה) ו "RollingUpdate" (ברירת המחדל).

הנה שני מקורות, המציגים כ"א כמה תסריטי deployment אפשריים, בהתבסס על יכולות הליבה של קוברנטיס:
  • מצגת של Cloud Native Computing Foundation (בקיצור: CNCF) - הארגון שהוקם על מנת לקחת אחריות על פרויקט קוברנטיס, ולנהל אותו כ Open Source בלתי-תלוי. יש גם סיכום one-pager של האסטרטגיות שהוא מציג. 
  • פוסט של ארגון בשם Container Solutions - שגם הוא מציג בצורה ויזואלית יפה, את משמעות האסטרטגיות השונות (סט דומה, אך לא זהה לקודם).



הגדרת Deployment בפועל


בלי לקשקש הרבה, נגדיר manifest של deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-deploy
  labels:
    app: hello-world-deployment
spec:
  replicas: 4
  selector:
    matchLabels:
      app: hello-world
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  minReadySeconds: 5
  template:
    metadata:
      labels:
        app: hello-world
        env: dev
        version: v1
    spec:
      containers:
      - name: hello-pod
        image: hello-world:latest
        ports:
        - containerPort: 8080
          protocol: TCP
אתם כבר אמורים להכיר את 3 המפתחות הראשונים, ולכן נתמקד ב spec:
  • את הגדרת ה pod ניתן למצוא תחת ה template והן זהות ל Pod שהגדרנו בפוסט הקודם (מלבד label אחד שהוספנו). שימו לב שה metadata של ה Pod מופיע תחת template, ולא במפתח ה metadata של ה manifest.
  • replicas הוא מספר העותקים של ה pod שאנו רוצים להריץ. 
  • ה selector משמש לזיהוי חד משמעי של סוג ה pod שאנו מתארים. חשוב שניתן label "יציב" שבעזרתו קוברנטיס יידע לקשר ולהשוות אם היו שינויים בין pod templates. אם היה שינוי - עליו לבצע deploy.
    • למשל: אם שינינו גם את label הגרסה וגם את ה image - איך אפשר לקשר בוודאות שמדובר באותו האפליקציה, ולא בחדשה?
    • מקובל להשתמש ב label בשם app או app.kubernetes.io/name. 
  • על אסטרטגיית ה deployment כבר דיברנו. הנה 2 פרמטרים חשובים של אסטרטגית rolling deployment:
    • maxUnavailable - הכמות המרבית המותרת של pods שאינם זמינים תוך כדי פעולת deploy. הדבר משפיע כמה pods קוברנטיס יכול "להוריד במכה" כאשר הוא מתקין גרסה חדשה. המספר מתייחס למספר ה pods התקינים בגרסה הישנה + החדשה ביחד, וברירת המחדל היא 25%. ניתן גם לקבוע 0.
    • maxSurge - הוא פחות או יותר הפרמטר ההופכי: כמה pods חדשים ניתן להרים "במכה". ככל שהמספר גדול יותר - כך ה rolling deployment עשוי להיות מהיר יותר. גם כאן ברירת המחדל היא 25%.
      בקיצור גמור: אם יש לנו 4 pod replicas, הערכים שקבענו ב manifest יבטיחו שה cluster תמיד יכיל בין 3 ל 5 pod replicas בזמן deployment.
  • minReadySeconds - שדה רשות (ערך ברירת מחדל = 0) שמציין כמה שניות לחכות לאחר שה pod מוכן ("ready") לפני שמעבירים לו תעבורה. ההמתנה הזו היא פרקטיקה מקובלת, מכיוון שהנזק מ pod בעייתי שמחובר ל production traffic - עשוי להיות משמעותי. אפשר להיתקל גם בערכים של 20 ו 30 שניות. חשוב להזכיר שערך גבוה יאט את תהליך ה rolling deployment מכיוון שאנו ממתינים ל minReadySeconds - לפני שאנו ממשיכים להחליף עוד pods.
    • כאן שווה להזכיר את ה Readiness & Liveliness Probes של קוברנטיס. קוברנטיס מריץ ב nodes רכיב טכני הרץ כ container ומבצע בדיקות Health-check על ה pods השונים ב node ומדווח את התוצאות הלאה. כל pod צריך לענות ל2 קריאות: well-known/live./ ו well-known/ready./
      • מצב ה live הוא אות חיים בסיסי. המימוש המומלץ הוא פשוט להחזיר HTTP 200 OK מבלי לבצע פעולות נוספות. אם התשובה המתקבלת היא לא 2xx - קוברנטיס יאתחל את ה pod הזה מיד.
      • מצב ה ready אמור להיות עשיר יותר, בד"כ בודקים גישה לבסיס הנתונים ("SELECT 1") או גישה למשאבים קריטיים אחרים ל Pod (למשל: גישה לרדיס, או שירותים אחרים הקריטיים לפעילות ה pod). אם האפליקציה עוברת עדכון (למשל: הטמעת קונפיגורציה חדשה / עדכון caches ארוך) - הדרך הנכונה היא להגדיר אותה כ "לא ready" בזמן הזה.
        אם התשובה ל ready היא שלילית, קוברנטיס עשויה לנתק אותו מתעבורה נכנסת עד שיסתדר. אם המצב מתמשך (ברירת המחדל = 3 כישלונות רצופים) - ה pod יעבור restart.
        • שגיאה נפוצה היא להגדיר את live ו ready אותו הדבר - אבל אז מערבבים פעולות live יקרות מדי, ו restarts מיותרים של pods (כי עשינו restart בעקבות live אחד שכשל, נניח - מתקלת רשת נקודתית ואקראית).





את המניפסט, כמובן, מחילים כרגיל:
$ kubectl apply -f my-deployment.yaml
נוכל לעקוב אחר מצב ה deployment בעזרת הפקודה:

$ kubectl get deployment hello-deploy
אם אנו מגלים שהגרסה לא טובה (נניח: עלה באג חדש ומשמעותי לפרודקשיין), אנו יכולים לבצע rollback. הפקודה:
$ kubectl rollout history deployment hello-deploy
תציג לנו רשימה של revisions של ה deployment. נניח שאנו רוצים לחזור לגרסה 1, עלינו לפקוד:
$ kubectl rollout undo deployment hello-deploy --to-revision=1
כאשר מדובר ב RollingDeployment, ה rollback כמובן הוא deploy בפני עצמו שייקח זמן, ויעבוד לפי אותם הכללים של deploy חדש. תאורטית אנו יכולים פשוט לשנות את ה deployment.yaml בחזרה  למצב הקודם ולבצע deploy חדש - אי כי זה פחות מומלץ, אפילו רק מטעמי תיעוד.



סיכום


סקרנו את משאבי ה ReplicaSet וה Deployment בקוברנטיס - הדרך העיקרית לעדכן את המערכת ב pods חדשים / מעודכנים, בצורה Resilient.
על הדרך, הרחבנו מעט את ההבנה כיצד קוברנטיס עובד.

עדיין, לאחר שביצענו deployment לא נוכל לגשת ל pods שלנו (בקלות הרצויה). לשם כך עלינו להגדיר עוד משאב-ליבה בקוברנטיס בשם Service, המתייחס ל"קבוצה של Pod replicas בעלי אותו הממשק".

סמנטיקת ה Service מתוכננת להיות הנושא לפוסט הבא בסדרה.

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