בפוסט הקודם שלום, AWS Lambda! הצגנו את למבדה, ובעיקר כיצד ה main successful flow עובד.
לא הספקנו כמעט ולהיכנס לעומק / להתנהגויות השונות במודל של למבדה.
זה מה שננסה לעשות בפוסט הזה.
כאשר מתבצעת הפעלה (invocation) ראשונה של הפונקציה שלנו, למבדה מפעילה "Container" שבו תרוץ הפונקציה.
מהו ה container הזה? אין תיעוד מדויק, אך מדובר בסופו של דבר בסביבת ההרצה של הפונקציה, ע"פ ה settings שהגדרנו.
הרצת ה container אורכת זמן מסוים, נאמר כ 100-200ms - מה שנקרא cold start.
על הזמן הזה - המשתמש מחויב.
זמן מה לאחר שהפונקציה הופעלה, למבדה תשמור את ה container חי. הזמן המדויק כנראה תלוי בעומס על השרתים של למבדה, אבל הוא יכול לנוע בין שניות ספורות - לעשרות דקות.
בכל הפעלה עוקבת של הפונקציה, למבדה תנסה לעשות שימוש חוזר בקונטיינר קיים. היא לא תעשה שימוש חוזר במקרים הבאים:
למרות החשש האפשרי מ cold start על הביצועים - ההשפעה של ה cold start על משתמש הקצה היא לרוב קטנה למדי. הנה רכיבים טיפוסיים שגם להם אנו ממתינים בהפעלה של פונקציית למבדה:
דיי פופולארי היום לדבר על טכניקות ל"שמירת ה container חי" והימנעות מ Cold Start.
בשקלול של כל הרכיבים, נראה ש"הימנעות מ Cold Start" היא התעסקות דיי מיותרת - המשקל של הרכיבים האחרים הוא גדול יותר. לפחות, ברוב הגדול של המקרים.
Container reuse
אם לפונקציה שלכם יש קצב הפעלה בינוני עד גבוה, רבות מההפעלות יתרחשו על container קיים.
זה אומר ש:
כאשר יש הרבה יותר בקשות הרצה להפעלה של פונקציות מאשר containers - למבדה תגדיל את מספר ה containers הרצים.
כאשר יש מעט יותר בקשות הרצה להפעלה של פונקציות מאשר containers - למבדה תבצע throttling. כלומר: תמתין עם הבקשה עוד קצת - ואז תפעיל אותה על גבי ה containers הקיימים.
שווה להזכיר את שירות ה 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) - אבל לאמזון יש יתרון מובנה מול מתחרים קטנים יותר.
יהיה מעניין!
הפוסט הקודם באמת היה מאוד מקדמי / בסיסי - ונראה לי שהצלחנו לסגור את הפער ולספק היכרות מספיק מעמיקה עם שירות הלמבדה - ויחסית בקלילות.
שיהיה בהצלחה!
לא הספקנו כמעט ולהיכנס לעומק / להתנהגויות השונות במודל של למבדה.
זה מה שננסה לעשות בפוסט הזה.
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.
למרות החשש האפשרי מ 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, בכל זאת.
המדד היחידי שלא הוסבר הוא ה iterator age שרלוונטי רק למקורות מסוג stream-based events.
הוא מציין כמה זמן ארך מרגע שפונקציית הלמבדה שלנו קיבלה batch של הודעות, ועד שסיימה לטפל בהודעה האחרונה. כלומר: מה ה latency המרבי של טיפול בהודעות ב batch, שהגיעו ממקור שכזה.
הוא מציין כמה זמן ארך מרגע שפונקציית הלמבדה שלנו קיבלה 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) - אבל לאמזון יש יתרון מובנה מול מתחרים קטנים יותר.
יהיה מעניין!
סיכום
הפוסט הקודם באמת היה מאוד מקדמי / בסיסי - ונראה לי שהצלחנו לסגור את הפער ולספק היכרות מספיק מעמיקה עם שירות הלמבדה - ויחסית בקלילות.
שיהיה בהצלחה!