על DevOps וה DevOps toolkit

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

  • מפתחים אחראים לכתוב קוד
  • איש ה UX אחראי לעצב את חווית השימוש
  • ה DBA מתמחה ב Database Systems
  • ה QA בודק את המערכת
  • איש ה Operations אחראי להתקין את התוכנה ולנטר אותה
  • וכו'…

באופן טבעי, הושתת ניגוד-אינטרסים מובנה בין חלק מבעלי התפקידים:
  • איש ה UX רוצה להשיג את חווית השימוש הטובה ביותר – והמפתח רוצה לשחרר פיצ'רים מהר.
  • איש ה QA רוצה שלמות במוצר – והמפתח רוצה לשחרר פיצ'רים מהר.
  • איש ה Operations רוצה יציבות – והמפתח רוצה לשחרר פיצ'רים מהר.

שלא תבינו לא נכון: המפתח גם רוצה להשתמש בטכנולוגיות חדשות, לכתוב "קוד ספרותי", להתפתח, וללכת לים – אך האלמנט הדומיננטי שיוצר חיכוכים עם שאר בעלי התפקידים הוא הרצון של המפתח (ובמידה גדולה יותר: המנהל שלו) – לשחרר פיצ'רים מהר.   בשנים האחרונות, התרחשו שני שינויים במבנה הקלאסי של קבוצת הפיתוח:

שינוי #1: הגבולות בין אנשי ה QA והמפתחים החלו להיטשטש

בעקבות צירוף של אנשי ה QA "לצוותי סקראם" והגדרת מטרות משותפות ברמה צוותית – ניגוד-האינטרסים המובנה פחת: ה QA שותפים לזמני ה Delivery והמתפתחים אחראים יותר לאיכות.
מפתחים החלו להיות שותפים פעילים יותר בתהליכי האיכות (בדיקות יחידה, בדיקות אינטגרציה וכו') דבר שהן השפיע על הגישה שלהם לנושא וגם יצר שותפות-גורל גדולה יותר בין ה QA למפתחים.שינוי #2: המעבר לענן ואוטומציה של התפעול

המעבר לענן הסיר מאנשי ה Operations בארגון כמה עיסוקים הרחוקים מאוד מהמפתחים: טיפול בחומרה ורשתות תקשורת – עולם שלם בפני עצמו. את החומרה והרשת מנהלים בצורה אוטומטית בעזרת תוכנה (להלן Virtualization ו Software Defined Networks) וכך התקרב העולם של אנשי ה Operations לעולם המתכנתים – עולם של כתיבת סקריפטים ~= קוד.

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

היכולת של ה Operations להתמודד עם אתגרים אלו כקבוצה חיצונית לפיתוח – הפכה מורכבת יותר ויותר. חילופי ההאשמות והמתח בין הקבוצות רק הלך וגבר (להלן blaming culture).שינוי שאנו נמצאים במהלכו כיום נקרא The DevOps Movement – ההכרה שהדרך הטובה ביותר להתמודד עם סביבות המחשוב המורכבות שנוצרו – הוא בשיתוף פעולה אמיתי בין Operations ל Developers.

מקור: ThoughtWorks Radar – מארס 2012

כמובן שארגונים רבים לא מכירים בעומק הרעיונות שמאחורי תנועה זו, ופשוט משנים את ה title של אנשי ה Operations ל "DevOps" – שם פופולארי יותר.

מקור: ThoughtWorks Radar – נוב' 2015

עבודת ה Dev-Ops בארגון

בשלב הזה אני לסקור את העבודה שיש בפני ה Operations וה Dev (וגם קצת IT וגם קצת QA) בארגון. אני יכול להתחיל בהצהרות ברמה גבוהה "לדאוג ליציבות המערכת". הצהרות אלו הן נכונות – הן אם לא תמיד כ"כ מוחשיות. מה כן מוחשי? כלים ותהליכים. אספתי את סוגי הכלים והתהליכים הקיימים בעולם Dev-Ops לכדי רשימה, ואני הולך לפרט לגבי כל קטגוריה בנפרד. אני אציג תמונה (/ "סיפור") מציאותית – אך כמובן לא יחידה: ניתן לתאר את התהליכים הללו בצורות שונות, ולאנשים שונים וארגונים שונים יש תפיסות שונות לגבי גבולות הגזרה בין התהליכים / הכלים / ותחומי-האחריות השונים. אני לוקח כמה הנחות בסט הכלים שאראה:

  • אני מניח שמדובר ב Deployment בענן, ולא ב On-Premises (קרי: אין ניהול של חומרה ורשת פיסיים) – ואני מתמקד ב AWS (עננים אחרים הם דיי דומים בהיבט זה).
  • אני מניח שמדובר באפליקציית ווב.
  • אני מניח ש Docker (או פתרון אחר של Linux Containers) אינו מעורב – אחרת התמונה תשנה במידת-מה.
אם יצאתם זה עתה מהאוניברסיטה, או אי בודד – פוסט זה יכול לספק תמונה לא רעה על תהליכי פיתוח בקבוצות-תוכנה בתעשיה.
אם אתם כבר בתעשיה – אני מקווה שתמונה זו תוכל להשלים "חורים" שאולי חסרים לכם.
המורכבות של Stack מודרני, מול Stack בן עשור. מקור: https://goo.gl/9yCLtp

לרבים מההיבטים שאתאר (למשל: Logging, Version Control, וכו') יש היבטים ברורים מאוד של פיתוח: קביעת conventions לעבודה, למשל –  בד בבד עם היבטים של תפעול (התקנה ותחזוק של השרתים, גיבויים וכו').

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

כלים "פנימיים"

אלו הכלים שלרוב משמשים את הפיתוח. מישהו צריך לנהל ולתפעל אותם. ה"מישהו" הזה, הוא הרבה פעמים צוות ה IT) Operations). בסטארט-אפים קטנים – הרבה פעמים האלמנטים הללו מנוהלים ע"י המתכנתים עצמם.

דוגמאות לכמה כלים בכל קטגוריה

Issue Tracking

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

יש כאן ניגוד אינטרסים קטן: מי שמביט על כלל הפרויקט (מנהל פרויקטים, מנהל פיתוח) מעוניין לאסוף כמה שיותר פרטים על כל משימה – עבור יכולות הניתוח, בעוד מי שעובד עם המערכת (מתכנתים) מעדיף להתעסק עם הכלי כמה שפחות – כי בסוף העיסוק ב Issue Tracking לא מייצר Value ללקוחות.
Jira (ויש מתכנתים האומרים: ״Jifa״) הוא כנראה הכלי הנפוץ ביותר, שמכסה גם היבטים של ניהול פרויקטים ובאגים – אך יש גם כלים אחרים.

Version Control

בסטארט-אפים מקובל בעיקר לעבוד עם Github.com – ולקבל משם שירותים מנוהלים של ניהול קוד.
ארגונים גדולים, יותר מקובל לעבוד עם Git ישירות או עם GitHub Enterprise (או החלופות שלו) – תוכנה שיש להתקין ולנהל לבד, עם היכולת לאבטח בצורה יותר טובה.

מלבד ניהול השרת של ה Version Control, יש עניין של ניהול התוכנה עצמה: ניהול ה Repositories, וההרשאות (האם אתם בודקים כל תקופה שעובדים שעזבו הוסרו מהמערכת?), הכשרה לעובדים (לא כולם מכירים את Git), ופתרון בעיות ("אופס… עשיתי force– למשהו שגוי….").

Build

את הקוד צריך לקמפל (במקרה של Java, Go, #C, וכו') – או שלא (במקרה של רובי, Python, או ג'אווהסקריפט).
לרוב יהיו לנו בדיקות (יחידה, אינטגרציה, ביצועים, אבטחה) אוטומטיות – שירוצו כחלק מתהליך ה Build.
תוצרי הקוד (תמונות, קבצי JavaScript, קבצים בינאריים) לעתים קרובות עוברים עיבוד נוסף: דחיסה / אופטימיזציה – לתמונות, minification / obfuscation ו unification לקבצי ג'אווהסקריפט, או bundling לקבצים בינאריים (למשל: אריזת קבצי jar. כקובץ war. או ear. – בעולם הג'אווה).

את תהליך ה Build לרוב מריצים על כל submit / push של קוד ל Version Control – על מנת לאתר תקלות בשלב מוקדם ככל האפשר. באופן זה העלות לתקן את הבעיות – פחותה.

בטעות, יש כאלו שקוראים לתהליך ה Build בשם "Continuous Integration", ולשרת ה Build – כ "CI Server". זה שימוש שגוי בטרמינולוגיה: Continuous Integration הוא תהליך בו מגבירים את תדירות האינטגרציה בין המפתחים השונים. למשל: כל מפתח מבצע merge של הקוד שלו חזרה ל master / trunk – כל יום!

