שלום, AWS Lambda! (חלק ב\')

בפוסט הקודם שלום, AWS Lambda! הצגנו את למבדה, ובעיקר כיצד ה main successful flow עובד.
לא הספקנו כמעט ולהיכנס לעומק / להתנהגויות השונות במודל של למבדה.

זה מה שננסה לעשות בפוסט הזה.

The Container Model

כאשר מתבצעת הפעלה (invocation) ראשונה של הפונקציה שלנו, למבדה מפעילה "Container" שבו תרוץ הפונקציה.
מהו ה container הזה? אין תיעוד מדויק, אך מדובר בסופו של דבר בסביבת ההרצה של הפונקציה, ע"פ ה settings שהגדרנו.

הרצת ה container אורכת זמן מסוים, נאמר כ 100-200ms – מה שנקרא cold start.
על הזמן הזה – המשתמש מחויב.

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

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

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

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

  • אין קונטיינר זמין – למשל "הרגו" אותו לא מזמן…
  • הבקשה הגיעה ל Availability Zone A, אבל הקונטיינר נמצא ב Availability Zone אחר.
  • יש מספר הרצות מקביל של הפונקציה: אם מספר ההרצות בשניה עלה מ 10 ל 100, למבדה תתחיל להריץ עוד ועוד קונטיינרים במקביל – עד שהצורך יסופק.
    כלומר: ייתכן ויש כבר 6 קונטיינרים עובדים, אך במקום לחכות שקונטיינר יתפנה – למבדה החליטה להפעיל קונטיינר שביעי.

    • בעת כתיבת הפוסט, אמזון לא תריץ יותר מ 1000 קונטיינרים במקביל – אלא אם ביקשתם.
    • למבדה תקבע את כמות המקביליות כתלות במקור ה event:
      • אם מדובר בהודעות ב Queue (או "stream-based-event") – המקביליות תהיה ע"פ כמות ה shards של queue.
      • כאשר מדובר בקריאות ישירות (למשל HTTP) – המקביליות תהיה מספר הקריאות בשניה * כמה שניות לוקח לפונקציה לרוץ בממוצע.
    • בכל מקרה, למבדה היא לא קסם. גם אם יש spike "מטורף" – אמזון לא תפתח יותר מ 500 קונטיינרים חדשים בדקה. אפשר לקרוא עוד פרטים על המדיניות כאן.
  • סיבה אחרת לא מתועדת / שאני לא מכיר.
זמני ה Cold Start תלויים ב:
  • כמובן: הקוד שלכם. אם אתם משתמשים ב ORM, או ספריות שדורשות אתחול משמעותי – הזמן הזה ישולם ב Cold Start.
  • כמות הזיכרון שהוקצתה לפונקציה (=> CPU). יותר זיכרון – אתחול מהיר יותר.
  • כמות הקוד שנטען ב ZIP, ספריות וכיוצ"ב: פחות קוד – אתחול מהיר יותר.
  • קוד ב node.js ופייטון יאותחל מהר יותר מקוד ג'אווה או #C.
זמני ההרצה של 4 עותקים זהים של פונקצית למבדה. הפונקציה memInfo0 נקראת בצורה תדירה – ולכן כמעט ואינה מושפעת מ cold start, לעומת הפונקציות האחרות שחוות cold start כמעט כל פעם: כלל שהפונקציה תדירה פחות – הסיכוי ל cold start גדל. מקור: New Relic.

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

  • network latency – עוד כ 100ms או יותר (?), אלא אם אתם קוראים לפונקציה באותו ה region – ואז ה latency הוא מילישניות בודדות.
  • זמני הריצה של ה API Gateway – כ 30 עד 150 מילישניות (ע"פ נתונים של New Relic).
    • הקמת חיבור מאובטח (TLS/SSL) – עשוי לארוך עוד 50-200 מילישניות.

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

Container reuse

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

  • הגדרות "סטטיות" שנעשו מחוץ ל handler function – עדיין תקפות: חיבור לבסיסי נתונים / רדיס וכו'.
  • Cache ששמרתם בזיכרון – עדיין מלא.
  • דברים שנרשמו לדיסק עדיין שם (יש לכם עד חצי ג'יגה אכסון בתיקיה tmp/)
זה פוטנציאל לשיפור ביצועים משמעותי.
כמובן, שלעולם אי אפשר להסתמך על container reuse: אם הוא יצא – אפשר לנצל אותו, אם לא אז לא. עליכם לכתוב את הקוד בהתאם.
כמו כן, חשוב להבין את התנהגות ה cache בצורה נכונה: אם יש מקביליות גבוהה (concurrent invocation), אזי יש כמה  containers שכל אחד עם cache ב state שונה. חשוב לקחת את זה בחשבון. אם חשוב לכם cache אחיד – פשוט השתמשו ברדיס.
Throttling: a process responsible for regulating the rate at which application processing is conducted
מקור: Robyn

Throttling and retries

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

  • עבור מקורות מסוג Queue (רשמית: "stream-based events") – למבדה תבצע retry לעד. עד שהבקשות מטופלות.
  • עבור מקור קריאה אסינכרונית – למבדה תבצע retry בטווח של עד 6 שעות מרגע הקריאה, עם המתנה בין ניסיון לניסיון, מה שאמור ליצר סיכוי נמוך מאוד למצב של הודעה שלא טופלה.
  • עבור קריאות סינכרוניות (ה event source ממתין לתשובה) – למבדה תחזיר לאפליקציה HTTP status code של 429 – ותצפה שהאפליקציה תבצע retry בעצמה.
Throttling הוא מצב תקין וצפוי בלמבדה – מצב שעליכם לקחת בחשבון! הוא יקרה.
פונקציית הלמבדה שלכם כמובן יכולה להחזיר שגיאות. כאלו שחזרו מלבדה עצמה או מהקוד שלכם.
בלמבדה יש הפרדה בין 2 סוגי Exceptions:

  • Synchronous Exception – כזו שתוצאתה חוזרת למי שקרא לפונקציה.
  • Asynchronous Exception – כזו שתוצאתה מגיעה ל cloudwatch. לקוח הפונקציה לא מודע לדבר.
כשאר הפעלה של פונקציה נתקלה ב Synchronous Exception (שנגרמה ע"י הקוד שלכם או ע"י למבדה) – למבדה תבצע retry ע"פ המדיניות הבאה:
  • עבור מקורות מסוג Queue (רשמית: "stream-based events") – למבדה תבצע retry כל עוד ה data מבחינת ה queue הוא בתוקף.
  • עבור מקור קריאה אסינכרונית – למבדה תבצע retry עוד שתי פעמים, בד"כ בהפרש של כדקה, ואז תשלח את ההודעה ל Dead Letter Queue שהוגדר על ידכם על גבי SNS או SQA.
    • ליתר דיוק בדיקות הראו שאכן ב 99% מהפעמים יהיו 2 retries, אבל לעתים רחוקות יהיה רק retry אחד – ולפעמים גם יבוצעו עד שש retries. מקור.
  • עבור קריאות סינכרוניות (ה event source ממתין לתשובה) – הודעת השגיאה תחזור לאפליקציה, ובאחריותה לבצע retries.
חשוב לציין מגבלה שלא כתובה פה: בעת כתיבת הפוסט, ל API Gateway יש timeout קבוע של 30 שניות. אם יש לכם פונקציה שזמן הריצה שלה ארוך יותר – API Gateway יבצע timeout, בכל זאת.
בשלב הזה אתם אמורים להבין כמעט את כל המדדים שמסופקים ב Monitoring של למבדה:

המדד היחידי שלא הוסבר הוא ה iterator age שרלוונטי רק למקורות מסוג stream-based events.
הוא מציין כמה זמן ארך מרגע שפונקציית הלמבדה שלנו קיבלה batch של הודעות, ועד שסיימה לטפל בהודעה האחרונה. כלומר: מה ה latency המרבי של טיפול בהודעות ב batch, שהגיעו ממקור שכזה.

Lambda@Edge

שווה להזכיר את שירות ה Lambda@Edge ששוחרר לאחרונה, שירות המאפשר להריץ פונקציות למבדה (Nodejs בלבד, כרגע) על ה PoPs (כלומר: Point of Presence) של CloudFront.

לאמזון יש כ 14 regions בעולם עליהם אפשר להריץ את כל שירותי ה AWS, אבל יש לה קרוב ל 100 מיקומים בהם יש לה נוכחות – עבור שירות ה CDN שלה, הרי הוא CloudFront.

עבור פעולות פשוטות למדי, אם תוכלו להריץ את הקוד קרוב מאוד ללקוח שלכם – תוכלו לספק לו תשובה מהירה ביותר: ה latency ל"קוד" שלכם עשוי להיות עשרות מילי-שניות, ולא מאות מילי-שינות.
למבדה_על_הקצה (Lambda@Edge) תנהל עבורכם את שכפול הקוד לרחבי העולם – ותדאג שהלקוחות שלכם, יקבלו זמני תגובה משופרים.

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

מקור: אמזון

כמובן שבשירות כזה מאוד חשוב לספק זמני תגובה מהירים מאוד – מה שלא תמיד מתרחש בצמד למבדה + API Gateway.

ה API Gateway הוא מחוץ לתמונה, יצירת ה HTTP connection עם TLS/SSL – כבר מתבצע ע"י cloudfront, ולכן הרכיב היחידי שעלול לגרום לעיכובים (ולייתר את היתרון שבריצה על PoP) הוא זמן האתחול של ה container של למבדה. לא סתם התחילו ב nodejs כסביבת-ריצה.

ללמבדה_על_הקצה – יש מגבלות משלה (מקסימום 128MB זיכרון, זמני ריצה קצרים יותר, אין גישה לשירותים אחרים של אמזון, וכו')

שירותים נוסח למבדה_על_הקצה כבר היו קיימים (למשל: PubNub Blocks) – אבל לאמזון יש יתרון מובנה מול מתחרים קטנים יותר.

יהיה מעניין!

סיכום

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

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

—-

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

Lambda@Edge

שלום, AWS Lambda!

למבדה, שירות ה FaaS של AWS, הוא שירות המאפשר להריץ "פונקציות" בצורה מנוהלת:
אין צורך לחשוב על שרתים, על תשתיות, High Availability ו Fault tolerance, או על Scaling.

אתם כותבים את הפונקציה, ומוסיפים קצת הגדרות.
שירות למבדה יעשה עבורכם את הניהול: הקצאת CPU, רשת ו I/O, זמינות, טיפול ב scale, וכו'.

ללמבדה יש גם גם כמה נקודות שכדאי להיזהר איתן:

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

בעת כתיבת הפוסט, השירות תומך בסביבות הריצה של Java, Node.js, Python, ו #C.
עם מעט "tweaking", ניתן להריץ שפות נוספות כמו Go על גבי סביבות הריצה הקיימות.

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

מקור: אמזון

שלום, למבדה!

הנה פונקציית ה Hello World בלמבדה. היא מאוד פשוטה.

ה event הוא אובייקט ה input שלנו, הוא יכיל את כל המידע שזמין להפעלת פונקציית הלמבדה. פונקציית למבדה היא מעין "טריגר ל event" – ועל כן המינוח.
בג'אווה וב #C יש וריאציה בה ניתן לקבל את ה input כ stream.

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

ה callback ספציפי למימוש של Node.js – ולו קוראים כאשר סיימנו את הפעולה (אם אנחנו רוצים לשדר פלט).
הפרמטר הראשון הוא error (אם יש), השני הוא אובייקט התוצאה, שיעבור ()JSON.stringify.

מדוע בחרתי ב node.js עבור הדוגמה?
זו הסביבה הכי קלה לשימוש והכי מתועדת של למבדה. ג'אווהסקריפט היא קלילה (זמן טעינה מהיר) ואינה צורכת הרבה זיכרון – מה שטוב עבור פונקציות למבדה. גם פייטון נמצאת במקום טוב, אבל התיעוד על Java ו #C – נמצא מאחור.

מה עושים עם הקוד? "איפה שמים אותו"?
בואו נראה קצת מסכים (מה AWS Console) – ה UI עוזר לשקף דברים שלא נראה ב aws-cli:

הלכתי ב AWS Console (קרי – ה UI) לאזור של למבדה, ויצרתי פונקציה חדשה.
אני אבחר את ה template הריק. באופן כללי אני לא אוהב להסתמך על templates…

טריגרים? – לא מעניינים אותי. אני רוצה רק קוד! נקסט!

  1. שם הפונקציה, כפי שיופיע ב console ובכלי האדמיניסטרציה.
  2. כאן בוחרים סביבת-ריצה. לסביבות השונות יש defaults שונים וגם פרמטרים שונים לקונפיגורציה.
  3. יש אופציה להקליד את קוד הפונקציה בתוך online editor – אבל בסביבה אמיתית הרבה יותר הגיוני לטעון את הקוד ל S3 – ולהשתמש בו משם. ממש path לשם הקובץ.

הנה עוד הגדרות, והן קצת יותר מעניינות:

  1. אמנם כמות הזיכרון המוקצה לפונקציה היא תחת "Advanced setting" – אך יש לה חשיבות גדולה.
    אמזון לא מכירה את הפונקציה שלנו ולא לוקחת "אחריות" על הקצאת הזיכרון. זו אחריות שלנו.

    1. ללמבדה יש monitoring שיראו לנו אם הפונקציה שלנו מתקרבת לקצה הזיכרון שמוקצה לה (או כשלה בשל מחזור בזיכרון) – ואז אפשר להגדיל את נפח הזיכרון. המקסימום הוא 1.5GB.
    2. החיוב בלמבדה נעשה ע"פ GB-second שנצרכו ע"י הפונקציה, כאשר אמזון מעגלת את זמן ריצת הפונקציה לרזולוציה של 100ms כלפי מעלה. פונקציה שרצה במשך 6ms – תחויב בעשירית GB-second על ההרצה הספציפית.
    3. למבדה תקצה CPU לפונקציה ע"פ הזיכרון שהוגדר. לא מפורט כמה CPU זה יהיה, אך יש קשר ישיר בין כמות הזיכרון שהוקצתה – ל CPU שיוקצה: על מכונה עם 4GB זכרון, פונקציה שהוקצה לה 1GB זיכרון – תקבל 25% מה CPU time – יחס ישר. המודל הזה מפשט לאמזון את ה scheduling של invocation של פונקציות – לחומרה הזמינה. הגיוני.
      1. המשמעות הראשונה היא שאם יש לי פונקציה שזקוקה להרבה CPU, ומעט זיכרון – יהיה עלי להקצות עבורה הרבה זיכרון בכדי לקבל חלק גדול יותר מה CPU.
        בחישובי עלות, פעמים רבות יהיה יותר זול להקצות לפונקציה הרעבה ל CPU יותר זיכרון/CPU. היא תרוץ מהר יותר – וכף נחוייב על פחות GB-sec.
      2. למרות שאין תיעוד ברור בנושא, נראה שמעל הקצאה של 1GB – ייתכן ופונקציית הלמבדה תקבל הקצאה של שני cpu cores. אין מניעה להשתמש ב threads בתוך פונקציית למבדה.
    4. מעניין לציין, שה default ב UI של nodeJs הוא 128MB זיכרון, בעוד זה של ג'אווה הוא 512MB זיכרון. לא קשה להגיע בג'אווה לצריכה גבוהה של זיכרון – בשל tradeoff תכנוני שנלקח ע"י מתכנני ג'אווה וה JVM. אמזון לא רוצה להכשיל אתכם – ולכן מקצה ב default גבוה מספיק עבור רוב הפונקציות האפשריות.
  2. עלי גם להגדיר timeout להרצת הפונקציה. ישנן פונקציות ש invoication שלהן עשוי לארוך דקה או שתיים – וזה בסדר. בפונקציה כמו שלי ("שלום עולם") – זמן ריצה של דקה משמעו באג שגרם ל infinite loop, שיגרום לי לשלם הרבה יותר ממה שהתכוונתי. האחריות על ה tradeoff בין סיכון לקטיעת הפונקציה ובין סיכון לתשלום מוגזם – היא על המשתמש (אבל ה default הוא הגיוני).
    1. לא ניתן כיום לקבוע timeout ארוך מ 5 דקות לפונקציה. אם יש לכם עיבוד כבד (נאמר: ETL) – אזי יהיה עליכם לשבור את העבודה ל chunks קטנים יותר. זה בריא גם בשבילכם מהסיבות הנ"ל.
  3. כאן אני מצביע על נקודת ההרצה של הפונקציה (ה "main" שלה).
    במקרה של nodejs, אם שם הקובץ שלי הוא index.js, ועשיתי export לפונצקיה בשם handler אזי עלי להקליד "index.handler". לכל סביבת ריצה יש חוקים מעט שונים.
  4. אלו הן הרשאות הריצה (execution) של הפונקציה – הרשאות לגשת לשירותים אחרים של AWS.
    יצרתי role עם הרשאות ה default (קרי basic execution policy – גישה לכתיבת לוגים וזהו) – וקראתי לו lambda_basic_execution – שאוכל להשתמש בו בפונקציות נוספות.

אני ממשיך הלאה, מאשר את יצירת הפונקציה, ומגיע למסך ניהול הפונקציה:

הוספתי שורת קוד של logging בכדי להדגים את כלי האדמיניסטרציה של למבדה. היכולת לכתוב לוגים היא חשובה מאוד – זו הדרך הכי יעילה לבדוק איך הפונקציה רצה. ב nodeJS פעולת logging נעשית באמצעות console.log (הרי "קל" לדרוס את console). בג'אווה, למשל, מקבלים את ה Logger מתוך ה context.

אני יכול ללחוץ על פתור "Test" – ולראות את הפונקציה שלנו בפעולה!

  1. הנה ה output של ריצת-הבדיקה. יש פלט – זה כבר טוב! אחרת הייתה לי הודעת שגיאה.
  2. הנה הלוג של ריצת-הבדיקה – אני יכול לראות שם את הלוג שכתבתי.
  3. מאחר ולא הרצתי את הפונקציה לאחרונה, אנו רואים ביצועים של cold state. בהרצות הבאות שאריץ בטווח של דקות – הביצוע ייקח פחות מ 1ms (זמן הביצוע הוא רק של הקוד שלי, ולא כולל את החלק של למבדה). ה cold start – הוא נושא לפוסט המשך.
  4. הנה אפשר לראות את צריכת הזכרון של ההרצה. בהרצה עוקבת הזיכרון מעט יותר גבוה: 22-24MB. מדוע? – פוסט המשך.
כמובן, שברגע שאני רוצה שהפונקציה שלי תהיה reproducible (ליצור אותה קודם על סביבת בדיקות ורק אז ליצור בדיוק אותו דבר בסביבת הפרודקשיין), אני לא אשתמש ב UI אלא בכלי ניהול תצורה כמו Terraform בכדי לייצר אותה.

למבדה Prison Break

איזה יופי! איזו גאווה! כתבנו פונצקיית למבדה שרצה. אשכרה רצה!
אני לוחץ שוב, ושוב על כפתור ה "Test" – ואני מרוצה.

אבל נניח שאני רוצה לקרוא לה ממקור שאינו ה UI של אמזון. כרגע, זה לא אפשרי…

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

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

לפונקציות למבדה יש שני סוגי הרשאות:

  • הרשאות ריצה (Execution) – הרשאות לגשת לשירותים אחרים של AWS.
    • את ההרשאות האלו מגדירים כ IAM role – כמו "lambda_basic_execution" שהגדרנו למעלה.
    • בתיעוד של למבדה קוראים לחלק הזה גם בשם Pull Model – מה הפונקציה שלכם יכולה "למשוך".
    • לפעמים הטריגר של הפונקציה יהיה רק scheduler ("רוצי כל 5 דקות") ואז נפונקציה תקרא ל SQS או Kinesis – למשוך משם נתונים ולטפל בהם.
  • הרשאות הפעלה (Invocation) – הרשאות הנדרשות ע"י ה trigger / event source – על מנת להפעיל את פונקצית הלמבדה.
    • את ההרשאות מגדירים גם כ IAM role – אבל על מי שרוצה לקרוא ללמבדה, למשל: API ב API Gateway.
    • בתיעוד של למבדה קוראים לחלק הזה גם בשם Push Model – מי מפעיל את הפונקציה שלכם.
נגדיר טריגר מסוג AWS API Gateway.

כמובן שעדיף להגדיר IAM role, אבל כרגע לצורך הדוגמה – אני בוחר ב "Open": כל מי שיש לו את ה URL יכול להפעיל את הפונקציה (ולחייב אותי).

סיימנו. תוכנית הבריחה הושלמה!

אני לוחץ על קישור 1 – שהוא ה URL של ה API שיצרתי:

אופס! ב Prison Break, כמו ב Prison Break – תוכניות משתבשות. צריך לעבוד על עוד משהו.
אני בודק שוב: הפונקציה שלי תקינה.

אז מה הבעיה?

בלמבדה זו שאלה שתשאלו את עצמכם לא מעט. כלי העבודה העיקרי – הוא לוגים.

לכן אני אלחץ על קישור 2 – שלוקח אותי ל view הכללי  של ה API שהגדרתי ב API Gateway. משמעות המונח "ANY" = כל מתודות ה HTTP.

המסך הזה ממש "מצייר" את ה flow שמתרחש: מהלקוח, דרך ה API Gateway (קודם צד ה HTTP, ואז צד ה Integration / Transformation), עד ללמבדה – ובחזרה.

אני יכול להקליד על 1 לראות את מהות האינטגרציה. מכיוון שה Integration type הוא lambda_proxy – אזי כמעט ואין מה להגדיר. כמעט הכל מנוהל ע"י אמזון. כנ"ל לגבי הטרנפורמציה בחזרה.
קישור 2 – מוביל אותי חזרה למסך של ניהול פונקציית הלמבדה שלי.
אני לוחץ על קישור 3 – שמאפשר לי לבדוק, מקצה לקצה, את ה API ביחד עם פונקציית הלמבדה.
שם, בתוך הלוג – אני מוצא את הבעיה:

לפני הטרנספורמציה הכל נראה בסדר.
שניה! איזו טרנספורמציה?

קריאת ה HTTP עצמה לא מתאימה בדיוק לאובייקט ה event שאנחנו מקבלים. מישהו לקח את ה HTTP payload (טקסט רציף), פרסר אותו וסידר אותו יפה במבנה היררכי של פרמטרים. מי שעשה את זה הוא ה lambada_proxy של ה API gateway: הוא המיר את ה HTTP למבנה json שלמבדה יודעת לקבל ולעטוף באובייקט ה event.

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

הנה, שינינו את הקוד למבנה המצופה ע"י ה lamda_proxy והנה התוצאה:

איזה כיף!

סיכום

"ליאור, למה כל הסיבוב הזה? למה לא כתבת את הקוד הנכון מלכתחילה? גם ככה הפוסטים שלך ארוכים!!"

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

מקווה שהצלחנו.

כמובן שיש עוד כמה דברים בסיסיים וחשובים שכדאי לספר על למבדה. בלי נדר, אולי יהיה פוסט המשך.

עדכון: הנה חלק ב' של הפוסט.

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

רפרנסים

מדריך למפתח של אמזון על פונקציות למבדה
AWS Lambda Blog

ארכיטקטורות ללא שרת (Serverless Architectures) – מה זה השטויות האלה?!

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

\"עכשיו תעשה לי את זה בלי שרת\" – מבקש המראיין.
ווב, בלי שרת? כלומר… Rich Client שלא מתקשר עם אף אחד?

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

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

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

הרי קשה להתעלם מהבאזז המוזר הזה של \"Serverless Architectures\", באזז שכולל Frameworks, ספרים, ואפילו Conference שמוקדש לנושא.

בפוסט הזה אנסה להסביר את הנושא. נראה שבעצם כל התשובות נכונות.

הכרזה של ServerlessConf בניו-יורק

אז מה בעצם הכוונה ב \"Serverless\"?

כמו באזזים שונים שנתקלתי בהם בעולם התוכנה לאורך השנים (\"SOA\", \"MVC\", או \"Big Data\"), חוסר בהירות או אחידות בהבנת העקרונות הבסיסיים מאחורי הבאזז – לא מונעים מהבאזז לפרוח (אולי אפילו להיפך). זה קצת כמו הברקזיט: קודם מצביעים – ואז הולכים להבין במה מדובר 😉

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

BaaS (קיצור של Backend as a Service) – שירות המאפשר למפתחי צד-לקוח (בעיקר מובייל, ולכן לעתים נקרא גם MBaaS) לבצע bootstrapping[א] מהיר ללא הצורך בידע והשקעה בצד השרת.
שירותים כמו parse.com (שנקנתה ע\"י פייסבוק ואח\"כ נסגרה), Firebase, או Kinvey הפכו פופולריים בעולם הסטארטאפים, וקיימים גם פתרונות פופלארים בקהילות הסגורות של עולם ה Enterprise (יבמ, אוקרל, סאפ) שבוודאי יהיו פחות מוכרים לאיש התוכנה הממוצע.

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

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

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

FaaS (קיצור של Functions as a Service) היא הגל השני של ה Serverless Architectures, שעלה לכותרות בעיקר בעקבות ההצגה של שירות AWS Lambda. למרות שהשירות שוחרר בגרסתו הראשונית עם מגבלות רבות, הוא הגיע משחקן מוכר ומוערך מאוד (אמזון), עם מחיר כניסה אפסי (אפשר להתנסות בו במחיר של סנטים), ובמרחק נגיעה – כי למאות אלפי (?) מתכנתים יש כרטיס אשראי כבר מוזן באמזון, והם יכולים להפעיל את השירות במרחק של קליק.

הפתרון, בגדול, מאפשר לכתוב ולעשות deploy לפונקציה בודדות – שתופעל כתגובה לקריאה ל endpoint (נקראים: \"event sources\") שנגדיר.
תפעול הפונקציות הללו (ניהול עומס, monitoring, ו High Availability) מנוהל ע\"י אמזון. רק מעדכנים את קוד הפונקציה – ואמזון דואגת לשאר.
בעצם FaaS הוא סוג של PaaS (כלומר: Platform as a Service, סליחה על השימוש התכוף בבאזזים) ברזולוציה קטנה הרבה יותר: במקום אפליקציה – פונקציה.

מאז הצגת AWS Lambda הזדרזו המתחרים להציג שירותים מקבילים, והיום יש לנו גם את Google Cloud Functions (עדיין באלפא, בעת כתיבת פוסט זה), את Azure Functions (גם הוא חדש), ואת OpenWhisk של יבמ (שרצה על פלטפורמת Bluemix של יבמ, פלטפורמה מוכרת בכנסים – וקצת פחות מפתרונות בפרודקשיין).

במקביל לענקיות הענן, ישנם גם מפתרונות של שחקנים קטנים, למשל:StackHut או WebTask.

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

תת-וריאציה (מעט משונה) של FaaS היא DBFaaS, הגישה הזו גורסת שלעתים קרובות אנו זקוקים ב FaaS לפנוקציות שמבצעות עיבוד קל על נתונים. היכן יותר יעיל לשים את הפונקציות הללו – אם לא ב Database עצמו?
לחברת SAP יש מוצר בשם HANA, בסיס נתונים in-memory, שמאפשר ממשק וובי ישיר ל stored procedures שלו.
גישה זו שוברת כמעט כל מוסכמה של Layered Architecture או MVC, ומציבה דאגות רבות (Scalability? אבטחה? יכולת debugging ותהליכי תוכנה נכונים?). אני עדיין לא מצאתי איזון בריא לגישה הזו – אך אין ספק שהיא מערערת מוסכמות וגורמת לחשיבה מחודשת של הדברים. למה בעצם לא??

טרנפורמציה של ארכיטקטורת Client-Server לארכיטקטורת Serverless. מקור: Bliki

Serverless Web Site (אין קיצור מגניב)

תצורה אחרונה, וקצת פחות מהותית מהאחרות אליה מתייחסים כ Serverless Architecture היא לארח אתר אינטרנט (לרוב עם פונקציונליות מוגבלת, ובעיקר חוסר-תקשורת בין המשתמשים השונים) כ Static Content, ובניית כל הפונקציונליות ב JavaScript.
את קובצי ה HTML/CSS/JavaScript מארחים בשירות אכסון כמו S3 או אפילו עדיף – CDN.

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

כמה שהגישות השונות שהצגתי הן שונות זו מזו – יש ביניהן סינרגיה.

כאשר אנו משתמשים ב BaaS, תחת היכולות הגנריות שמציע השירות הספציפי – מה יותר טבעי מלהוסיף קצת custom server functionality כ FaaS?

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

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

מקור: אמזון. לטעמי השימוש כאן במונח \"Microservice\" הוא לא ממש מדויק.
אם היו גם מציינים גם Big Data, דוקר, ו IoT – זה היה כבר יותר הגיוני 🙂

אז מה הם באמת התכונות העיקריות של ארכיטקטורה \"ללא-שרת\" (קרי: מבוססת FaaS)?

אפשר לייחס ל Serverless Applications את התכונות הבאות:

  • התבססות במידת האפשר על שירותי צד-שלישי.
  • ה Flows העיקריים של האפליקציה מנוהלים בצד-הלקוח. צד השרת מספק בעיקר \"פונקציות\".
  • בניגוד לשרת שחי לאורך זמן, וטיפל בבקשות שונות – ב Serverless Architectures המופע של צד-השרת חי לאורך טיפול בבקשה. יכולים להיות אלפים כאלו שמופעלים ונדומים בשנייה.
    • זו תכונה טכנית יחסית, שעשויה להשתנות. ייתכן והספקים יתחילו לספק הנחות למי שמבטיח כמות מסוימת של שימוש בשעה – והדרך להוזיל עלויות תהיה כן להחזיק כמות מסוימת של instances קבועים.
    • יתרונות נוספים של instances קבועים הם cache \"חם\" (ברמות השונות) וביטול / קיצור ה initialization latency.
  • ארכיטקטורות Serverless לוקחות את רעיון ה Continuous Deployment לאקסטרים חדש: כעת ניתן, בכל רגע, לבצע דיפלוי ברזולוציה של פונקציה בודדת.
יתרונות:
  • חסכון בעלויות, כי משלמים רק על מה שרץ – במיוחד כאשר יש traffic דליל. בסקייל בינוני ומעלה – זה יותר יקר.
  • יותר אופרציה מנוהלת ע\"י ספק השירותים, או קצת בהקצנה: noOps#. רעיון ה Platform as a Service (בו כמעט כל היבטי ניהול אפליקציות מבוצע ע\"י הספק) לא הצליח כ\"כ בפועל, אך יש סיכוי שמודל ה FaaS יצליח יותר.
  • כאשר כל פונקציה (עם מאפייני הביצועים הייחודיים שלה) היא מבודדת – קל יותר לעשות Scaling. טעות נפוצה היא ש FaaS הוא \"infinitely scalable\". כאשר יש לפונקציה state משותף כזה או אחר – צווארי הבקבוק של ה scalability יהיו שם – כפי שהיה תמיד.
  • אפשרות לקרב את הפונקציה למשאב אחר, למשל: קרוב מאוד לשירות צד-שלישי ולבצע חישובים שם, קרוב לנתונים, או אולי קרוב ללקוחות (למשל: לשים פונקציות שירוצות אצל ספק ה CDN וכך יספקו תשובות מאוד מהירות למשתמשי הקצה שלי, בכל רחבי העולם).
חסרונות:
  • חלוקה של מערכת להרבה פונקציות קטנות דיי מהר הופכת לאתגר תפעולי מצד המתכנתים. רבים מהמימושים המוצלחים של FaaS כרגע – מבוססים על פונקציות מעטות.
  • חלוקת המערכת להרבה פונקציות קטנות עלולה ליצור קשיים בהשגת אמינות גבוהה: הרשת יכולה לשבש קריאות בין פונקציות שונות – ואם אנו זקוקים ל\"אפס פספוסים\", אנו נצטרך להתגונן בפני כשל של הרבה מאוד נקודות אינטגרציה.
  • Lock-In ל Vendor המספק לכם את השירותים. אולי הפונקציות עצמן מבודדות (isolated), אך סביר שהן משתמשות ב infrastructure נוסף (הרשאות, API Gateway, אכסון נתונים, וכו\') שכובל אתכם לספק הספציפי.
  • כאשר כל פונקציה רצה בפני עצמה בשרת צד-שלישי, ולא ניתן להריץ את הקוד לוקאלית – הפיתוח נעשה מורכב יותר. 
    • לא יפתיע אותי אם נראה בקרוב יותר ויותר אמולטורים שמאפשרים להריץ את פתרונות ה FaaS (בצורה פחות יעילה / אמינה – רק לצורך פיתוח) על המחשב המקומי.
  • לפחות כרגע, לפתרונות כמו AWS Lambda יש Initialization latency לפונקציה – כלומר: אתחול הפונקציה מוסיף לזמני התגובה כ 100-600 מילישניות (ע\"פ דיווחים שונים). זה בסדר ל Event Processing, אבל קצת פחות טוב ל UI או מערכות semi-realtime.

    איזה סוג של מערכות אם כן, מתאימות יותר ל Serverless Architectures?

    • UI-Centric Apps – אפליקציות בהן חווית המשתמש היא המרכזית, והצרכים מצד השרת הם מצומצמים. אלו יהיו בעיקר אפליקציות מובייל – אך לעתים גם מערכות ווב.
    • Message-Driven Systems – מערכות שעיקר עיסוקן הוא טיפול באירועים, למשל: טיפול בטרנזקציות בנקאיות, איתור Fraud, או מערכת שמחליטה איזו מודעה להתאים למשתמש. מערכות אלו ניתן לפרק בקלות יחסית לסדרה של פונקציות, כאשר יש הגיון לשפר כל פונקציה בנפרד: הן מבחינת ביצועי תוכנה = לרוץ מהר יותר, והן מבחינת ביצועים עסקיים = לתת תשובה קצת יותר מדויקת.
      • אם המבנה הלוגיקה העסקית הוא של Pipes and Filters – מבנה ועקרונות של Serverless Architectures עשויים בהחלט להיות רלוונטיים.
      • באזז נוסף שמתקשר ל Message-Driver Systems ו FaaS הוא IoT (קיצור של Internet of Things): אירועים שמגיעים מהמון מכשירים קטנים.
    • חיזוק לנאמר לעיל: מערכות בהן אין (או כמעט ואין) State בצד השרת.
      • בשפה של תכנות פונקציונלי: הפונקציות של FaaS הן Pure Functions.
    • (קצת תיאורטי) יש טיעון שמערכות שחוות spikes גדולים מתאימות ל Serverless Architecture בגלל הציפיה לתשלום ע\"פ גרנולריות מדויקת של שימוש, ויכולת Scalability גבוהה כאשר הפונקציות הן stateless.
      • כאשר AMI של אמזון (שנבנה בצורה המתאימה) יכול לעלות ולהיכנס לשימוש בטווח של דקה או שתיים – רוב ה spikes יכולים להיות מטופלים בצורה סבירה עם autoscaling. קשה לי להאמין שהעלויות של FaaS יוכלו להתחרות בעלויות של ניהול התשתית בעצמנו (IaaS) – כאשר מדובר ב Scale גבוה. אין ארוחות חינם.

    לגבי העלויות ארצה לציין זאת במפורש: יש אינסוף אזכורים לכמה AWS Lambda עשויה זולה יותר מ EC2. שמעתי לא פעם את המספר \"80% חסכון!\". זה סיפור טוב. כשצוללים לפרטים, ניתן לראות סיפורים על חסכון משמעותי – כאשר יש 3 קריאות ביום (מול שרת t2.micro).

    יותר קשה למצוא ניתוחים של עלויות AWS Lambda ב scale גבוה. כל הניתוחים הללו שראיתי (הנה דוגמה) מראים מחיר גבוה יותר בשימוש ב Lambda מאשר בשימוש ב EC2.

    סיכום

    \"כמה זמן יעבור עד ש Serverless Architectures יהיו הסטנדרט?\" – היא שאלה שכבר שמעתי.
    \"כמו שהענן הולך להעלים את תצורות ה On-Premises… הרי Serverless יעלימו את ה Client-Server\"

    האמת – שאני לא יודע.
    ייתכן וזה באזז שיחלוף, וייתכן שלגישת ה Serverless Architectures תהיה השפעה רבה על עיצוב מערכות מכאן ואילך.

    אנשים רבים מבלבלים או מחליפים בין Microservices ל FaaS: מתארים פונקציה בודדת פשוטה (FaaS) כמיקרו-שירות.
    למשל: שמעתי מישהו שהסביר שמיקרו שירות הוא Fully Stateless בהגדרה, ואסור שיהיו לו תלויות בשירותים אחרים.
    הוא תיאר ככל הנראה פונקציה ב FaaS – ולא מיקרו-שירות.

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

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

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

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

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

    [א] קרי – להעמיד מוצר ראשוני / התחלתי

    AWS Elastic Load Balancer

    אחד מהרכיבים הבסיסיים של ה Elastic Cloud Compute (בקיצור: EC2) של אמזון הוא ה Elastic Load Balancer (בקיצור: ELB).

    Load Balancer הוא \"לא כזה נושא מורכב\" – הוא מחלק תעבורה בין כמה nodes ב cluster.

    אם כך, אז מה יש להמשיך ולהתעמק?!

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

    אמנם ELB הוא \"רק Load Balancer\", אך הוא גם מערכת מבוזרת בפני-עצמה, עם קשרים הדוקים לשירותים אחרים של אמזון כמו EBS, CloudWatch, Route53, ו Auto-Scaling.

    מצד שני, ELB הוא גם דיי פשוט בפונקציונליות שלו, מה שהופך אותו לקצת שונה מ Load Balancers של \"העולם הישן\": Nginx, HAProxy, או (F5 Local Traffic Manager (LTM.

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

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

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

    רק לקוחות מעטים, עדיין משתמשים בפתרונות כמו HAProxy או Client-Side Load Balancing – פתרונות שיכולים לספק יותר אמינות וגמישות, אך במחיר לא קטן של תפעול מורכב יותר. התפעול של Load Balancing בין AZs הוא לא דבר פשוט.

    בפוסט זה אנסה לתת סקירה קצרה אך מעמיקה מספיק – בכדי להביא את הקורא להבנה טובה של ה Amazon Elastic Load Balancer.

    תסריט בסיסי

    נתחיל עם הת\'כלס: תצורת הבסיס של ELB נראית כך:

    • משתמש רוצה לגשת לשרת הווב שלנו, המנוהל כ Cluster מאחורי ה ELB.
    • ראשית המשתמש פונה ל DNS (של אמזון, או אחר) על מנת לקבל את כתובת ה IP של האתר שלנו: our-site.com.
      • התשובה של ה DNS תהיה alias נוסף שמפנה ל ELB שלנו. משהו בנוסח:  blabla123…elb.amazonaws.com
      • על המחשב של המשתמש לפנות שוב ולקבל כתובת IP מ Route53 – ואז הוא יקבל כתובת IP אליה יוכל לשלוח את הבקשות.
    • באמזון בעצם יש Load Balancing דו-שלבי: ראשית Route53 מנתב בין ה regions ול ELB הנכון, ואז ה ELB הנכון מנתב ל instance הנכון ב AZ הנכון.
        • אסור לנסות ולשמור את כתובת ה IP שחזרה לנו מה DNS alias של ה ELB. ה ELB הוא בעצם מערכת מבוזרת, ולא Appliance יחיד:
          • מכונות נופלות וקמות – ואז ה IP משתנה.
          • כנראה ומאחורי תמונת \"ה ELB היחיד\" שלנו, בעצם יש כמה מכונות שמשרתות אותנו – ואנו לא רוצים לפנות רק למכונה אחת (ולהעמיס עליה, או להיות תלויים בה במצב של כשל).
    • בשלב הבא ה ELB מפנה את הבקשה לאחד ה instances שרשומים אצלו. איזון העומס ב ELB עובד באחד משני האופנים הבאים:
      • Round Robin – או ליתר דיוק, איזון המבוסס על ספירת ה tcp connections הפתוחים לכל instance בכל רגע (מקור)
      • אם מחברים את ELB ל Cloudwatch, ניתן להתבסס באיזון על מדדים יותר איכותיים ממספר ה connections – כמו CPU Utilization או צריכת זכרון.
        • אם יש לנו instances שאינם באותו הגודל (לא מומלץ), או שה instances מטפלים במשימות בסדרי-גודל שונים לחלוטין של עבודה (לפעמים x, לפעמים 100x) – כדאי, ואפילו חשוב לבצע את החיבור הזה.
    זהו, ה flow הבסיסי הסתיים ואנו מעבירים בקשות לשלושת ה instances מהמשתמשים שלנו. hoowhoo!

    High Availability

    כמובן שהדבר הכי בסיסי שניתן לעשות עבור HA הוא לעבוד cross-AZ. אם AZ (שהוא Data Center פיסי) כושל – ה AZ האחרים ימשיכו לספק את השירות:

    • ELB מתואר כשירות ברמת ה Region. שום כשל של AZ לא יפיל אותו.
      • מה שקורה בפועל הוא שיש מכונות של ה ELB בכל ה AZ, הרי ה ELB הוא מערכת מבוזרת.
      • אם לא היה ברור: מכונות מ AZs שונים מתחברות לאותו ELB, כך שהוא מאזן cross-AZ.
    • ה ELB יכול (מומלץ מאוד) לבצע Health Check ל instances לדעת אם הם במצב תקין.
      • גם אם ה VM לא קרס (זה משהו ש ELB יידע ממנו לבד) – יכול להיות ש instance ספציפי שהתוכנה \"נתקעה\" בו, process חשוב נפל, וכו\'.
      • אנו יכולים לספק URL בתוכנה שלנו שיספק אינדיקציה לבריאות המכונה. זה יכול להיות דף ה login (מדד חלש) או endpoint מיוחד שבודק גם קישוריות ל DB ו flags פנימיים (מדד חזק יותר). כל עוד ה URL שסיפקנו מחזיר קוד 200 – ה ELB מבין שה instance במצב תקין.
      • אם ה instance אינו תקין, יש מנגנון הדומה מעט ל Circuit Breaker שבודק זאת שוב ואם instance נראה לא תקין – ה ELB יחדול לשלוח לו traffic עד שהוא נראה שוב במצב תקין. 

    הגדרות ה HealthCheck במסך יצירת ה ELB ב AWS Console.
      • ה ELB ידגום כל instance כל 5 שניות (הערך יכול להיות בין 5 ל 300 שניות). אם התוצאה לא הייתה HTTP 200, או שעבר response timeout של יותר ממה שהוגדר (ברירת מחדל: 5 שניות) – זו בדיקה שנכשלה.
      • אם יש 2 בדיקות ברצף שהן לא טובות – ה ELB ידליק \"סטטוס אדום\" ל instance ויפסיק לשלוח לו traffic.
        • שימו לב שהגדרות ברירת המחדל מכתיבות מצב בו instance שלא עונה יתפקד ויקבל traffic עד כ 15 שניות עד שינותק (5 שניות מאז דגימה אחרונה, ו 2 * 5 שניות timeout). זה אולי default סביר לאתר אינטרנט, אבל עבור מערכות realtime / קריטיות – ייתכן ותרצו להדק את התנאים.
        • יש הגדרה שנקראת connection draining שתאפשר לבקשות שכבר בטיפול לנסות ולהסתיים לפני שה ELB מנתק את ה instance הבעייתי. יש timeout (ברירת מחדל: 5 דקות) לאחריו ינתקו את ה connection ויודיעו ללקוח המרוחק שלא לנסות ולהמתין עוד לתשובה מה instance הבעייתי.
        • גם כאשר ה instance מנותק, ה ELB ימשיך לשלוח בדיקות health check – ולאחר רצף של 10 הצלחות – יחזיר אותו לפעול (\"מצב ירוק\") וישלח לו traffic.
      • אם אתם משתמשים ב Auto Scaling חשוב שתחברו אותו ל ELB כל ש instances חדשים ה Auto Scaling מרים – יירשמו ל ELB בצורה אוטומטית (וגם להיפך: יוסרו אוטומטית).
        • אפשר לכוון את Auto Scaling להגיב גם למה ש ELB חווה מהשרתים מבחינת זמני-תגובה, ולא רק למדדים סינתטיים (כמו CPU או זיכרון).
    • Route 53 מבצע Health Checks ל ELB, ואמור לזהות כשל של AZ תוך 150 שניות – ואז להפסיק לו את ה Traffic עד שהוא חוזר לתפקד.

      יש אנשים שמנסים \"לשפר את ה Availability\" של המערכת ע\"י הגדרת ELB נוסף שהוא redundant (ורישום שלו כ alias נוסף ב DNS). הדבר הזה אפשרי, אך לרוב הוא כמעט חסר משמעות:
      • ELB היא תשתית מבוזרת ו multi-tenant. ה ELB הנוסף לא ירוץ על חומרה אחרת / vector אחר של המערכת – אלא על אותה התשתית בדיוק.
      • אם אתם כבר רוצים, התקינו Load Balancer מסוג אחר (למשל HAProxy) כ Load Balancer משני. לזה יש משמעות.
      • גישה אחרת היא לעבור ל clinet-side load balancing, ולהפסיק להתבסס על ELB. מכיוון שכל שרת מחזיק cache של כתובת השרתים שהוא זקוק להם – אין \"נקודת כשל יחידה\" במערכת.

      חיבור ל VPC

      אם אתם לא מכירים / מבינים VPCs – כתבתי לאחרונה פוסט בנושא.
      מאז הצגת ה VPC אמזון מאפשרת לבחור בין 2 \"הצבות\" שונות של ה ELB: 
      • ELB ציבורי – החשוף לאינטרנט.
      • internal ELB – שיהיה חשוף רק בתוך subnets של ה VPC.
      הצורך ב internal ELB הוא כאשר אנו רוצים ששרת הווב שלנו (לדוגמה) יתקשר מול שירות פנימי שלא נגיש מהאינטרנט (לצורכי אבטחה) – ואת השירות אנו רוצים לנהל כ cluster.
      ה ELB היא תשתית מבוזרת של אמזון, ובפועל משייכים את tenant (? – לא כ\"כ ידוע כיצד ה ELB ממומש מתחת לקלעים) הספציפי ל subnets שהגדרנו. שימו לב שבניגוד ל ELB ציבורי, אם נגדיר ELB רק ב subnet בודד הוא בהכרח ירוץ רק על מכונות באותו AZ – וכך יהיה לו Availability נמוך יותר מאשר ELB רגיל – שרץ על מכונות בכל ה AZs.
      את ה resolving מ alias לכתובת IP עובר internal ELB עדיין עושים דרך Route53 – מה שלא מצוין בתרשים. הגישה ל IP (או בעצם IPs) של ה ELB עצמו כמובן תאכף ע\"י ה VPC.
      מהרגע שמסירים IP מ ELB אחד (נניח שמשרת את חברת קוקה-קולה) ועד שמשתמשים בו ל ELB instance אחר (למשל: שלנו) יש שבוע של צינון בו ה IP לא בשימוש, מה שמפחית מאוד את החשש שנקבל traffic שלא יועד לנו.
      יש הגדרה על ה ELB בשם \"cross-zone load balancing\".
      • כאשר היא enabled (זוהי ברירת המחדל), כל מופע של ה ELB בכל AZ ישלח traffic בצורה אקראית לכל AZ אחר. גישה זו משפרת את ה Availability ואיזון העומס בין השרתים.
      • כאשר היא disabled, כל מופע של ה ELB ינתב את ה traffic רק ל instances ב AZ שלו. גישה זו משפרת את ה latency (כי גישה בין AZ מוסיפה עוד כ 1-3 מילישניות). מומלץ להימנע מגישה זו אם יש לכם מספר קטן של instances (למשל: 1-2 בכל Availability zone).
      איזון עומס בין 3 AZs בלי ועם cross-zone load balanging. מקור: אמזון
      כפי שכבר ציינתי קודם לכן, load balancing בין regions אפשר לבצע בעזרת Rotue53 (או DNS אחר). יכול להיות גם VPC בסיפור – אך לא רציתי לסבך יתר על המידה את התרשים:
      Route 53 יודע לבצע latency based routing, וכך לשלוח כל לקוח ל Region שיתן לו latency טוב יותר.

      פרטים והשלמות על ה ELB

      יש עוד סדרה של עובדות חשובות שלא כיסיתי לגבי ה ELB. אביא אותן כאן בתפזורת:
      • ה ELB תומך בפרוטוקולים הבאים בלבד: HTTP/HTTPS, ו TCP.
        • ב EC2-Classic יש מגבלה על מספרי ה ports שה ELB יטפל בהם בטווח 1-1000.
      • ה ELB מספק SSL Termination and processing – וחוסך CPU למכונות ה EC2 בעזרת חומרה ייעודית למשימה. CPU רגיל נחשב מאוד לא יעיל למשימות הללו.
      • לא ניתן להצמיד ל ELB כתובת IP קבועה, אפילו לא בעזרת Elastic IP.
        • זה עלול להיות קושי אם יש לכם מערכת On-Premises שנגשת אליו, ואתם רוצים לבצע white-listing לכתובת ה IP (משיקולי אבטחה).
      • ניתן להטמיע רק SSL Certificate יחיד על ELB. כלומר: אם יש לכם כמה Domains (למשל: גם gett.com וגם gettaxi.com) – תזדקקו ל 2 ELBs שונים אם אתם רוצים להציב בהם SSL Certificate.
        • עם זאת, ה SSL certificate יכול להיות wildcard certificate, כלומר: gett.com.*, המאמת את הזהות גם של drivers.gettt.com וגם riders.gett.com.
        • ל ELB יש העדפות מסוימות ב SSL chiper negotiation, למשל:
          • יעדיף AES על פני 3DES על פני RC4.
            • ייתכן והתמיכה ב RC4 בכלל הוסרה לאחרונה. לא וידאתי.
          • יעדיף GCM על פני  CBC + HMAC.
          • בד\"כ לא תהיה בכך בעיה, אך עבור client ישנים או עבור תאימות לתקנים כמו PCI או SOX – ייתכן ותצטרכו לשנות את ההגדרות הללו.
      • עוד בנושא אבטחה, אפשר לציין ש ELB מקבל עדכוני אבטחה בצורה תדירה. לכמה ההתקפות המפורסמות: POODLE (הבעיה ב ssl 3.0), או HeatBleed – היו ל ELB כבר פתרון ראשוני תוך יממה מרגע שהבעיה התגלתה.
      ל ELB יש מנגנון בשם Listeners שמכתיב את הרגישות של ה ELB לפרוטוקולים שונים: HTTP ו TCP. לעתים נשמע בשיחה כאילו יש \"HTTP Load Balancer\" ו \"TCP Load Balancer\" באמזון – אך בסופו של דבר זהו אותו ELB כאשר מקונפגים לו Listeners שונים. הנה תצורות העבודה השונות שלהם:
      מקור: אמזון
      • ה HTTP Listener יחזיק כמה tcp connections לכל instance, גם כשאין traffic – על מנת לחסוך latency. את קריאות ה HTTP הוא יעביר על גבי ה connections הללו – ברגע שיגיעו.
      • אם התעבורה שלכם היא HTTP 1.0/1.1 לא סטנדרטי (response codes או headers שונים שדורשים התנהגות מסוימת) – ייתכן ותאלצו לעבור עם ה TCP Listener – כי ה HTTP listener לא יידע לספק את ההתנהגות הרצויה.
      • ה HTTP Listner מאפשר יכולת של Cookie-Based sticky session – משתמש יחזור ל instance שטופל בו. ב tcp פשוט אין צורך שכזה.
        • ה cookie יכול להיווצר ולהיות מנוהל ע\"י ה ELB (מה שנקרא ELB Generated Cookie Stickiness) או ע\"י האפליקציה (מה שנקרא Application Generated Cookie Stickiness).
        • ה Cookie Stickiness עולה המקרה של instance שכשל ואיבד את ה states הרלוונטיים.
        • ההמלצה היא לא להשתמש ב Cookie Stickiness אלא לאחסן את ה states במקום מרכזי (Redis או ElasticCache, המבוסס על Memcached) ואז לפזר את ה traffic בין ה nodes ע\"פ היכולת שלהם לטפל בתעבורה. כלומר: central state – מה שגורם ל instances להיות \"כאילו-stateless\".
        • אם לכל instance יש caches מקומיים משמעותיים, שלא ניתן לשתף – זו אולי סיבה שיכולה להצדיק שימוש ב Cookie Stickiness, בכל זאת.
      • על אף שלא מצוין בטבלה, ELB יוסיף גם headers של X-Forwarded-Proto ו X-Forwarded-Port (מקור).

        הנה התצורות השונות של ה TCP listener:

        • זוהי תצורת ה Load Balancing הבסיסית יותר, שתתאים אם ה traffic שלכם אינו HTTP (יכול להיות פרוטוקולים שונים העובדים על גבי TCP), או כ Fallback במידה ויש לכם בעיות תאימות עם ה HTTP Listener – אבל אז גם לא תקבלו את הערך המוסף שהוא נותן.
        • ה TCP Listner כן תומך ב Proxy Protocol גרסה 1 (פרטים) – שמאפשר סוג של מקבילה ל X-Forwarded-For ברמת ה TCP. 

        בעיות שונות שעלולות לצוץ בעבודה עם ELB

        כאשר EBS כושל – גם ELB כושל.

        נראה שיש תלות בין השירותים, כך שכל פעם שהיו בעיות ב EBS (בדרך כלל ב AZ יחיד) – גם ELB הפסיק לתפקד בצורה טובה באותו AZ. כנ\"ל אגב גם לגבי RDS, Elastic Beanstalk, ואחרים.

        לקח: אם יש בעיות ב EBS – אל תנסו לשבור את הראש להבין מה קרה ל ELB שלכם…

        Timeout לא-צפוי מהשרתים שלכם

        לעתים יש קריאות ארוכות למדי (למשל: יצירת דו\"ח ארוך במיוחד שאורך כמה דקות) – אך הוא לא מסתיים ואתם \"חוטפים\" timeout.
        אתם בודקים את ה timeouts של ה Application Servers ושל ה Web Server, למשל Nginx או Apache – אך זה לא הם.

        זה עלול להיות ה ELB, שיש לו timeout ברירת מחדל של 60 שניות ל connection. אם עלולות להיות לכם קריאות ארוכות מזה – יהיה עליכם לשנות גם את ה connection timeout שלו – מה שנקרא \"Idle Timeout\" בהגדרות של ה ELB. ניתן לקבוע את הערך ל 1 עד 3600 שניות.

        התמודדות עם Traffic Spike

        אחד החולשות של AWS שאנשי מכירות של Google Compute Engine אוהבים להזכיר הוא \"ההתחממות\" ההדרגתית של ה ELB והקושי שלו להתמודד עם spikes.
        ELB מקצה משאבים ל Tenant שלכם (הכוונה: ל \"ELB Instance\" שלכם) ע\"פ שילוב של הידיעה כמה EC2 instances חוברו לו, וע\"פ כמות ה traffic הנכנס. הוספת משאבים הוא לא תהליך מאוד מהיר ויכול לארוך כמה דקות.

        אם מגיע Traffic רב יותר ממה שהמשאבים שהוקצו ל Tenant שלכם יכולים לטפל בהם, ELB יסגור חלק מהבקשות ל Connection ויחזיר HTTP 503.

        מה ניתן לעשות?

        אם אתם יודעים על ה Spike הצפוי, למשל אתם מכינים לעצמכם התקפת Self-Denial of Service (כלומר: קמפיין פרסומי שעלול לגרור הרבה Traffic) עליכם לפנות לאמזון ולבקש הגדלת משאבים (נקרא \"Pre-Warming\").

        אלטרנטיבה אחרת היא להשתמש בכלי Load (בד\"כ כלי בדיקות) על מנת לגרום ל ELB להגדיל משאבים גם ללא פניה לשירות של אמזון. למשל: !Bees with Machine Guns (שזו לא התכלית המקורית שלו).

        תשובות 400 (Bad_Request) או 405 (Method_Not_Allowed) חוזרות מה ELB
        ה HTTP-Listner של ה ELB בודק כמה סימנים חיוניים של בקשות כמו Content Length (יכול להחזיר 400), או שם מתודה לא מוכרת / ארוכה מ 127 תווים עשוה להחזיר 405. כנראה שיש עוד מקרים דומים.

        הפתרון הוא לרוב לתקן את התקשורת כך שתעמוד בתקני HTTP 1.0/1.1 או להוריד את ה HTTP-Listener.

        פיזור עומס לא מידתי על השרתים (אין Load Balancing טוב)

        זה קורה. אל תצפו ל Balancing מושלם, במיוחד לא כאשר כמות ה instances שלכם היא קטנה יחסית (פחות מ 5-6 instances). יכולות להיות לכך כמה סיבות:

        • יש שונות גדולה בין זמני הטיפול בבקשות השונות.
        • חיברתם לאותו ELB instance מכונות בגדלים שונים או קונפיגורציות עבודה שונות.
        • ה Clients שלכם לא מכבדים את ה TTL של ה DNS (שאמור להיות קצר מאוד כאשר מדובר ב Alias של ה ELB) – וזה גורם ל Balancing של Route53 לא לעבוד בצורה טובה.
        מלבד המקרה האחרון, חיבור ל CloudWatch וביצוע Load Balancing ע\"פ מדדים של CPU ו/או זיכרון – כנראה ישפרו את המצב.

        סיכום

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

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

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

        (Understanding the new ELB metrics in CloudWatch (2013

        Amazon Virtual Private Cloud

        VPC (קיצור של Virtual Private Cloud) היא רשת מבודדת לוגית ב Data Center של AWS. היא באה לדמות Data Center פרטי של ארגון (כלומר: ב On-Premises), בו תעבורת הרשת, והגישה לשרתים מוגבלת למי שנמצא בתוך ה Data Center.

        • מבחינה טכנית, ניתן לחשוב עליה כתת-רשת (subnet) בתוך אמזון שמוקצה רק לכם.
        • מבחינת אבטחה, ניתן לחשוב עליה כ VLAN [א] – רשת משותפת שהתשתית יודעת להפריד בין packets של תתי-רשתות נפרדות – הפרדה מוחלטת.
        VPC הוא לא שירות פרימיום. הוא כמעט חינם, ואם אתם פועלים ב AWS ולא משתמשים בו – כדאי להכיר.

        בניגוד לניהול רשת \"קלאסי\" בו צריך להכיר את הראטורים השונים ולתכנת אותם, העבודה ב VPC היא ברמת התוכנה ובממשק אחיד של אמזון – סוג של Software Defined Networking (קונספט שגם תופס תאוצה ברשתות On-Premises).

        למרות שה VPC מבודד אותנו משאר העולם, ניתן לבצע קשרים מאובטחים עם רשתות אחרות:

        • VPN שיחבר בין רשת אחרת של הארגון (למשל – המשרד) ל VPC בו נמצאים השרתים.
        • VPC Peering – חיבור (שניתן להגדיר את מידת החופש שלו) בין שני VPCs, למשל: VPC של ספק שירות או לקוח, או אולי VPC אחר של הארגון שלכם (כי לעתים נכון ליצור בענן כמה VPCs לאותו הארגון).

        אם אתם רוצים אבטחה אפילו גבוהה יותר, אז VPC תומך במצב של הפרדה מוגברת – מה שנקרא \"dedicated tenant\".
        כל ה EC2 instances שנוצרים בתוך VPC שהוא dedicated tenant הם dedicated instances (כלומר: השרת הפיסי רק משרת אתכם – אין לכם \"שכנים\"), וגם חלק מתשתיות הרשת יהיו מופרדות בצורה טובה יותר – עבורכם.
        השירות הזה, באופן טבעי – מגדיל את העלויות בצורה משמעותית.

        מה החשיבות של VPC, ומדוע לא כדאי לעבוד באמזון בלעדיו?

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

        אמנם גם ללא VPC ניתן להגן בעזרת \"Firewall\" על השרתים שלכם: ניתן לאגד שרתים דומים בקבוצה הנקראת \"קבוצת אבטחה\" (Security Group), ואז להגדיר:

        • תקשורת נכנסת – כתובות IP, פורטים, ופרוטוקולים (tcp, HTTP, וכו\') ספציפיים שיכולים לגשת למשאבים בקבוצת האבטחה.
        • תקשורת יוצאת – כתובות IP, פורטים, ופרוטוקולים אליהם יכולים המשאבים בקבוצת האבטחה לפנות.

        הגישה הזו היא מוגבלת:

        • ניתן להגדיר רק הרשאות (whitelist) ולא חסימות (blacklist).
        • אם אתם רוצים לבודד בין קבוצות שרתים שלכם (\"ניתן לגשת לבסיסי הנתונים רק מתוך ה Application Servers\") היא הגדרה כמעט בלתי אפשרית ללא VPC: כתובות ה IP משתנות כל הזמן, ויהיה עליכם לעקוב אחר השינוי ולעדכן את ה Security Groups. נתקן: אפשרי – אך לא סימפטי לתחזוקה.
        VPC מאפשר הרבה מאוד גמישות להגביל את התעבורה בין כל קבוצת משאבים שלכם – לכל קבוצה אחרת, ובצורה שניתנת לניהול.
        עוד צורך חשוב ש VPC פותר הוא בידוד הרמטי יותר בין חלקים במערכת, למשל: משאבים המשרתים מחלקות שונות, בין dev ל production, וכו\'. הפתרון שהיה מקובל קודם ל VPC הוא לנהל חשבונות (account) שונים ב Amazon – מה שהקשה מאוד על כמה פעולות אחרות (למשל: העברת נתונים בצורה מאובטחת בין החשבונות).

        עוד כמה שימושים ל VPC:

        • Compliance – אם יש תקן שמחייב אתכם להגביל ו/או לעשות Audit לכל מי שניגש לקבוצה מסוימת של משאבים – VPC יהפוך את המלאכה לקלה יותר.
        • הפרדה בין dev/test ל production – לצורך הגנה בפני טעויות אנוש.
        • הפרדה דומה בין יחידות שונות בארגון, למשל: ארגון ה BI רוצה אחריות משלו על החשבון, והפרדה מכל מה שמתרחש ב R&D.

        \"The \"Default VPC

        החל מ 2014 [ב] כל חשבון חדש שנוצר באמזון, נוצר ב Default VPC – שזו בעצם תשתית התקשורת המשופרת של אמזון שמאפשרת הגדרה של VPCs. אם אתם נמצאים \"ב Default VPC\" – אין הכוונה שבאמת אתם משתמשים ב VPC, אלא שאתם נמצאים על התשתית החדישה יותר של אמזון, ברשת משותפת… ביחד עם עוד כמה אלפי ארגונים.

        תשתית הרשת הישנה יותר של AWS נקראת \"EC2 Classic\". היא מוגבלת יותר:

        • לא מאפשרת יצירה של VPC.
        • פחות גמישות ב Security Groups: לא ניתן להגביל outbound traffic, לא ניתן להחליף באופן דינאמי Security Group למשאב).
        • פחות גמישות בהגדרות רשת בסיסיות: לא ניתן לתת למשאב יותר מכתובת IP אחת, קשה יותר לשלוט בהגדרות ה DHCP, DNS, ו NTS של המשאבים.
        • לא ניתן להשתמש ב Enhanced Networking (משלמים יותר, מקבלים רשת יותר \"יציבה\" ומהירה).
        • וכו\'.
        אם אתם נמצאים על EC2-Classic – אז כדאי לעבור \"Default VPC\" גם אם אתם לא מתכננים לנהל VPC משלכם. יבוא היום ואמזון תכריח אתכם לעבור. בכל מקרה, מעבר שכזה הוא תהליך מורכב – אם אתם לא מוכנים לספוג downtime. כלי שיכול להקל בכזה מעבר הוא AWS ClassicLink.

        ב Default VPC, כל מכונה חדשה שתוסיפו תקבל אוטומטית כתובת IP ציבורית באינטרנט – בכדי לתאום לאחור להתנהגות של EC2 Classic. זה לא יקרה ב VPC שהוגדר על ידכם.

        כמו כן, ב Default VPC כל שרת שלכם יוכל לגשת לאינטרנט – כי הוגדר לו Internet Gateway כברירת מחדל.
        ב VPC משלכם, ברירת המחדל היא ששרתים לא יכולים לגשת לאינטרנט – אלא אם חיברתם אותם בעצמכם ל Internet Gateway.
        מדוע זה חשוב? כי אם יש לכם שרת שלא אמור מתוקף תפקידו לגשת לאינטרנט (למשל Database Server) – גישה לרשת יכולה לרמז על תוכנה עוינת שרצה על המחשב ושולחת מידע לתוקף. תוכלו למנוע זאת ע\"י בקרה ושליטה בתקשורת היוצאת מהרשת שלכם.

        יוצאים לדרך!

        יצירה של VPC היא פעולה דיי פשוטה, והיא נראית כך:

        1. אנו יוצרים את ה VPC ומגדירים לו טווח כתובות (cidr-block) של בטווח הכתובות השמורות לרשתיות פרטיות, בכדי לא להתנגש עם כתובות באינטרנט.

        CIDR (קיצור של Classless Inter-Domain Routing) היא שיטה להגדרת תתי-רשתות (subnets) שיכולה לנצל את טווח הכתובת של IPv4 בצורה יעילה יותר, ואולי מעט יותר קלה להגדרה מהשיטה של subnet-masks.

        התחביר של CIDR נראה כך:

        /

        כאשר כתובת ה IP הבסיסית היא כתובת בת 4 מספרים בני 8 ביט (0-255) – סה\"כ 32 ביט, וה Suffix הוא מספר בין 0 ל 32 – המתאר כמה ביטים בכתובת הבסיס הם \"קבועים\" ומתארים בעצם את טווח תת-הרשת.

        למשל:
        127.0.0.0/24 – היא צורה לתאר תת-רשת עם טווח הכתובות 127.0.0.0 עד 127.0.0.255.
        24 ביט הם שלשה מספרים, ולכן 127.0.0 הוא החלק ה\"קבוע\" בטווח הכתובת.

        עוד דוגמה:
        30.0.0.0/8 היא צורה לתאר תת-רשת שטווח הכתובת שלה הוא 30.0.0.0 עד 30.255.255.255.
        8 ביט הם מספר אחד, ולכן רק 30 הוא החלק ה\"קבוע\" בטווח הכתובות.

        ניתן להשתמש ב CIDR בכתיבה מקוצרת, ולהשמיט מכתובת הבסיס אפסים שאינם קבועים. למשל הדוגמה הראשונה יכולה להיכתב כ 127.0.0/24 והדוגמה השניה יכולה להיכתב כ 30/8. ניתן למצוא עוד מגוון דוגמאות בערך בוויקיפדיה.

        בדוגמה שלנו הגדרנו את 10.0.0/16 – הטווח בין 10.0.0.0 ל 10.0.255.255 – טווח אפשרי של 65K כתובות (המקסימום של VPC מאפשר). אנו כמובן משתמשים באחד הטווחים של פרוטוקול ה IP לרשתות פרטיות – לא נרצה התנגשות עם כתובות של שירותים ברחבי האינטרנט. התוצאה של הפעולה מחזירה לנו id של ה VPC שרק נוצר, במקרה שלנו: vpc-a01106c2.

        2. בתוך ה VPC עלינו להגדיר subnet לכל Availability Zone. ה VPC, כקונספט לוגי שמכיל הגדרות, יכול להתפרס על גבי Region של אמזון, אבל ה subnet הספציפי יכול לחיות רק ב Availability Zone – שהרי זהו Data Center פיסי.

        ה Subnet יהיה בטווח קטן יותר של כתובות IP – כ 256 כתובות, בכדי לא \"לשתות\" את כל הכתובות של ה VPC, ואנו נייצר אותו ב AZ בסימון a של אירלנד (eu-west1).

        3. הנה אנו מגדירים subnet נוסף ב AZ בסימון b, ובאופן דומה. שימו לב שמרחב הכתובות של ה subnet הזה הוא שונה.

        התוצאה היא המבנה הבא:

        לכל subnet יהיה id, למשל \"subnet-d38d91dd\" וברגע שנבקש להקים EC2 instance – יהיה עלינו לציין באיזה subnet ליצור אותם.

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

        1. אנו יוצרים Internet Gateway, רכיב של AWS שמאפשר גישה לאינטרנט. בהפעלת הפעולה – נקבל id (למשל \"igw-c0a643a9\")

        2. אנו מוסיפים route ל \"router הווירטואלי שלנו\", שאומר שכל כתובת בעולם (0.0.0.0/0), אם לא נמצאה בתוך ה subnet – הולכת ל internet gateway שלנו.

        ה VPC מחזיק טבלת ניתוב (routing table) לכל ה subnets, אך ניתן להגדיר טבלאות ניתוב שונות – ולקשר כל subnet לטבלת ניתוב שונה.

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

        לשמחתנו ניתן לבצע את כל הפעולות עם ה aws console – ממשק אחיד לו ניתן לכתוב סקריפטים (ניתן גם דרך ה UI של אמזון – למי שמעדיף). לא צריך להתחבר ל router החומרה ולעשות את זה מולו ישירות – כמו פעם.

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

        אז איך מתכננים VPC?

        \"VPC ניתן להגדיר ב-2 אופנים: VPC יחיד, או ריבוי VPCs – אך יש אלפי תצורות שונות בהן ניתן להגדיר את שני האופנים\" — יועץ AWS זקן וחכם.

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

        • VPCs בניהול ה R&D:
          • Production
          • Dev/Test – ההפרדה נעשתה כדי למצמצם טעויות אנוש שישפיעו על production.
            יש גם ארגונים שלא מאפשרים למפתחים בכלל לגשת ל production – וזו דרך פשוטה לנהל זאת.
        • VPC עבור ה IT:
          • מערכות ה Active Directory, מערכת ניהול הכספים, נוכחות משתמשים ומערכת ה HR – לא צריכות להשפיע על production, ואף מפתח לא זקוק לגישה אליהן. לכולם יותר נוח שרק לאנשי ה-IT תהיה גישה לרשת זו.
        • VPC עבור ה Data Science
          • צוות ה Data Science זקוק להרשאות Admin ל AWS כי הם מרימים ומורידים מכונות (למשל EMR) בכמויות סיטונאיות, וחס וחלילה לא נרצה שיפגעו בפרודקשיין. \"אופס סגרתי לכם 100 מכונות בפרודקשיין, לא שמתי לב בגלל המספר הקטן\" – הוא לא מה שאתם רוצים לשמוע…

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

        כמו כן, ברוב הפעמים המערכת שלנו תהיה מפוזרת בכמה Regions של אמזון. לצורך הפוסט אני מפשט את התמונה ומניח שאנו פעילים רק ב Region יחיד – ה Region (העתידי) של IL-Center-1 😉 .

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

        לחצו להגדלה!

        1. בתוך ה VPC, יש לנו 2 (בד\"כ 3 או 4 – לא היה לי כח לצייר) AZs זהים. בגלל שכל AZ הוא Data Center מבודד פיסית (ואנו רוצים את היתירות הזו) – יהיה עלינו להרכיב subnets סימטרים ב AZs השונים, שיבדלו ע\"י טווח כתובות שונה (כלומר: ה CIDR שלהם). כמובן שעדיף לעשות זאת בעזרת כלים אוטומטיים (למשל: Cloud Formation) ולא בצורה ידנית.
          לצורך פשוט התרשים, פירטתי רק את המבנה של AZ סימן a – אנא הניחו ש AZ סימן b הוא סימטרי לחלוטין, גם בתוכן וגם בחיבוריות פנימה והחוצה.
        2. ה Traffic שמגיע מבחוץ (משתמשים באינרטנט, למשל) עובר דרך ה DNS של אמזון (קרי: route 53). ה DNS מספק Load Balancing ראשוני בין ה AZs השונים. משם ה Traffic עובר ל Router של אמזון שמנתב אותו כל פעם ל AZ שנבחר.
        3. הכניסה הראשונית היא ל subnet \"ציבורי\" אליו ניתן לגשת מכל מקום באינטרנט, ושם אנו מבצעים את הסינון הראשוני של התעבורה. אמזון מספקת לנו שירות, המגיע כ AMI שעלינו להתקין, שנקרא VPN NAT Gateway – שהוא בעצם Reverse Proxy. רוב הסיכויים שנרצה סינון קצת יותר רציני של Traffic, למשל – WAF (קיצור של Web Application Firewall). לאמזון יש גם שירות שכזה – אך הוא עדיין מאוד בסיסי.
          1. אם הרעיון של ה public subnet מזכיר לכם DMZ ברשתות On-Premises זה לא במקרה – זה בדיוק אותו התפקיד, ואותו הרעיון של Layered Security.
        4. ה Subnets הפנימיים (נקראים \"פרטיים\" כי אנו מגבילים את הגישה אליהם) מכילים את חלקי המערכת שלנו. למשל, ה Subnet הראשון אליו מגיעים מכיל את ה Web Server ואנו יוצרים כלל שמאפשר רק לתקשורת שמקורה ב public subnet – להגיע ל subnet הזה.
          1. באופן דומה, שירות X יסכים לקבל תקשורת רק מה Web Server או כל Subnet שנגדיר לו.
          2. הגדרת הכללים היא קלה: אנו יודעים מה ה CIDR (טווח הכתובות הקבוע) של כל subnet, כך שב Security Group של שירות X אנו נאפשר את ה Subnets (אחד לכל AZs) של ה Web Servers.
            אם מתווספים שרתי ווב חדשים – הם עדיין יהיו בטווח הכתובות של ה subnets, ולכן הכללים שהגדרנו – עדיין יהיו תקפים.
          3. אנו יודעים איזה חלק של המערכת אמור לתקשר עם איזה חלק אחר – וכך אנו חוצצים את השירותים שלנו למספר subnets, ע\"פ פרופיל התקשורת שלהם: הנכנסת והיוצאת.
            יש ארגונים שיחלקו את הרשת שלהם ל 3-4 פרופילים שכאלו, ויש כאלו שיחלקו ל 50. הכל תלוי במידת האבטחה שאתם רוצים להשיג ועד כמה אתה מוכנים לטרוח בכדי להשיג אותה.
        5. כפי שציינתי, גם התקשורת היוצאת היא פרופיל שכדאי לשלוט בו. יהיו מעט התקפות יעילות על הרשת שלכם – שלא יידרשו סוג של קשר עם המתקיף.
          1. באופן דומה, נהוג גם את התקשורת היוצאת לסנן בעזרת IDS, WAF, או לפחות NAT פשוט. לאחר הסינון התקשורת תגיע ל Internet Gateway שזה שירות של אמזון (highly available, fully elastic) – שמאפשר את תקשורת הנתונים החוצה.
        6. סביר שתרצו לאפשר גם לעובדים במשרד לגשת לחלקים מהמערכת, ולכך יש שירותי VPN. אם אתם רוצים לאפשר גישה מה VPN ל Web Server, יהיה עליכם להגדיר כלל נוסף שמאפשר ל subnet של ה Web Server לקבל תקשורת מה VPC Gateway.
        7. הזכרנו שהמוטיבציה העיקרית ל VPC היא אבטחה. עוד שירות חשוב של אמזון הוא ה VPC Flow Logs.
          זהו שירות המאפשר לקבל לוגים על כל התעבורה אל ומחוץ ל VPC.
          ישנם שירותי צד-שלישי (כמו Observe Networks, Dome9, SumoLogic, ועוד) שישמחו לאסוף עבורכם את הלוגים הללו, לנתח אותם, להציג ב Dashboard יפה – ואף לנסות ולאתר אנומליות.

        עוד כמה פרטים אחרונים

        VPN

        ניתן לאפשר ערוץ תעבורה בין המשרד, או כל רשת אחרת ל VPC שלכם על גבי VPN. הרכיב באמזון שמאפשר זאת הוא ה VPN Gateway (בקיצור: VGW, נקרא גם Virtual Private Gateway) היודע לעבוד עם פרוטוקול IPSec VPN.

        כל חיבור VPN כולל 2 IPSec Tunnels (ערוצים מוצפנים), כך שאם ערוץ אחד כשל, פרוטוקול ה BGP (קיצור של Border Gateway Protocol) ינתב את התעבורה דרך הערוץ השני. ניתן כמובן להגדיר יותר מ VPN Gateway  – וכך לאפשר יתירות של ארבעה Tunnels ויותר.

        AWS Direct Connect

        AWS Direct Connect הוא שירות של אמזון לחיבור רשת משופר (קווים שאמזון שוכרת לצורך זה, ככל הנראה) בין VPCs ב Regions שונים לרשת של הארגון שלכם. אם יש יש לכם תעבורה משמעותית בין הרשתות – השירות הזה עשוי להפחית עלויות, לשפר latency ולשפר את האבטחה.

        התקשורת ב AWS Direct Connect מבודדת ע\"י פרוטוקול VLAN – כך שהיא לא תערבב עם תקשורת של VPCs אחרים.

        גם בשימוש ב Direct Connect, ניתן להגדיר Tunnels של VPN כ fallback, במידה וה Direct Connect כושל (בעזרת פרוטוקול BGP).

        VPC Peering

        זוהי היכולת המאפשרת לחבר בין שני VPCs, בהסכמה הדדית, ובצורה קלה ויעילה. את התעבורה שמגיעה מ VPC אחר ניתן עדיין להעביר דרך Firewall או WAF – במידת הצורך.
        ניתן בפועל להשתמש גם ב VPN על מנת לתקשר בין 2 VPCs, אם כי VPC Peering עושה זאת בצורה פשוטה הרבה יותר – ובטוחה באותה המידה.

        VPC Endpoint

        מאפשר להגדיר ערוץ תקשורת פרטי בין ה VPC לשירות של אמזון (ולא דרך הרשת הכללית של ה Data Center).
        כרגע השירות היחידי שתומך ביכולת זו הוא S3 – אך עוד שירותים יתווספו בעתיד.

        בכדי להשלים את התמונה, ב S3 Bucket ניתן להגדיר (בעזרת Policy) שיסכים לקבל קריאות רק מתוך VPC endpoint מסוים (ע\"פ id) – וכך גם לוודא שלא בטעות פניתם ל Bucket שלא דרך הערוץ המאובטח.

        סיכום

        Virtual Private Cloud היא במידה מסוימת יכולת \"לא סקסית\" של AWS – אך חשובה מאוד.
        לפני כמה שנים דובר כמה ארגונים חוששים, משיקולי אבטחה, לעבור לענן. ראיתי תצורות רשת של כמה חברות גדולות. רובן היו פרימיטיביות יותר ממה שניתן להשיג בעזרת VPC במאמץ סביר. למשל: לחלק כל שירות ל subnet ולהגביל את סוג התקשורת הנכנסת + היוצאת? מבחינת אבטחה – זה נהדר!  בד\"כ ארגונים מספקים רמת שליטה שכזו רק על כל ה Data Center או רק בסיסי הנתונים (כולם, כמקשה אחת).

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

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

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

        AWS re:Invent 2015 | (ARC403) From One to Many: Evolving VPC Design

        SDD422) Amazon VPC Deep Dive)

        25 טיפים להגדרת VPC

        [א] מהו VLAN – אפשר לקרוא הסבר קצר בפוסט שפירסמתי על OpenStack.

        [ב] 4 בדצמבר 2013, לדקדקנים שביננו.