2016-06-18

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



6 תגובות:

  1. אנונימי18/6/16 22:18

    ליאור, תודה על הסקירה!
    האם תוכל להרחיב על Weighted Round Robin ב-ELB?
    ככל הידוע לי, זו תכונה הנתמכת רק ב-ROUTE53.

    השבמחק
    תשובות
    1. היי אנונימי,

      תודה רבה על התגובה.

      זו טעות שלי: אין WRR ב ELB - רק ב Route53, כפי שציינת.

      אתקן.

      מחק
  2. תודה רבה, תמיד הפוסטים שלך ברמה גבוהה!
    כידוע לי, ELB מאחורי הקלעים הוא פשוט Nginx

    השבמחק
    תשובות
    1. תודה!!

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

      לגבי ELB:
      בתיאור המשרה למפתחים של ה ELB אומרים במפורש שיש קוד ב ++C, ג׳אווה, פרל ורובי. זה נשמע כמו פתרון proprietary. מקור: https://goo.gl/Xe6bHd

      אם ELB מבוסס על Nginx, אני תוהה מדוע הוא לא תומך ב UDP, ב HTTP 2.0, או ב WebSockets?
      הנה השוואה ש Nginx עשו בינם ובין ELB, שמציגה דיי הרבה הבדלים: https://goo.gl/0MntGg

      2 האגורות שלי

      מחק
    2. תודה, מאוד מעניין!
      אולי הם עשו fork ל Nginx שיתאים להם?

      ארכיטקט מהשותפה הבכירה של אמאזון בארץ אמר לי את זה ממש לאחרונה בכנס שהיה השבוע
      היה לנו איזה סיפור עם ה elb + beanstalk

      מחק
    3. היי רומן, אני באמת לא יודע.

      ייתכן ויש חלקים של Nginx בתוך ELB, אך כרגע ממה שאני מכיר - הם נראים דיי שונה אחד מהשני.

      מחק