ו Git Flow? – הוא כבר לא כ"כ באופנה. זו בערך הגישה ההפוכה לגישה של Continuous Integration.
בעולם ה Build יש כלים ברמות שונות: Jenkins או TravisCI הם Build Servers שמנהלים את ה Build Pipeline.
כלים כמו Maven, SBT, Ant, או Gradle (כולם מעולם הג'אווה) – הם כלים להגדרת המיקרו של ה build (וניהול תלויות). דומים יותר ל Make או Rake.
בעולם הג'אווהסקריפט משימות מסוימות מבוצעות עם כלים כמו Grunt, Gulp, או npm – ואת משימת האריזה עם כלים כמו webpack או Browserify.

Artifact Management (בקיצור AM)

תוכנת AM היא כזו שעוזרת לנהל תוצרי תהליך ה-build (למשל: קבצים בינאריים, Gem Files, או Images של VM) בצורה: מסודרת, מאובטחת, Highly Available, ו Durable (כלומר: יש גיבויים).

תהליך ה Build, מרכזי או כזה שנעשה על המחשב של המפתח, ישלוף חלק מהתוצרים ב AM – במקום לייצר אותם מחדש.

AM יכולים לנהל תוצרים פנימיים שנוצרו ע"י הארגון, או Caching לתוצרים חיצוניים כמו ספריות Open Source (בכדי לקצר זמני הורדה שלהם ברשת). הרבה פעמים יהיו בארגון כמה כלי AMs שונים (למשל: תוצרי ג'אווה או רובי, תוצרי AWS, ותוצרי Linux).

Development Environment Management 

אין מה לעשות: בכדי לפתח בצורה יעילה – אנו זקוקים לסביבות שאפשר לנסות עליהן הכל, ואפשר "לחרב" אותן מבלי לחשוש.
הרבה פעמים אנו רוצים בסביבות הללו נתונים (data) מציאותיים, קרי – עותק של חלק מהנתונים מ production, כאשר הם מצומצמים ומנוקים (sanitized) ממידע רגיש (למשל: כתובות אימייל של משתמשים אמיתיים). ההכנה של הסביבות הללו – היא עבודה לא קטנה.

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

Collaboration

Collaboration טוב נוצר קודם כל ע"י אנשים – ולא כלים. בכל זאת – הכלים יכולים לעזור.
מישהו צריך להתקין ולנהל את כלי ה Collaboration הללו. את התוכן, באופן טבעי – בדרך כלל ינהלו המתכנתים.

כלל קטן: אם אנשי ה Operations מתחזקים את כלי ה Collaboration אך לא משתמשים בהם – אין לכם תרבות של DevOps בארגון. שקיפות ושיתוף בין פיתוח ו Operations – זה הבסיס.

כלי "Production"

את הכלים הללו לרוב מפעיל צוות שנקרא בארגונים גדולים בשם ״Web Operations״. זו נחשבת התמחות שונה מצווות ה IT Operations.

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

כשעבדתי ב SAP למשל, צוות ה IT Operations ישב איתנו ונקרא "DevOps". הם ניהלו את כל הכלים הפנימיים, הדריכו ותמכו במפתחים וממש עזרו להם. הצוות הזה הכיר Git, Jira, ו Maven – יותר טוב מכולם.
צוות ה Web Operations נקרא "Hosting", ישב בגרמניה והיה מאוד מרוחק מאיתנו – פיסית ומנטלית. אם השתקפה בעיה ב Monitoring – מישהו היה מתקשר, אבל מעבר לזה הקשר ביננו היה חלש, ולא היה הרבה מה לעשות בנידון. זה היה תת-ארגון אחר. לכלי ה Monitoring למשל, לא הייתה לנו גישה. אפילו לא לצפייה. ממש לא DevOps Culture…

דוגמאות לכמה כלים בכל קטגוריה

 Web/HTTP

בקטגוריה זו יש לרוב יהיה שימוש ב 2 או 3 מתוך תתי הקטגוריות הבאות:

  1. HTTP Server – עבור הגשת Static Content ו/או ניהול התעבורה עבור Technology Stack שאינו multi-threaded מטבעו. כלומר: PHP, Ruby, Python. הכלים הנפוצים בקטגוריה זו היא Nginx ו Apache httpd.
  2. Application Server – סביבת ריצה לאפליקציות ווב. בעולם הרובי Applications Servers נפוצים הם Puma, Unicorn, ו Raptor. בעולם הג'אווה הם יכולים להיות Tomcat, Jetty, או Glassfish.
  3. Content Delivery Network (בקיצר CDN) – שהיא יעילה אף יותר מ HTTP Server בהגשת Static Content, אך יש לעתים סיבות להשתמש בשניהם.
כלים אלו משפיעים במידה ניכרת על ה Traffic / התנהגות המערכת. כנראה שתרצו מישהו בארגון שמבין בהם, יידע לקנפג אותם, לנטר אותם, ולאתר תקלות בצורה מהירה – ולא תמיד אלו יהיו המפתחים.

Configuration Management

כלי ניהול תצורה הם סיפור בפני עצמו. הקדשתי להם לאחרונה פוסט בשם כלי ניהול תצורה – מבראשית ועד ימי ה Docker.

Packaging

ה Packaging מטפל באריזת ה VM Image שממנו ניצור instances של מכונות בענן.
אם אתם עובדים בענן כלשהו (אמזון, Azure, או אפילו VMWare) – סביבת הענן תאפשר לכם לייצר Image ממכונה.

כלים כמו boxfuse או packer מאפשרים לכם לייצר מאותה המכונה Images מסוגים שונים (למשל: אחד ל Azure ואחד ל AWS במקביל), וגם ליצור Images קטנים ויעילים יותר (ע"י זיהוי התלויות הנדרשות ובניית "סביבת מערכת הפעלה" מספיקה לצרכים הספציפיים).
Image מוקטן שכזה מתאים ל Immutable Deployments (להלן הפוסט על כלי ניהול תצורה), ומגיע על חשבון החופש להתקין בקלות עוד כלים על המכונה במהלך הדרך.

למרות שאני מציג את ה Packaging כקטגוריה בלתי-תלויה בכלי ניהול תצורה – לרוב משלבים בתהליך ה Packaging כלי ניהול תצורה, שילוב שלרוב הוא מובנה בכלי ה Packaging.

Deployment

"מה הבעיה לעשות Deployment?!" – היא השאלה הראשונה כאשר עוסקים בקטגוריה זו.
"אני פשוט הולך ל UI של אמזון, בוחר image, ומאשר – תוך 2 וחצי דקות השרת למעלה".

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

  • כיצד להתקין שרתים חדשים מבלי לפגוע ב Traffic הקיים? לא Downtime ולא שיהוק.
  • אם אנו עושים זאת בהדרגה, כיצד מתמודדים עם מצב הביניים בו יש כמה שרתים בגרסה n, וכמה שרתים בגרסה n-1 החיים זה לצד זה?
  • מכיוון שמדובר ב cluster, כיצד עושים זאת עם כל הגדרות ה Load Balances, רשת, אבטחה – ומבלי להתבלבל. ה UI הרגיל של אמזון כבר לא כ"כ טוב לבעיה הזו.
  • מה עושים במצב של כשל? מתי מזהים אותו וכיצד – ואיך עושים Rollbacl? (לא נחכה לתיקון קוד עם בעיה חמורה בפרודקשיין, ולא משנה עד כמה המתכנתים שלנו זריזים).

בגדול, יש שתי "אסטרטגיות: עיקריות לביצוע Deployment:

  • Rolling Deployment
    • התהליך (נניח שהשרת שלנו רץ על m nodes):
      • ניקח node אחד ואז:
        • ננתק אותו מה LB
        • נוודא שכל ה requests שבתהליך סיימו להיות מטופלים
        • נתקין את הגרסה החדשה
        • נחבר אותו בחזרה ל LB וננטר אותו לזמן קצר לראות שהכל בסדר.
      • נחזור על כל התהליך הזה עוד כ m-1 פעמים.
    • ל Rolling Deployments יש ייתרון של חסכון במשאבים (צריך עוד שרת אחד, אפילו אם יש לנו 100 nodes), אבל הוא יכול להיות תהליך ארוך – ו rollback הוא קשה וארוך גם כן.
  • Blue/Green Deployment
    • התהליך (נניח שהשרת שלנו רץ על m nodes – "ה cluster הכחול"):
      • ניצור cluster חדש ("ירוק") זהה בחומרה ל cluster המקורי – ונתקין עליו את הגרסה החדשה.
      • ננתב חלק קטן מה traffic ל cluster החדש וננטר לזמן קצר. הכל בסדר? נחליף ברגע אחד את ה cluster הירוק להיות ה cluster הפעיל.
        • ניתוב ה Traffic יכול להיעשות באסטרטגיה של DNS cutover, באמזון – זמינה גם אסטרטגיה בשם swap auto-scaling groups, ויש גם אסטרטגיות אחרות.
      • ה cluster הכחול ימשיך לחיות עוד זמן מה (החיוב באמזון למכונות הוא ע"פ שעה עגולה של שימוש). אם נגלה בעיות בגרסה החדשה נוכל לחזור ל cluster הכחול – ברגע.
    • Blue/Green Deployment דורש יותר משאבים – אך הוא בטוח יותר, ומאפשר Rollback מהיר.
האם אתם רוצים לבצע בשטח את אסטרטגיית ה Deployment בצורה ידנית? קרוב לוודאי שלא, שתעדיפו לעשות את התהליך בעזרת כלי שיצמצם משמעותית את טעויות האנוש ויקצר את התהליך.לרוב ספקי הענן יש כלים משלהם לפעולות Deployment (ובכלל ניהול ה Deployment Pipeline) – אך הכלים החיצוניים עדיין נחשבים טובים יותר.

הערה: Jenkins הוא לא כלי מוצלח לניהול Deployment. נכון: גרסה 2.0 שלו הציגה קונספט של "Pipeline as code", אך קונספט זה ממשיך לעקוב אחר המודל הקיים והסבוך של pipelines של Jenkins והגמישות בו מבוססת על Plugins.

Jenkins הוא סבבה כנראה ל build pipeline, כך בכל הקשור ל deployment – מומלץ להיצמד לכלים ייעודיים.
כדאי להכיר: אלו סט הכלים של HashiCorp – ארגון מוערך מאוד שהוציא כמה כלים מאוד מעניינים (דגש על Vagrant ו Consul) בתחום. חשוב גם להיזהר מהנטייה האישית "לאסוף סריות": לא כל כלי של HashiCorp הוא בהכרח מתאים לכם. בוודאי שלא!
RDBMS Migration
אם אתם זוכרים – בבסיסי נתונים רלציוניים (וגם כמה אחרים), ישנה סכמה מוגדרת מראש של הנתונים. סביר שחלק מהעדכונים של התוכנה יידרשו שינוי סכמה בבסיס הנתונים.להזכיר: בזמן ה Deployments (אם רוצים zero downtime) יהיו שלבים בהם ירוץ קוד חדש וישן זה לצד זה – ושניהם מול בסיס נתונים אחד (זה לא ריאלי בד"כ לרפלק את בסיס הנתונים לצורך deployment. יותר מדי סיכונים והשקעה).

ניתן לבחור ב-2 גישות:
  • קוד תואם לאחר שיתמוך ב-2 הסכמות (השקעה גדולה). את המיגרציה ניתן לעשות לאחר עדכון הקוד (סיכון).
  • סכמה תואמת לאחור – כלומר רק תוספות ולא מחיקות או שינויים (הגישה הנפוצה). את המיגרציה יש לעשות לפני עדכון הקוד, בשלב מוקדם של ה deployment.
עבור migrations יש כלים רבים. חלקים מגיעים כחלק מה Framework (כמו ב !Ruby On Rails, Play, או Django). ישנם כלים כמו Flyway או Liquibase שאינם תלויים בשפת תכנות או Framework ספציפי.ה Migration עצמו הוא סכנה ליציבות המערכת: תקלה בו יכולה להיות בעיה קשה.

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

כלים כמו LHM או Table_migrator מצמצמים סיכונים ע"י העתקת הטבלה הצידה, ביצוע ה migration, ואז החלפתה (rename) – קונספט דומה ל Blue/Green Deployment – רק שצריך לנהל delta של השינויים שלא נכנסו לטבלאת העותק בזמן ה migration – ולעדכן אותם מאוחר יותר.

Monitoring

אין דיי לומר על עולם זה, ה Monitoring הוא גם עין ימין וגם עין שמאל שלנו – על סביבת הפרודקשיין.
פעם, כשחדר השרתים היה במשרד היה ניתן לקפוץ אליו – ולשמוע את המאוורים מסתובבים. היום גם את זה אי אפשר ;-).

כלי ה Monitoring הם אלמנט חשוב לתפעול, ולרוב יהיה לנו Monitoring בכמה רמות שונות (מערכת, אפליקציה, Proactive Monitoring, וכו'.) – ייתכן וכל רמה בכלי שונה.

כתבתי כבר בעבר פוסט על New Relic – הכלי האהוב עלי בתחום: על Performance Monitoring ו New Relic.
כתבתי גם פוסט בשם Monitoring: מבוא ל Graphite ושימוש ב Time-Series – המתאר כלים ל custom monitoring (בהיבט של time series).

תת-קטגוריה אחת ששווה להזכיר היא נושא ה Alerting – שליחת אימייל / SMS / או טלפון (עם הודעה קולית) לאנשים הרלוונטיים (ע"פ תורניות), ועל סמך הצטברות מדדים כאלו או אחרים ממערכות ה Monitoring. כלי כזה לדוגמה הוא PagerDuty.

תת-קטגוריה קרובה אחרת היא הנושא של Event Management: בהינתן מדדים כאלו ואחרים – הפעל response שנקבע מראש (ונבדק). למשל: restart ל service, הריגת טרנזקציה מסוימת ב DB, הגדלת מספר השרתים, וכו'.

Logging

לכתוב מהקוד ללוג – זו לרוב פעולה פשוטה למדי: בוחרים ספריית Logging, מסכימים על כמה כללים – וכותבים.

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

הכלים הנפוצים לניהול לוגים הם ה ELK Stack (קיצור של ElasticSearch, LogStash, ו Kibana) או לחלופין Splunk או SumoLogic (שלא כוללים את איסוף הלוגים – LogStash או מוצרים חלופיים).

יש גם תת קטגוריה של Error Tracking – שהם כלים ייחודיים לניתוח תקלות משמעותיות במערכת (מול לוגים – שכוללים את כל המידע וגם info, traces, וכו'). כלים מוכרים בקטגוריה זו הם Airbrake, Sentry, ו Raygun.

יכולות ייחודיות לכלים אלו הם סנכרון עם עדכוני קונפיגורציה / תוכנה – וניתוח ה impacts שלהם מבחינת צפיפות ה errors, חיתוך ע"פ id של משתשמש (שאנחנו סיפקנו) בכדי לנסות ולאתר מדוע שימוש מסוים גורר בעיות – וכו'.

Security

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

  • מתחילים עם Firewall – שבדרך כלל ספק הענן יספק.
  • אם המערכת שלכם עובדת על גבי HTTP, קרוב לוודאי Web Application Firewall (בקיצור WAF) – כמו Cloudflare, אינקפסולה, או BigIP (של F5).
  • דרך כלים ל Authentication ו/או Federated Identity Management (בקיצור FIM) כמו PingIdentity, OKTA, או Auth0.
  • כלי Intrusion Detection System (בקיצור: IDS) – כמו Snort, או Trend Micro's Deep Security.
  • כלים ל audit בגישה ל Production כמו ObserveIt או CyberArk.
  • כלי SIEM – כלי לניהול לוגים בהיבט האבטחה, כלים כמו ArcSight, QRadar, או Splunk (שהוא גם "חזק" בעולם ה Security).
  • כלים ל Security Static Code Analysis המחברים לתהליך ה build כגון Checkmarx או Fortify.
  • ועוד…

בארגונים יותר גדולים, כל הנושא של כלי ה Security מופקד ביחיד צוות Operations מיוחד, שמתמחה בנושא.
בארגונים קטנים יותר – זה היה צוות ה Web Operations או אפילו המפתחים.

לכאורה, ניתן לכתוב פוסט מקביל לזה – רק בהיבט האבטחה.

סיכום

עוד פוסט שחששתי שלא יהיה לי מה לכתוב בו – אך התארך מעבר הצפוי 🙂
נושא ה Operations וה Production הוא עולם בלתי נגמר.

תנועת ה DevOps שמשפיעה על כלל התעשיה (שלב א': שינוי ה Title של אנשי ה Operations – אך יש גם את השלבים הבאים) – משנה את העולם.

עוד כוח שמשנה את התמונה הוא טכנולוגיית ה Linux Containers (ובחזיתה: Docker), גישה החותרת ל Fully Immutable Deployment – גישה שעשויה לשנות כמה מהתהליכים ויחסי-הכוחות בעולם זה.
תוצר לוואי של גישת ה Containers היא החתירה ליצירת כלי Infrastructure Management חדשים (כמו Mesos, Tectonic, או Atlas), שלא מבוססים על ספקי הענן  – מה שעלול לגרום לעוד כמה תזוזות.

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

לינקים רלוונטיים

DevOps Awesome Links – כמה קישורים מעניינים!

DevOps From the ground up Deep Dive into Blue/Green Deployments on AWS – על אסטרטגיות Deployment.

ההבדל בין Packer ל Vagrant – מאמר + דברים של מיטשל האשימוטו (Hashi)

Cybersecurity Market Review – סוקר הרבה כלי Security במגוון קטגוריות

AWS: "השירותים הפשוטים" של אמזון – חלק ב', וארכיטקטורה מונחית-אירועים

בפוסט הקודם כיסינו שני "שירותים אפליקטיביים" בסיסיים של AWS: שירות ההודעות SNS, ושירות התורים SQS.
בפוסט הזה נמשיך את הסקירה ונכסה את שירות ה Workflows שנקרא SWF – שירות שימושי, אך קצת פחות מוכר.

Simple WorkFlow Service (בקיצור SWF)

SWF בא לפתור לנו כמה בעיות שלא נפתרות ב SQS או SNS. למשל:

  • נניח שיש לי סדרה של הודעות בין שירותים הקריטיות לביזנס, שעלינו לוודא שכולן טופלו בהצלחה + לדעת מתי כולן הסתיימו.
    כיצד מתמודדים עם משהו שכזה בעזרת SQS? מחזיקים הודעות אימות ואז סורקים אותן כל כמה זמן?! – לא יעיל.
  • נניח שיש לי מקביליות-חלקית ברצף של הודעות שבהן ארצה לטפל: חלק מההודעות עלי לשלוח בזו אחר זו, אבל חלק ניתן לשלוח במקביל – ולקצר את התהליך.
    כאשר אני שולח הודעות ב SNS או SQS  אין לי שליטה מה קורה איתן, ובטח שאינני יכול לתזמן את הטיפול בין הודעות שונות….
הפתרון הפשוט לבעיה זו הוא לנהל רצפי-הודעות שכאלה בצורה ריכוזית של תקשורת "הודעה-תשובה" בקוד של שירות כלשהו. למשל שירות 'A':

  • שירות A שולח את הודעה 1 לטיפול ומחכה שתסתיים.
  • שירות A שולח את הודעות 2, 3, ו 4 לטיפול במקביל (משיקולי ביצועים). הוא מממש Pattern של Monitor – שנותן אות ברגע שהטיפול בשלושת ההודעות הסתיים.
  • שירות A שולח את הודעה 4 לטיפול – והנה כאן ה"טיפול המיוחד" מסתיים.

הקוד שעלי לכתוב הוא לא מסובך, אבל גם לא סופר-פשוט (תלוי מאוד בשפת התכנות וספריות-העזר שיש בידי).
קשה יותר לכתוב אותו שיהיה יעיל ו Scalable (שוב: תלוי בשפה / סביבת ריצה), וקשה אפילו יותר לכתוב אותו שיהיה גם Highly Available.

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

רגע של תיאוריה

ישנו סגנון ארכיטקטוני שנקרא Event-Driven Architecture (ארכיטקטורה מונחית-אירועים, להלן EDA).

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

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

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

למשל, אפיון לדוגמה של אינטראקציה של הודעות יכול להיות:

  • Broker – מתווך ביניים שיוצר decoupling בין שולח ההודעה, ובין המקבל שלה, אך עושה זאת בתצורה של "הודעה-תשובה".
  • Message Broker – שהוא כמו Broker, אבל עובד בתצורה של "שגר ושכח"
  • Publisher-Subscriber, תצורה של Message Broker עם Fan Out, כלומר: שליחת הודעה אחת וקבלה שלה ע"י כמה נמענים (נוסח SNS של אמזון).
  • Message Queue – נוסח SQS של אמזון.
  • ועוד ועוד…
מכיוון שיש הרבה סוגי אינטראקציה אפשריים, ועל כל אחד ניתן לעשות כמה וריאציות, ההמלצה היא קודם כל לחשוב ב "טופולוגיות" ורק אז לבחור את סוגי האינטרציה. קיימות 2 טופלוגיות מרכזיות:
  • Broker Topology – שליחה וטיפול בהודעות בודדות ובלתי-תלויות.
    כל הודעה עומדת בפני עצמה, והמערכת שלנו היא כמו "כוורת דבורים" של שליחת הודעות וטיפול בהודעות – ש"זורמות מעצמן", ללא ניהול מרכזי.
  • Mediator Topology – ההודעות, או לפחות חלק מהן, אינן זורמות באופן עצמאי – אלא מנוהלות במסגרת מקומית מסוימת.
    למשל: יש לנו בכוורת כמה "דבורים סמליות (sergeant)" שמנהלות בצורה אקטיבית כמה תהליכים שהוגדרו כמורכבים או קריטיים במיוחד. ה Mediator הוא בעצם סוג של Workflow Engine. הנה דוגמה:
טופולוגיה של Broker. מקור – Mark Richards
בתרשים למעלה מתוארת טופולוגית Mediator. בתחתית התרשים אנו יכולים לראות שירותים שונים (Processors) שיודעים לטפל בהודעות. הם יכולים לעבוד בכמה תצורות שונות, למשל: בתצורה בה שולחים הודעה ומספקים כתובת (queue אחר, בד"כ) לשלוח אליו את ההודעות שטופלו (במידה וזקוקים לתשובה).
ה Mediator מנהל עשרות תורים קטנים (בשם Channels, למעלה), כאשר כל תור מתאר instance של Workflow בהרצה. ה Mediator מחזיק State Machine המאתר את ה Workflow. בכל שלב הוא שולח הודעות א-סינכרוניות (בד"כ) ל Processors הנכונים (בתרשים למטה לכל processor יש תור), וברגע שטיפול בהודעה הסתיים, הוא עובר שלב ב State machine, וכך יודע מה ההודעה הבאה שעליו לשלוח – עד סיום ה Workflow.
—את ה Mediator (= דפוס עיצוב) ניתן לממש לבד בקוד. שירות SWF הוא מעין "תשתית" שמאפשרת ליישם Mediator בצורה קלה ואמינה יותר.

הערה: SWF הוא לא חדשני. יש הרבה Frameworks לניהול Workflows (שמות נרדפים הם Process Integration, EAI, וכו'), ותיקים ומשוכללים בהרבה. כמו ששמו מעיד עליו: הוא שירות פשוט ובסיסי למדי, אבל שיכול לספק אמינות גבוהה ואינטגרציה טובה לסביבת אמזון.

ניתן לתאר את עיקר התכונות של SWF באופן הבא:

  • שירות מבוזר, אמין, highly scalable, המנוהל כשירות.
  • השירות מספק בעיקר כלי ניהול והרצה של ה workflow – קרי Mediator.
    את ה activities – יהיה עליכם לממש לבד (בג'אווה, רובי, וכו') ולעשות deploy של הקוד על מכונות EC2 שבבעלותכם או אפילו מכונה מרוחקת (לא על AWS). מה ש SWF עושה הוא לקרוא לקוד שלכם ברגעים הנכונים, אבל הלוגיקה – היא בבעלותכם.
  • ניתן לשלב בצעדי ה Workflow קריאה לשירותים שלא רצות בענן (למשל: אפליקציות on-premises) או פעולות אנושיות (למשל: אישור או דחיה של בקשה).
  • ה workflow יכול לחיות עד כשנה (ניתן להגדיר retention period משלכם לכל workflow), לאחר מכן – אמזון תמחק אותו.
  • את ה workflow מנהלים בתוך domains, שלרוב נקצה אחד כזה לכל אפליקציה (לצורך ארגון והפרדה).
    • כל domain יכול להכיל מספר workflows.
    • workflow מדומיין A מנוע מלתקשר עם workflow מדומיין B.
  • ה state של ה Workflow execution, נשמר במקום אמין במיוחד (S3?) שמבטיח שלא סביר [א] שהוא יאבד.

השחקנים העקריים ב Workflow של SWF הם:

  • Activity Worker – הוא מי שמבצע את ה Activity. לפעמים זה יהיה חישוב פשוט, ולעתים – delegation של העבודה לחלק אחר של תוכנה, או לאדם שייקח החלטה.
  • Decider – לוקח החלטה מה לעשות ברגע ש Activity יחיד ב Workflow הסתיים – כלומר, מה השלב הבא ב workflow. המימוש הוא לרוב סט חוקים פשוט או State Machine.
הקוד שמפעיל את ה workflow נקרא Workflow Starter.
דוגמה ל workflow פשוט של עיבוד תמונה: מתאימים לה את הגודל, מוסיפים כיתוב (watermark) ושולחים הודעה שהתמונה מוכנה.

הערה קטנה של מינוחים:

start_to_close_timeout – הוא פרמטר של workflow או activity, שלאוזן ישראלית ממוצעת עלול להישמע כמו "מתי מתחילים לסגור את העניינים" 🙂
בפועל מדברים על timeout נוקשה, שיעצור את ה activity/worfklow אם מרגע התחלת הפעולה (start) עד סופה (close) – היא לא הסתיימה.

באופן דומה יש timeout שנקרא schedule_to_start, שמגביל את הזמן בו workflow יכול להמתין לפני שהוא מתחיל להיות בכלל מטופל (כלומר: להמתין ב Queue לתחילת טיפול).

בתיעוד של אמזון, ניתן למצוא תיעוד ברור על קונספטים מתקדמים יותר של שירות ה SWF, כגון: markers, signals, tags ו child workflows.

בקיצור רב (רק בכדי לקבל מושג):

  • Signals מאפשרים לעדכן את ה workflow בשינוי state מעניין שהתרחש – מאז שה workflow החל לפעול.
  • tags – מאפשרים לתייג, לצורך חיפוש עתידי ב workflow history , אחר מצבים מיוחדים שה workflow הגיע אליהם.
  • Markers – מידע (או state) נוסף שה Deciders יכולים לשמור על ה workflow – לצורך ביצוע החלטות עתידיות.
  • Child Workflow – צעד בודד ב workflow, פותח workflow משל עצמו. הצעד ב workflow מסתיים כאשר ה child workflow הסתיים. הגיוני.
שיהיה בהצלחה!

—-

לינקים רלוונטיים

Webinar: Introduction to Amazon SWF

AWS in Plain English – הצעה לשמות טובים יותר לשירותים של AWS + הסבר קצר על כל שירות. אהבתי (!) אם כי דווקא ההסבר על SWF הוא לא כ"כ מוצלח לטעמי (הייתי פשוט קורא לו "Workflow Service").

[א] "לא סביר" הוא עדיין אפשרי, כמובן.
"חוק מרפי-בענן" גורס: "כל מה שעלול להיכשל – אכן ייכשל: בסוף השבוע, בחצות, ובשיא העומס".

מערכות מבוזרות – מבט עדכני

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

קיטרתי – גם אני לא עשיתי זאת בעצמי (כמעט).

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

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

  • מוצרים: אפליקציית לקוח של טורנט, Tor (רשת פרוקסי לאנונימיות), או משחק Massively Multi-player Online.
  • Frameworks: המאפשרים פתרון בעיות מבוזרות ב"רמת הפשטה גבוהה".
יש משהו מאוד קוסם בסקירה כיצד מוצרים מבוזרים עובדים – זה יכול להיות ממש מעניין!
האמת היא שיש לי (ובכלל בקהילה) – ידע מוגבל על הנושא. (הנה פוסט נחמד על Tor)
 
בסקירה של קטגורית Frameworks יש משהו יותר מעשי: סביר יותר שתבחרו Framework לעבוד בו – מאשר לכתוב מערת מבוזרת מ scratch, תוך כדי שאתם מושפעים ממימוש של מערכות אחרות.
כמו כן – הידע עליהן זמין ונגיש. לאחרונה התחלנו לעבוד עם Hadoop – מה שעוזרת לי לשלב את תוכן הפוסט עם העבודה השוטפת.
 
בסופו של דבר, גם ה Frameworks וגם המוצרים המבוזרים שמתוארים בפוסט עושים שימוש נרחב במנגנונים ("פרימיטיביים") של מערכות מבוזרות שתיארתי בפוסט הקודם: Multi-cast ורפליקציה, בחירת מנהיג, השגת קונצנזוס ועוד. חלק מכובד מהמערכות שאתאר בפוסט זה משתמש ב Zookeeper, מוצר שהזכרתי בפוסט הקודם – בכדי לבצע את הפרימיטיביים המבוזרים הללו.
 
 
תוך כדי כתיבת הפוסט – נוכחתי שאני מתמקד בקטגוריה מאוד ספציפית של מוצרים מבוזרים: מוצרי Big Data.
 
זה בסדר.
 
מוצרי Big Data לרוב מחשבים… הרבה נתונים. כ"כ הרבה נתונים שלא ניתן לאחסן במחשב אחד. לרוב גם לא בעשרה.
כדי לטפל בכמויות הנתונים (Volume) או במהירות שלהם (Velocity) – זקוקים למערכת מבוזרת. אותם מוצרים (או Frameworks) הם גם במובן אחד "מערכת מבוזרת", וגם "מוצר Big Data". בפוסט – נבחן בעיקר את הפן המבוזר שלהן.
 
בכל מקרה: כנראה שחלק מכובד מאוד מאנשי התוכנה שמתמודדים עם מערכות מבוזרות – עושים זאת דרך עבודה עם מוצרי Big Data, הפופולריים כ"כ היום.
 
 
בקיצור: זהו איננו פוסט על Big Data. יום אחד אולי אקדיש לנושא פוסט ראוי, ואולי גם לא 🙂
 
 

חישוב מבוזר

Scatter – Gather הוא השם המקובל לדפוס חישוב מבוזר, המבוסס על 2 רעיונות:

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

נ.ב. "זמן סביר" הוא יחסי. לא נדיר להיתקל בשאילתות שרצות על מערכות Hadoop במשך שבועות – עד לסיום החישוב.

Map Reduce – הוא היישום הידוע של פרדיגמת Scatter-Gather, שנחשב היום אמנם כבוגר – ומובן היטב.

הרעיון הוא לחלק את החישוב המבוזר לשני שלבים:

  • Map – עיבוד נתונים נקודתי על המכונה הלוקאלית (חיפוש / פילטור / עיבוד נתונים וכו')
  • Reduce – ביצוע סיכום של החישוב וחיבור התשובות של המחשבים הבודדים לתשובה אחת גדולה.
החלוצים בתחום של Map Reduce מבוזר על פני מספר רב של מחשבים הם חברת גוגל, ופרויקט Apache Hadoop – שהושפע רבות ממאמרים שגוגל פרסמה על מימוש ה Mapreduce שלה. בעשור מאז הושק הפך פרויקט Hadoop לסטנדט בתעשייה, עם קהילה עשירה ורחבה. כאשר הרווחים מתמיכה ב Hadoop הגיעה למאות מיליוני דולרים בשנה – נכנסו לשם גם שחקני ה Enterprise הגדולים (כלומר: EMC, IBM, Oracle, וכו')
.
Google Map-reduce ו Hadoop מבוססים על מערכת קבצים מבוזרת (GFS ו HDFS בהתאמה), ותשתית לביצוע חישוב מבוזר על גבי ה nodes שמחזיקים את הנתונים הנ"ל (ב Hadoop נקראת YARN, בגוגל כנראה פשוט "Mapreduce").
 
בפועל יש ב Map reduce שלושה שלבים עיקריים: Reduce, Map ו Shuffle (העברת סיכומי הביניים בין ה Mappers ל Reducers – פעולה אותו מספק ה Framework).
 
דוגמה לבעיית מיון מבוזרת (הניחו שמדובר במיליארדי רשומות) הנפתרת בעזרת MapReduce. המפתחים כותבים את פונקציות ה Map וה Reduce, ה framework מספק להם את ה Shuffle (העברת הנתונים בין ה mappers ל reducers).

ניתן לכתוב פונקציות "Map" ו "Reduce" בקוד, אך סביר יותר להניח שכאשר אתם מגדירים חישובי Map-reduce רבים – תעדיפו להשתמש ברמת הפשטה גבוהה יותר כגון אלו שמספקים Pig (מן "שפת תכנות" מיוחדת ל Map-Reduce) או Hive (המבוסס על SQL).

כדרך אגב: Pig קיבל את שמו מכיוון שחזירים אוכלים גם צמחים וגם בשר – ו Pig יודע לטפל גם ב structured data וגם ב unstructured data.

השנים עברו, וכיום המודל של חלוקת החישוב המבוזר לשלבי Map ו Reduce  – נחשב מעט מוגבל.
 
כמו כן, ההסתמכות על מערכת קבצים כבסיס לחישוב היא טובה ל DataSets ענק שלא ניתן להכיל בזיכרון (יש clusters של Hadoop שמנתחים עשרות, ואולי מאות PetaBytes של נתונים), אבל בעשור האחרון הזיכרון הפך לזול (מכונה עם 1TB זכרון היא בהישג יד) – והיכולת לבצע את החישוב בזיכרון יכולה להאיץ את מהירות החישוב בסדר גודל או שניים, ואולי אף יותר (תלוי בחישוב). עובדה זו מקדמת את הפופולריות של הפתרונות לחישוב מבוזר מבוסס-הזיכרון (כמו Storm, או Spark), ומשאירה ל Hadoop את היתרון בעיקר עבור מאגרי הנתונים הבאמת-גדולים (נאמר: 100TB ויותר של נתונים, מספר שבוודאי ישתנה עם הזמן).
 
המוצרים העיקריים ב Eco-System של Hadoop
 

דוגמה נוספת לדפוס של Scatter – Gather הוא הדפוס בו עושים שימוש ב Spark. אני לא בטוח שזהו שם רשמי – אך אני מכיר את המינוח "Transform – Action" (כאשר "map" הוא פעולת transform אפשרית, ו "reduce" הוא action אפשרי).

Spark הוא הכוכב העולה של עולם ה Big Data. בניגוד ל Hadoop הוא מאפשר (כחלק מהמוצר) מספר רב יותר של פונקציות מאשר "map" ו "reduce".
במקום פעולת ה map ניתן להשתמש בסמנטיקות כגון map, filter, sample, union, join, partitionby ועוד…
במקום פעולת ה reduce ניתן להשתמש בסמנטיקות כגון reduce, collect, count, first, foreach, saveAsXxx ועוד.

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

המוצרים ב Eco-System ה מתהווה של Spark (חלקם מקבילים לכלים קיימים של Hadoop). מקור: databricks

ל Spark יש עוד שתי תכונות חשובות:

  • הוא מחזיק (סוג של Cache) את הנתונים בזיכרון – אליו הגישה מהירה בסדרי גודל מאשר בדיסק.
  • הוא מרכז את רוב שרשרת החישוב באותו node ומצמצם את הצורך להעביר נתונים בין nodes ב cluster (גם ל Hadoop יש כלים שעוזרים לצמצם העברות של נתונים בין nodes, אך Map-reduce פחות מוצלח בכך באופן טבעי)
פרטים נוספים:
  • ה "agent" של Spark רץ במקביל למערכת ה storage המבוזר, תהיה זו Hadoop או Cassandra (או סתם קבצים בפורמט נתמך) – ושואב ממנו את הנתונים הגולמיים לחישוב.
  • ה Agent (נקרא node – אני נמנע משם זה בכדי למנוע בלבול) מקבל Job (לצורך העניין: קטע קוד) לבצע חישוב מסוים על הנתונים. יש כבר ב Agent קוד של פונקציות שימושיות (filter, sort, join, וכו' – לפעולות "map" או count, reduce, collect – לפעולות "reduce").
  • ה Agent מחלק את המשימה ל stages ובונה גרף חישוב (על בסיס הידע כיצד מאורגנים הנתונים ב cluster) – ושולח tasks חישוביים ל Agents ב cluster.
ל Spark יש לא מעט נקודות אינטגרציה עם Hadoop – כך שהתחרות באמת היא לא בין Hadoop ל Spark (לפחות – עדיין), אלא יותר תחרות בין Spark ל YARN (מנוע ה Map-reduce).

 

Event Processing

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

זן אחר של חישוב הוא חישוב ב Streaming: ביצוע חישובים קטנים רבים, כחלק משטף של אירועים שנכנס למערכת.
בקטגוריה זו ניתן לכלול את Spark Streaming (המבוסס על Spark), אך אתמקד דווקא ב Storm – שנבנה לצורך זה במקור.

Storm

  • מערכת Horizontal scalable ו fault-tolerant – כמובן!
  • מערכת המספקת תשובות ב low-latency (שלא לומר real-time – כי זה פשוט לא יהיה נכון), תוך כדי שימוש בכוח חישוב מבוזר.
  • At-lest-once Processing מה שאומר שהנחת היסוד במערכת המבוזרת היא שחלקים שלה כושלים מדי פעם, ולכן לעתים נעשה אותו חישוב יותר מפעם אחת (לרוב: פעמיים) – בכדי להמשיך לקבל תוצאות גם במקרה של כשל מקומי.

Storm נולדה מתוך פרויקט בטוויטר לחישוב trending analysis ב low-latency.

המבנה של Storm Computation Cluster הוא סוג של Pipes & Filters, כאשר יש:

  • Stream – רצף של tuples של נתונים.
  • Spout – נקודת אינטגרציה של המערכת, המזרימה נתונים ל streams.
  • Bolt – נקודת חישוב ("Filter") בגרף החישוב ("הטופולוגיה").
    על מערכת Storm מריצים טופולוגיות חישוב רבות במקביל – כל אחת מבצעת חישוב אחר.
  • Supervisor – הוא node של Storm, שכולל כמה Workers שיכולים להריץ את קוד ה bolts או ה spouts.
  • Stream Grouping – האסטרטגיה כיצד לחלק עבודה בין bolts שונים.
בעוד פעולות אצווה סורקות את הנתונים מקצה אחד לקצה שני – וחוזרות עם תשובה ("42"), ב Event Processing כל הזמן נכנסים נתונים, והערך אותו אנו מחשבים (אחד או יותר) – יתעדכן באופן תדיר ("43, לא רגע… עכשיו 47… ועכשיו 40…").
 
טופולוגיה לדוגמה של Storm. מקור: michael-noll.com
 

אם העבודה היא חישובית בלבד – כל אחד מה supervisors יוכל להריץ את כל הטופולוגיה (ואז לא צריך להעביר נתונים בין מחשבים במערכת – שזה עדיף). נוכל לבחור, למשל, ב Local Stream Grouping – שמורה ל Storm לשלוח את הנתונים ל Worker על אותה המכונה.

במידה ויש נתונים מסוימים שיש להשתמש בהם לצורך החישוב, והם נמצאים על supervisors מסוימים – יש להגדיר את Stream Grouping כך שיידע לשלוח את הנתונים ל supervisor הנכון. במקרה כזה אולי נרצה לבחור ב Fields Stream Grouping – המורה ל Storm לשלוח את הנתונים למחשב ע"פ שדה מסוים בנתונים (למשל: "ארץ" – כי כל supervisor מחזיק נתונים של ארץ אחרת).
 
הערכה גסה איזה כלי יכול להתאים לכם – בהינתן בזמני ההמתנה לתשובה שאתם מוכנים לסבול. * Tez הוא מנוע שכולל שיפורים ל Map-Reduce, שמרביתם מזכירים את הארכיטקטורה של Spark – מלבד הגמישות לתאר תהליכים כיותר מ-2 שלבים עיקריים.

 

Mahout

ארצה להזכיר בקצרה את Apache Mahout (שהוא חלק מ Hadoop), פירוש השם Mahout הוא "נהג הפיל" – ואכן Mahout רוכבת על הפיל (Hadoop).
זהו כלי שמספק סט של אלגוריתמים שימושיים הממומשים בצורה מבוזרת, חלקם על גבי Map-Reduce וחלקם על גבי Spark – ומאפשר לעבוד ברמת הפשטה גבוהה אפילו יותר.

סוגי הבעיות ש Mahout מכסה (כל אחת – בעזרת כמה אלגוריתמים שונים) הן:

  • סינון שיתופי – "אנשים שאהבו את x אהבו גם את y"
  • Classification – אפיון "מרחב" הנתונים מסוג מסוים – גם אלו שאין לנו. סוג של חיזוי.
  • רגרסיות – הערכת הקשרים בין משתנים שונים.
  • Clustering – חלוקה של פריטי מידע לקבוצות – ע"פ פרמטרים מסוימים.
  • ועוד
מימושים של האלגוריתמים  הנ"ל תוכלו למצוא בקלות למכונה יחידה, אבל אם יש לכם סט גדול של נתונים, או שאתם רוצים לחשב את האלגוריתמים בצורה תכופה – Mahout יחסוך לכם עבודה רבה!
 
 
 

.

Lambda Architecture

הכלים שהצגנו למעלה – מאפשרים לנו בחירה בין:

  • תשובה מדויקת – אטית (למשל: Map-reduce)
  • תשובה "לא מדויקת" – מהירה (למשל: Storm או Spark)
    בהנחה שאנו מקריבים דיוק עבור המהירות. למשל: משתמשים בחישוב בנתונים בני חמש דקות – ולא העדכניים ביותר, או חישוב על סמך דגימה חלקית – ולא על סמך כלל הנתונים.

בארגון בעל צרכי מידע – למה לבחור בין האופציות? האם אי אפשר לקבל את שניהם?

סוג של שילוב מקובל בין 2 הגישות נקרא "ארכיטקטורת למבדה", ספק אם על שם האות היוונית למבדה (Λ) שאם מטים אותה על הצד מזכירה מאוד את צורת הארכיטקטורה – ואולי בהקשר ל Lambda Calculus המבוססת על פישוט המערכת לפונקציות (בהקשר של תכנות פונקציונלי). הנחות היסוד של הארכיטקטורה הן:

  • מייצרים במערכת התפעולית שטף של אירועים – כולם immutable (בתרשים למעלה: "new data stream").
  • את האירועים אוספים ומפצלים ל-2 ערוצים:
    • ערוץ של שמירה לטווח ארוך – נוסח HDFS או AWS S3, ואז ביצוע שאילתות ארוכות באצווה (Batch).
    • ערוץ של עיבוד מיידי והסקת תובנות – נוסח Storm, שבו ההודעות ייזרקו מיד לאחר שיעובדו (הן שמורות לנו מהערוץ הראשון) – הערוץ המהיר.
  • את התובנות קצרות הטווח של הערוץ המהיר, ואת התובנות ארוכות הטווח – של ערוץ האצווה מרכזים ל storage שלישי (Service Layer) – לרוב HBase, Impala או RedShift, ממנו נבצע שליפות חזרה למערכת התפעולית.

כמו ארכיטקטורה, "ארכיטקטורת למבדה" היא רק תבנית מקובלת – ונכון יותר יהיה להתאים אותה לצרכים הייחודיים שלכם, ולא להיצמד בכוח לאיזו הגדרה מקובלת..

Apache Kafka

לסיום אני רוצה להזכיר עוד מערכת מבוזרת שהופכת פופולרית – Distributed Message Queue בשם קפקא.

בדומה למערכות הקודמות, קפקא היא מערכת Horizontally scalable ו Fault-tolerant.
בנוסף יש לה מימד של durability – הודעות נשמרות לדיסק ומשוכפלות בין מחשבים שונים.

בקפקא נלקחו בה כמה החלטות תכנוניות בכדי לתמוך בשטף אדיר של אירועים. קפקא מטפלת יפה בקצבים גבוהים מאוד של נתונים, במחיר של latency מסוים בכתיבת ובשליפת הודעות. שמעתי על חבר'ה שחווים latency אף של ל 2-3 שניות.

כלל-אצבע ששמעתי אומר כך: "אם יש לך עד 100,000 הודעות בשנייה – השתמש ב RabbitMQ, אם יש יותר – כדאי לשקול מעבר לקפקא".

 

כמה הנחות בעולם של קפקא הן:

  • הודעות ב Queue הן immutable – בלתי ניתנות לשינוי. ניתן להוסיף הודעות ל queue – ולקרוא אותן, אך לא לעדכן או למחוק אותן. ההודעות יימחקו כחלק מתהליך של ה Queue (למשל: לאחר 24 שעות) – והצרכן צריך לרשום לעצמו ולעקוב אלו הודעות כבר נקראו – כדי לא לקרוא אותן שוב. הן פשוט יושבות שם – כמו על דיסק.
    קפקא מנהלת את ההודעות כקובץ רציף בדיסק – מה שמשפר מאוד את יעילות ה I/O שלה. כל הפעולות נעשות ב Bulks.
  • ה Queue נקרא "Topic" ולא מובטח בו סדר מדויק של ההודעות (פרטים בהמשך).
  • Broker הוא שרת (או node) במערכת. אם לברוקר אין מספיק דיסק בכדי לאכסן את תוכן ה Topic, או שאין לו מספיק I/O בכדי לכתוב לדיסק את ההודעות בקצב מהיר מספיק – יש לחלק (באחריות ה Admin האנושי) את ה topic על גבי כמה partitions. ההודעות שמתקבלות ישמרו על partitions שונים, וקריאת ההודעות – גם היא תעשה מה partitions השונים.
קפקא עצמה מאוד מרשימה מבחינת יציבות וביצועים – אך זה נעשה במחיר אי-החבאת מורכבויות של מימוש מהמשתמשים שלו:
 
ה producer צריך לדעת על מספר ה partitions שבשימוש, מכיוון שקפקא מסתמך על כך שהוא עושה בעצמו מן "Client Side Load Balancing" (דומה לרעיון של Client-Side Directory שהסברתי בפוסט הקודם) – ומפזר את ההודעות בין ה partitions השונים (או שכל producer עושה זאת בעצמו – או שהם עושים זאת – כקבוצה).
הפיזור כמובן לא צריך להיות אקראי אלא יכול להיות ע"פ שדה מסוים בהודעה, שדה כמו "ארץ" – שיש לו משמעות עסקית.
 
ה consumer לא יכול לקרוא את ההודעות ב topic ע"פ הסדר – כי הן מפוזרות בין ה partitions השונים (קריאה נעשית רק מ partition מסוים). אין שום מנגנון בקפקא שמסייע להבין באיזה partition נמצאת ההודעה הבאה. בפועל מה שעושים הוא שמציבים consumer לכל partition – והם מעבדים את ההודעות במקביל, מבלי להתייחס להודעות שב partitions האחרים.
כמו כן – מישהו צריך לעקוב מהי ההודעה האחרונה שנקראה מכל partition: קפקא לא עושה זאת. הדרך המקובלת לעשות זאת היא להיעזר ב Zookeeper לניהול ה state המבוזר הזה (שיהיה יציב, זמין, וכו').
ב AWS Kinesis (וריאציה של Kafka – של אמזון) – מנהלים את ה state המבוזר ב DynamoDB (בסיס נתונים K/V – גם הוא מבוזר).
 
כל partition ממספר את ההודעות של ה topic באופן בלתי-תלוי. מקור: kafka.apache.org
 
לא מתאים לכם? – אתם יכולים לסדר את המידע בחזרה בעזרת מערכת נוספת שתעשה את העבודה (למשל: Storm?).
 
לכל Broker יש רפליקה (אחת או יותר), במבנה של Active-Passive. ה Active משרת את ה consumer – ואם הוא כושל, אחד ה Passives נבחר להיות ה leader – והופך להיות "ה Active החדש".
 
ה producer הוא האחראי על מדיניות ה consistency ויכול לבחור להחשיב כתיבה ל queue כמוצלחת כאשר ה Leader קיבל אותה, או רק כאשר ה Leader סיים לעשות רפליקציה של הנתונים ל broker נוסף (אחד או יותר – תלוי ברמת הפרנויה שלכם). התוצאה כמובן היא trade-off בין latency ל durability – שניתן לשחק בו בצורה דינאמית.
 
אפשר להשוות את קפקא למשאית: היא מאוד לא נעימה לצורך יציאה לדייט עם בחורה, או בכדי לקנות משהו בסופר.
אבל אם אתם רוצים להעביר אלפי טונות של מוצר כלשהו – בוודאי שתרצו כמה משאיות שיעשו את העבודה. צי של רכבים קטנים – פשוט לא יעשה עבודה טובה באותה המידה…

 

סיכום

בפוסט אמנם הצגתי הרבה מוצרי "Big Data" – אך הכוונה הייתה להתמקד באספקטים המבוזרים שלהם.
מערכות מבוזרות מוסיפות סיבוך למערכת – ולכן כדאי להימנע מהן במידת האפשר. כן… ברור לי שלא-מעט מהנדסים מחפשי-אתגר ימצאו את הדרך להשתמש במערכות מבוזרות – גם שאין צורך עסקי לכך.

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

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

—-

לינקים רלוונטיים:
השוואה בין Spark ל YARN

מצגת טובה אודות Spark

על Circuit Breakers ויציבות של מערכות מבוזרות

דמיינו 2 חנויות מכולת כמעט זהות: "המכולת של שמואל" ו"המכולת של הלל".

בשתי חנויות המכולת יש מוצרים איכותיים שאתם אוהבים, מוזיקה טובה מתנגנת, השירות מצוין, הן פתוחות 24/7, והמחירים – הוגנים. אתם אוהבים את המכולות הללו, ואתם אוהבים לקנות בהן.

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

במכולת של הלל – המצב הפוך. הם מוכנים לכך שלא יהיו מוצרים מסוימים זמינים. הם קוראים למצב הזה partial service או degraded service (הם קצת גיקים עם המונחים שלהם). המכולת פתוחה 24/7 למעט מקרים נדירים בהם הם סוגרים אותה (כאשר הלל צריך לצאת לאיזה סידור ואין לו מחליף), אבל כאשר אני הולך לקנות משהו – ייתכן ואחזור ללא קוטג' הביתה. לפעמים לא אכפת לי, אבל לפעמים זה מבאס. אם זה ממש חשוב לי אפילו אקפוץ למכולת אחרת להביא את הקוטג'. אבל בגלל שאני אוהב את החוויה במכולת – אני עדיין אחזור אליה בקנייה הבאה.
במקום לדאוג ל"אפס חוסרים במלאי", החבר'ה של הלל עסוקים בכך שחוסר של מוצר מסוים במלאי – לא יגרום למכולת להיסגר מעצמה. זה מאוד מוזר אבל הם מספרים שאם לא יעשו שום דבר, מחסור בטונה פתאום יגרום למכולת להסגר מעצמה. גיקים – אמרנו כבר?

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

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

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

Circuit Breakers

המנגנון הבסיסי המאפשר את מודל "המכולת של הלל" הוא ה Circuit Breaker.
לצורך הדיון אניח שמדובר במערכת בארכיטקטורת Micro-Services (בקיצור: MSA) והשירותים השונים הם הרזולוציה לבידוד בפני תקלות. ניתן לעשות אותו הדבר במערכת "מונוליטית", אם כי המשמעת העצמית הנדרשת היא לעתים מעט גבוהה יותר.

ה Circuit Breaker הוא מן Proxy לשירות מרוחק – אשר רק דרכו עושים את הקריאות. השירות המרוחק יכול להיות:
א. זמין
ב. לא זמין
ג. זמין – אך כושל (יש errors)
ד. זמין אבל אטי

אם ה Circuit Breaker מזהה שהשירות המרוחק אינו עובד כמו שצריך – הוא "יוצר נתק" (מטאפורה: פותח את המעגל) ולא מאפשר לקריאות נוספות לעבור. הפונקציות השונות מקבלות אינדיקציה על המצב הזה ופועלות כמיטב יכולתן לספק שירות חלקי – שעדיף על חוסר שירות.

במשך הזמן, ה Circuit Breaker נותן לקריאות בודדות לעבור ומנסה לזהות אם המערכת המרוחקת חזרה לספק שירות תקין. אם כן – הוא "סוגר את המעגל" ומחזיר את המצב לקדמותו.

מקורות: "דפוס העיצוב" של ה Circuit Breaker הוצג לראשונה ב 2007 ע"י מייקל נייגארד, בספר המצויין (!!): !Release It.
ב 2014 מרטין פאולר כתב פוסט בבלוג על דפוס העיצוב (הוא לא חידש שם, רק הסביר יותר פשוט), ומאז זהו ה reference המקובל.

המימוש הפנימי של ה Circuit Breaker הוא לרוב כמכונת מצבים פשוטה:

מקור: orgsync/stoplight (מימוש ברובי של Circuit Breaker)
  • המצב ההתחלתי הוא "מעגל סגור" (ירוק).
  • אם יש מספר מסוים של תקלות (מוצג בתרשים כ "fail") – עוברים למצב "אדום".
    • בד"כ לא מדובר בתקלה יחידה אלא threshold של סדרת תקלות. למשל: רצף של 5 exceptions, כאשר ה threshold הוא של 5.
  • במצב "אדום" – כל ניסיון קריאה לשירות המרוחק ייענה בערך החזרה מסוים (או Exception) מצדו של ה Circuit Breaker שאומר "תסתדרו בלי!" (כלומר: בלי השירות).
  • לאחר זמן מה (נניח: 120 שניות) של מצב אדום, ה circuit breaker יעבור למצב צהוב – ניסיון להחזיר שירות.
    • הוא יאפשר למספר נתון של קריאות לעבור לשירות המרוחק כדי לבדוק את התגובה. למשל: 10 קריאות. לכל שאר הקריאות הוא עדיין יחזיר את התשובה "תסתדרו בלי!".
    • אם ב 10 הקריאות הללו, הוא מזהה treshhold מסוים בעייתי, למשל: רצף של 3 exceptions מצד השירות המרוחק (לרוב ה threshold של המצב הצהוב הוא יותר מחמיר מזה של המצב הירוק) – הוא חוזר למצב אדום.
    • אחרת – הוא מחזיר את המערכת למצב ירוק.
כמו שאתם מבינים – יש המון וריאציות אפשריות של התנהגות של Circuit Breakers:
  • אפשר לשים thresholds שונים ומשונים. למשל, משהו שלא הזכרנו: שניסיונות חזרה למצב הצהוב יקרו בתדירות משתנה: יש גישה של המתנה של 2 דקות ואז כל 30 שניות (כאשר השירות המרוחק הוא חשוב) ויש גישה של להאריך את זמני הניסיון, למשל: פעם ראשונה 2 דקות, אח"כ 5 דקות, וכל פעם נוספת – 10 דקות (כאשר השירות המרוחק פחות חשוב ודווקא שגיאה ממנו היא לא נעימה).
  • כאשר circuit breaker מופעל – כנראה שתרצו שסוג של alert יעלה ל monitoring, הרי מדובר בהחלטה לתת שירות פחות טוב. מתי ואיך להעלות את ה alert – עניין לבחירה.
  • לעתים יש אפשרות של override ידני – אפשרות לקבע מצב ירוק/אדום/צהוב של ה circuit breaker בהתערבות ידנית.
  • אולי הכי חשוב: כיצד ה circuit breaker מאתר שגיאה של השירות המרוחק?
    • האם ע"י ניטור ה responses של ההודעות שחזרו מהשירות המרוחק (למשל: HTTP 5xx)?
    • האם ע"י ביצוע בדיקה יזומה (proactive) לשרת המרוחק (למשל: שליחת pinging או בדיקת health-check)?
    • אם מדובר על אטיות, ה circuit breaker יכול למדוד את מהירות החזרה של קריאות מהשרת המרוחק. אפשר להגיב לממוצע של קריאות אטיות, או לעתים להסתכל על אחוזון מסוים. למשל: אם 5% מהקריאות אטיות מ 10 שניות, אנו רוצים לנתק – מכיוון שזה אומר שירות גרוע למשתמש הקצה. האם מנתקים רק את הקריאות האטיות או את כולן?!
    • וכו'
תוכלו למצוא מספר מימושים שונים של Circuit Breakers, בכל שפת תכנות כמעט (לא ראיתי באסמבלי ;-)), אבל לא נדיר המקרה בהם תרצו לממש גם וריאציה משלכם – במידה ויש לכם מקרה חשוב שלא מטופל ע"י המימושים הזמינים.

לא נדיר גם מצב בו אתם משתמשים בכמה circuit breakers שונים בתוך המערכת. תלוי כמה גדולה ומורכבת היא.
ההתעסקות עם circuit breakers גם היא יכולה להיות משמעותית, וכדאי לשים לב ש:

  • אתם לא נסחפים לאזור ה over-optimization שאיננו משתלם מבחינת ההשקעה.
  • אתם יוצרים מערכת של circuit breaker שהיא מורכבת מדי לניטור ושליטה בזמן אירוע אמת ב Production (כאשר אתם לא יכולים לענות על שאלות כמו: "מדוע x התנתק"? או "אילו ניתוקים היו בזמן נתון").

אלו דוגמאות מהעולם האמיתי של ל Partial Service ניתן לתת? הנה כמה שאני נתקלתי בהן:

  • לוגים, לוגים, לוגים! בעם הייתה לנו מערכת עם שירות ירוד כמעט יומיים (!!) עד שהבנו שהיא נופלת כל הזמן כי הדיסק מלא ופעולות כתיבה ללוג נכשלות. אם כתיבה ללוג נכשלת – עדיף לא לכתוב לוגים, מאשר לגרום ל IO exceptions שמשבשים תהליכים שלמים במערכת.
    • בווריאציה אחרת מערכת מרוחקת לדיווח של בעיות (סוג של alerts) הגיבה ב latency של 4 שניות, וגרמה לשיבושים רבים בשירות שדיווח לה על בעיות זניחות, יחסית.
  • שירות ש"מצייר" מסלול נסיעה על המפה של החשבונית (בעולם המוניות). אם הוא לא זמין / מגיב היטב – שלח חשבוניות בלי ציור של המסלול, מה הבעיה?
  • שירות שמבצע סליקה של תשלומים. עדיף לשמור את סכום העסקה ולנסות לחייב כמה דקות מאוחר יותר (תחת סיכון של חיובים, עד סכום מסוים, ללא כיסוי) – מאשר לדחות על הסף את כל העסקאות, אפילו בפרק זמן קצר יחסית (כמה דקות).
  • וכו' וכו'

Throttling

עוד וריאציה דומה של Circuit Breaker היא מנגנון throttling ("להחזיק אצבע על הקשית כך שלא יהיה זרם חזק מדי").

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

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

המנגנון הוא דיי פשוט (אתאר את המימוש הספציפי) – עוקבים אחר מספר הבקשות הפעילות לשרת המרוחק בעזרת distributed, non-blocking lock (קל יותר באנגלית) על בסיס רדיס: לפני קריאה לשרת המרוחק מנסים "לקבל" Lock – אם מצליחים – מבצעים את הקריאה. אם לא מצליחים (כי כבר יש 10 קריאות פתוחות) – מחזירים הודעת "תסתדרו בלי!" לפונקציה שביקשה את השירות, ונותנים לה לספק שירות חלקי.

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

TCP timeouts

עוד נקודה ששווה להזכיר היא ה timeouts שאנו קובעים לתקשורת.
Timeouts הם מנגנון הגנה ממעלה ראשונה: אם שרת מרוחק לא מגיב – לא נרצה לתקוע thread (או אפילו request) לאורך זמן. תסריט נפוץ הוא שירות אטי, שגורם לשירות שקורא לו גם להיות אטי (כי הוא עסוק בלהמתין), שגורם גם הוא לשירותים האחרים התלויים בו – להיות אטיים באותו האופן.

למרות ש TCP הוא "reliable protocol", גם לו יש תקלות: בעיקר התנתקויות. כאשר קצב הקריאות בין שירותים הולך גדל – אנו נחווה תקלות אלו יותר ויותר.

התנהגות נפוצה היא לספק אותו ה timeout ליצירת ה connection וביצוע הקריאה עצמה (פעולת "קריאה"). אבל:

  • פעולת ה connection היא פעולה פשוטה ומהירה – היא אורכת, באופן טיפוסי, שבריר של שנייה (בניכוי network latency).
  • פעולת הקריאה לרוב גורמת לשירות השני לעבוד: לקרוא מידע מבסיס הנתונים, לקרוא לשירותים אחרים, לבצע עבודת CPU משמעותית וכו. זמן מקובל לקריאה שכזו הוא כ 100ms וגם לא נדיר להיתקל במצב של 1000ms ויותר (שוב: בניכוי network latency).

לכן, אם נגדיר timeouts באופן הבא:

  • עבור פעולת ה connection של ה TCP – נגדיר timeout בסך ה: tolerable latency
  • עבור פעולת ה read של ה TCP – נגדיר timeout בסך: tolerable latency + tolerable server time

נוכל לצמצם בצורה מורגשת זמני המתנה מיותרים.

כמו כן, אם יש לנו timeout על connection – כדאי לשקול בחיוב פעולת retry מיידית.
ניתן להראות ש timeout קצר ליצירת connection + ביצוע retry יחיד – היא מדיניות שברוב המקרים תהיה עדיפה בהרבה על יצירת connection עם timeout ארוך.

הסבר (דרוש רקע תאורטי בעבודה של TCP): אם ההודעה על לחיצת היד השלישית של TCP אובדת – אין מנגנון (מלבד timeout) לדעת זו. בעיה זו במדעי-המחשב ידועה כבעיית הגנרלים, ומשמעותה שלא ניתן לייצר מנגנון אמין ל handshake. הפתרון של TCP הוא לבצע 3 קריאות handshake בתקווה שזו נקודת איזון טובה בין עלות (מספר הקריאות) / סיכון לכישלון.

אחד הנתונים הידועים ב AWS הוא ש latency בין AZs יכול להיות עד 10ms. אם מסתכלים על הנתונים עצמם רואים ש 10ms הוא לא ממוצע, אלא אירוע נדיר יחסית: בבדיקות שערך Matthew Barlocker (חברת Lucid Software) – הוא ראה שבאחוזון ה 99.85% מקבלים latency בין AZ של 3ms בלבד:

הערה: אמזון מתעדפת נמוך (de-prioritize) קריאות ICMP (פרוטוקול השליטה של TCP/IP, הכולל גם את פקודת ה ping) – ולכן לא כדאי להסתמך על Ping להערכת ה latency ב AWS.

מסקנה אפשרית אחת היא שסביר לקבוע TCP Timeout של 3ms כאשר ליצירת connection באותו ה AWS region.
על פעולות HTTP GET ניתן לשקול (תלוי במקרה) מדיניות דומה של מתן timeouts קצרים יחסית (יש לקחת בחשבון את זמן השרת + network latency) – עם אפשרות ל retry.

"Be Resilient" vs. "Fail Fast"

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

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

מדיניות טובה אחרת אומרת: Fail Fast – אם המערכת מזהה שגיאה או סבירות גבוהה לשגיאה, עליה "לשבור את הכלים באותו הרגע" – וכך יהיה קל יותר לאתר את התקלה. אם התקלה נגררת ונגררת – ה"שבר" צפוי להיות בשלב מאוחר יותר במערכת, בשלב שיהיה קשה מאוד להבין ממנו מה מקור הבעיה האמיתי (root cause).

כיצד 2 גישות חכמות אלו משתלבות זו עם זו בעולם של מערכות מבוזרות, השאופות ליציבות גבוהה?

למרות הסתירה הבסיסית, ניתן לשלב את שתי הגישות:

  • אם מזהים כשל שנראה שניתן "לספוג" – אפשר לבצע Alert Fast, בשאיפה שה Alert מספיק ברור ומשמעותי כדי שיהיה ניתן לנטר אותו ולטפל בו בזמן סביר.
  • אם מזהים כשל שנראה שלא ניתן "לספוג" אותו – זהו באמת מצב שכדאי לעשות Fail Fast.
פעמים רבות, מה שמתחיל כ"כשל חלקי" שניתן לספוג אותו – מתגלגל בהמשך לכשל משמעותי יותר שלא ניתן עוד לספוג ("כמות הופכת לאיכות"  – של הכשל). אם אתם בוחרים בכיוון של Alerts – כדאי שאלו יהיו Alerts שתוכלו, ואכן תתייחסו אליהם – בזמן סביר.

סיכום

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

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

היכולת של מערכת להימצא במצבים שונים של כשלים-חלקיים מוסיפה מורכבות למערכת – אך מאפשרת להגיע לזמינות (availability) גבוהה יותר כאשר מדובר במערכת מורכבת.
העצה של מרטין פאוולר ל MonolithFirst היא טובה בהיבט הזה: מערכת מבוזרת היא בהחלט מורכבת יותר – ומבחינת זמינות  משתלם לעבור למודל מבוזר רק כאשר כלל המערכת הגיע לסף מסוים של מורכבות.
זמינות גבוהה של מערכת מושגת לא רק על ידי תכנון נבון, אלא גם ע"י אימונים ושיפור תמידי.
Chaos Monkey, למשל, הוא כלי Open Source ש"יפיל" לכם בצורה יזומה אך אקראית שירותים (או שרתים) במערכת – כדי שתוכלו לבדוק את ההתמודדות שלכם, ולשפר את צורות התגובה שלכם לכישלון שכזה.
בשלב ראשון ניתן להפעיל אותו בסביבת בדיקות – בה נפילה היא למידה ללא נזק, ובהדרגה ניתן להתקדם לסביבות יותר ויותר מציאויתיות ומחייבות. אם אתם מסוגלים להפעיל אותו ב production, בשעות העומס שלכם, ולשרוד עם שירות סביר – אז אתם בליגה העולמית!
שיהיה בהצלחה!

AWS: היכרות עם SxS – "השירותים הפשוטים" של אמזון – חלק א'

SxS הוא לא באמת מונח רשמי – "המצאתי" אותו לצורך הפוסט. מה שיש הוא:

  • Simple Notification Service (בקיצור: SNS)
  • Simple Queue Service (בקיצור: SQS)
  • Simple Workflow Service (בקיצור: SWF)
  • Simple Email Service (בקיצור: SES)
כל אחד מהשירותים הללו הוא פשוט למדי וממלא משימה בסיסית אחת, מצד אחד. מצד שני, אלו הן אבני בניין חשובות בבניין מערכות על גבי AWS.

Simple Notification Service

שירות SNS הוא שירות הודעות פשוט, מבוסס סכמה של Publish/Subscriber – כלומר: שליחת הודעת לערוץ מסוים (topic) מצד אחד, ורישום ע"י מנוי אחד או יותר לאותו ה topic – שיקבלו את ההודעות ב push. מנוי HTTP, למשל, יספק את ה endpoint המדויק אליו הוא רוצה לקבל את ההודעה.
מקור: אמזון
דפוס ה Publish/Subscriber בא גם לספק decoupling בין השולח לנמען (הם לא מודעים אחד לשני), וגם לספק שליחה של הודעה פעם אחת, והעברה שלה למספר נמענים, לפעמים בפרוטוקולים או פורמטים שונים של הודעה.
SNS מאפשר לשלוח הודעות בפרוטוקולים שונים, למנויים (Subscribers) שונים. הוא מבוסס push, מה שאומר שהודעות נשלחות למנויים ברגע שההודעה התקבלה (אין polling).
דוגמה נפוצה לשימוש ב SNS היא Monitoring של אירועים אפליקטיביים בשרתים (וריאציה מודרנית: שירותים). כשיש תקלה חמורה אנו יכולים לשלוח הודעה מהשירות שלנו SNS, שהוא מצידו ישלח הודעה לשירות ה monitoring שלנו + מייל + SMS לכונן שזמין באותו הרגע.
מן הסתם ההודעות בפורמט שונה, וכאשר שולחים הודעה ל SNS ניתן להגדיר כיצד תראה עבור מנויים מסוגים שונים.
הרישום של המנויים הוא בלתי תלוי בשירות ששולח את ההודעה, ונעשה ישירות מול SNS. אמזון משתמשת בעצמה ב SNS לתפעול AWS. לדוגמה, ניתן להירשם ב SNS להודעות על auto-scaling, הודעות על איבוד נתונים ב RRS של S3, או אירועים שונים ב RDS (בסיסי נתונים המנוהלים ע"י אמזון).
כלומר: SNS מספק לנו שירותים שליחת הודעות בפרוטוקולים שונים (מייל, SMS, וכו'), בזמינות גבוהה, ובצורה מנותקת מהשירות שלנו. השימוש ב SNS חוסך לנו את ההתעסקות עם הפרוטוקולים ועם ניהול המנויים.
ניתן לשלוח בהודעת ה SNS טקסט שונה לכל פרוטוקול של מנוי
(email-json הוא אימייל שנשלח בפורמט JSON ולא text-based).
SNS יודע לשלוח גם הודעות Push לשירותי מובייל (יכולת שנוספה בשלב מאוחר יותר):
  • Apple Push Notification Service (בקיצור: APNS)
  • Amazon Device Messaging (בקיצור: ADM)
  • Google Cloud Messaging for Android (בקיצור GCM)
  • וכמו כן, לשירותי ההודעות של מייקרוסופט (NPNS ו WNS), ושל Baidu (ענק החיפוש הסיני).
התעריפים של SNS נקבעים ע"פ כמות ההודעות שנשלחו לשירות, וכמות ההודעות שהוא שלח למנויים. התעריפים הם שונים למנויים מסוגים שונים: SMS או מובייל (יקר יותר), אימייל (באמצע), או HTTP/S (זול). הודעות שנשלחות ל SQS הן בחינם.

אמינות ההודעות

מה קורה כאשר נשלחה הודעת SNS ללקוח HTTP שכרגע אינו זמין? אולי השרת בדיוק נפל, או שהייתה תקלת רשת ששיבשה את ההודעה? במובייל ייתכן מצב זמני של ביטול הודעות push / הסרה והתקנה של האפליקציה ("Endpoint is disabled").
עבור חלק מהודעות SNS (למשל SMS, או אימייל), איבוד של הודעה אחת מתוך אלף היא לא בעיה גדולה. במקרים מסוימים – זו דווקא כן בעיה, ואז נרצה לטפל בה.

ל SNS יש מדיניות שליחה מחדש ("Delivery Retry Policies"), המאפשרת להעלות את רף האמינות של ההודעות שנשלחות. לצורך עניין זה אתמקד בהודעות HTTP/S.

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

  • הנמען החזיר קוד HTTP מסדרת 5xx. (כאשר יש 404 או 403, זו לא ממש הצלחה, אבל אין כנראה טעם לנסות לשלוח מחדש את ההודעה).
  • עבר timeout של 15 שניות ללא תגובה מה endpoint.
  • תקלת תקשורת ברורה (תקלה בשליחת ההודעה, SSL certificate לא מתאים, וכו').
אם ההודעה נכשלה ניתן לקבוע מדיניות של ניסיונות חוזרים לשליחת ההודעה.
סה"כ ייתכנו עד כ 100 ניסיונות, ובטווח של לכל היותר שעה.
ל SNS יש מודל של 4 שלבים של Retries, בכל אחד מהם ניתן לקבוע קצב retries ומספר ניסיונות שונה ("קודם תנסה בעדינות, אח"כ דפוק בפטישים!"). הגמישות היא רבה למדי.
את מדיניות השליחה-מחדש ניתן לקבוע ברזולוציה של מנוי.
קישורים מעניינים

Simple Queue Service

שירות המאפשר לנהל Queues (תורים), לשלוח ולקרוא מהם הודעות. זו צורת תקשורת מעט שונה מ SNS:

ההודעה נשלחת ל Queue, והשירות שמקבל את ההודעה שולף אותה ביזמתו מה Queue. לאחר שסיים לטפל בה – מוחק אותה (סדר זה נקבע בכדי לשמור על Data שלא יאבד). למבנה ה Queue יש כמה יתרונות:

  • איזון עומסים בין חלקים שונים של המערכת: כאשר קצב ההודעות מה Producer מגיע ב peaks – ה Queue יאזן אותם. השירות הצורך את ההודעות קובע את קצב העבודה לו הוא מסוגל להגיב – ולכן לא ייפגע מ Self Denial of Service.
  • מבנה ה Queue מאפשר לנתק, במידה מסוימת, בין זמן שליחת ההודעה – לזמן הטיפול בה. למשל: לשלוח הודעות ל Queue במשך כל היום אך להעלות ה Service  שמטפל בהן, רק למשך שעות הלילה – ואז לטפל בכל ההודעות באצווה.

לצורך שיתוף Queue בין כמה צרכנים (בד"כ מספר instances של אותו ה service) – לכל Queue מוגדר Visibility Timeout (ברירת מחדל = 30 שניות). אם הודעה נשלפה, היא נחשבת ל "in flight" ואז יש לשירות שאסף אותה 30 שניות למחוק אותה, או לבקש הארכה. אם לא עשה זאת – ההנחה היא שהוא "נכשל" בטיפול בה (למשל: קרס, איבד אותה, וכו') ולאחר 30 שניות היא תהיה זמינה לאיסוף ע"י instance אחר.

ניתן לבצע חלוקת עבודה בין כמה instances, פשוט ע"י שיתוף של Queue ביניהם:

  • ניתן להאזין ל Alert על עומק ה Queue, ואם נראה שהשירות לא עומד בקצב – לייצר לו עוד instances. אם תרצו – אמזון תנהל התנהגות זו עבורכם בעזרת SQS Based Scaling.
  • בכדי לא לבזבז זמן על Polling, ה API של SQS תומך ב "Long Polling". כלומר: קריאה ש"תתקע" למספר שניות שהוגדר, בהמתנה להודעה שאולי תגיע בזמן הזה – במקום לנסות שוב ושוב לבדוק האם ההודעה הגיעה.
הודעה שנאספה מספר פעמים, אך לא הצליחו לטפל בה – מסומנת כ"הודעה מורעלת" ומועברת ל Dead Letter Queue (אם הוגדרה מדיניות מתאימה).

הנה סיכום של כמה מההבדלים בין SQS ו SNS:

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

עבור השגה של ה Scalability, אמזון מציע Soft-FIFO בלבד. כלומר: הודעות יהיו "בערך" First In-First Out, הסבירות שהודעה a תשלף מה Queue לפני הודעה b גדלה ככל שהודעה a נשלחה זמן גדול יותר לפני הודעה b. ערבוב בין הודעות שנשלחו בזמן קרוב מאוד – הוא יחסית צפוי. ערבוב בין פרקי זמן ארוכים יותר (מספר שניות) – הולך והופך נדיר.

גודל ההודעה האפשרי הוא של 256KB.

ההגדרות של Queue, ב SQS

SQS הוא לא שירות יקר, אבל ניתן לחסוך עוד בעלויות ע"י שליחה של אצוות של עד עשר הודעות, במחיר של שליחת הודעה אחת – כל עוד הגודל הכולל של ההודעות באצווה לא גדול ממחסום ה 64KB.
אמזון מספקת ספרייה בשם AmazonSQSBufferedAsync client שמסייעת לנהל את האצוות, כמעט לבד, עבורכם. כרגע היא זמינה (ככל הידוע לי) רק בSDK לג'אווה.

אמזון מציעה מיליון בקשות ל SQS בחודש (לחשבון, בעת כתיבת פוסט זה), קריאה, כתיבה או מחיקה – בחינם. המחיר של כל מליון בקשות נוספות ל SQS הוא כחצי דולר.
אמנם גודל ההודעה המרבי הוא 256KB, אולם אמזון מחשיבה בקשות הגדולות מ 64KB, לצורכי תמחור, כבקשות נוספות. למשל: קבלת הודעה בגודל של 150KB – תחשב בתמחור כ 3 בקשות, למרות שנעשתה קריאת HTTP יחידה.

SQS vs. Kinesis
אמזון הציגה לאחרונה שירות נוסף בשם קינסיס (פירוש השם: "תנועה") – שגם הוא מספק ניהול Queues, אמין, זמין, היכול לתמוך ב Scale גבוה. קינסיס הוא "חיקוי" של Apache Kafka, עם כמה שינויים קלים.

מצד אחד – באמזון טוענים ש "SQS יכול לעמוד בכל Scale".
מצד שני – בעוד SQS הוא מנוהל לחלוטין יש לו כמה "כאבי ראש" שיש להתמודד איתם:

  • עליכם לנהל את ה shards אליהם מחולק ה Queue – בכדי להגיע Scale. כל shard מוגבל לטיפול של אלף הודעות בשנייה.
  • קינסיס הוא "פחות realtime-י": הוא לא יאפשר לתשאל על הודעות חדשות (לכל shard) יותר מ5 פעמים בשנייה (כן, הוא סופר).
  • הנתונים נשמרים על קינסיס כ 24 שעות (מול 14 ימים של SQS).
  • גודל הודעה בקינסיס מוגבל ל 50KB (מול 256KB של SQS),

למה, אם כן, להשתמש בקינסיס ולא SQS?!

על הבדל מהותי אחד, ניתן ללמוד מתוך התמחור של קינסיס:

  • כ 2 סנט לכל שעה בו השירות פעיל, ולכל ~7GB של הודעות שנשלח. כלומר: ~10GB כל שעה, לאורך 24 שעות = 24*2*2 = בערך 1$.
  • כ 3 סנט לכל מיליון פעולות כתיבה ל Queue. כלומר: ~10GB כל שעה, לאורך 24 שעות = בין 15 סנט ל ~7 דולר, תלוי בגודל ההודעות.
  • שימוש ב SQS לתעבורה דומה יעלה בין 10$ ל 360$ (חישוב אצבע, תלוי בגודל ההודעות, שימוש באצוות, וכו').
הבדל נוסף הוא שבקינסיס לא מוחקים הודעה שהתקבלה. אם משהו השתבש בשלב מאוחר יותר ב flow טיפול ההודעות, ניתן "להחזיר את המצביע" כמה שעות לאחור ולהתחיל את התהליך מחדש. כמובן שיש לתכנן את המשך ה flow באופן שיוכל להתמודד עם מאסה של הודעות שנשלחו שוב.
בקיצור: קינסיס (כמו קפקא) היא תשתית שנכתבה להעביר אצוות גדולות מאוד של הודעות – בצורה יעילה, בד"כ: נתונים ב flow של Data processing. אם אתם שולחים GBs של נתונים, מסביב לשעון, כנראה שגם SQS יוכל לספק את השירות – אבל קינסיס עשויה להיות זולה משמעותית ולהתאים יותר לתהליך.

סיכום

בחנו שני שירותים פשוטים, אך חשובים מאוד של AWS: שירות ההודעות, ושירות ניהול התורים.
בפוסט הבא נבחן את שני השירותים האחרים: שירות הדואר האלקטרוני, ושירות ה workflows.

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