HAProxy כפרוקסי הפוך: היכן הארכיטקטורה נשברת תחת עומס
הפרוקסי ההפוך עומד ראשון בארכיטקטורה והוא הראשון שסופג את ההשלכות: כל עוד התעבורה מתונה, קונפיגורציה של עשרה טורים מחזיקה את העומס בלי שמים לב.
היא נשברת במקום שבו זינוק בבקשות או כשל של צומת אפליקציה הופכים את הפרוקסי לנקודת כשל עבור השירות כולו — נעלם פתח הכניסה, ולא פונקציה בודדת.
עומס יתר של חיבורים מתגלה לפני כל השאר. התור ב-frontend מחושב לזרימה רגועה: בזמן מטח, המקומות הפנויים נגמרים, חלק מהלקוחות מקבלים ניתוק במקום תשובה, וה-backend שמסוגל לעבד אותם עומד בחוסר מעש.
ההידרדרות תחת עומס אינה ליניארית — זמן התגובה קופץ, משום שבקשות חדשות נכנסות לאותו תור עם הוותיקות וממתינות יחד איתן.
צוואר הבקבוק השני בארכיטקטורה הוא היעדר בדיקות תקינות של צמתים. בלי בדיקה תקופתית, מאזן העומס ממשיך לשלוח תעבורה למקום שבו האפליקציה כבר מחזירה שגיאות, וכשל של צומת אפליקציה גורר אחריו את כל השירות.
לצד זה סיבה טיפוסית נוספת: עריכות ידניות בקונפיגורציה בלי מנגנון חזרה לאחור, כאשר ריסטארט מתבצע בלי בדיקת תחביר ובלי גרסה קודמת שמורה — וברגע תקלה המהנדס נשאר בלי דרך חזרה.
כל שלושת התרחישים נסגרים ברמת הארכיטקטורה והחוקים, ולא באמצעות פעולות גבורה בזמן תקלה. בהמשך נפרט איך בדיוק: מגבלות וזמני timeout בהתאם לפרופיל התעבורה בפועל, בדיקות מצב עם הוצאת צומת מהסבב, החלה בטוחה של שינויים תוך שמירת הגרסה הקודמת וחזרה מהירה אליה.
מה נותן מאזן עומס מנוהל: עמידות ויכולת חיזוי
כשל של צומת מפסיק להיות אירוע עבור המשתמש. מאזן העומס מבוסס HAProxy סוקר את שרתי האפליקציות בפרקי זמן מוגדרים, ועם ספי rise/fall מוציא את הצומת שנכשל מהמאגר לפני שהלקוח מקבל שגיאה, בעוד צומת הגיבוי מקבל את העומס בעצמו. זמן ההתאוששות נמדד בשניות ואינו תלוי יותר במי שהבחין בבעיה ראשון — התורן או מערכת הניטור.
תפוקה גדלה על אותה חומרה. שימוש חוזר בחיבורים לשרתים ואיזון לפי מספר בקשות פעילות מסירים את חוסר האיזון שבו צומת אחד מחזיק יותר לקוחות מהאחרים; שימוש חוזר במפגשים מאובטחים מפחית את התקורה של הקמת קשר.
מחלקה שלמה של כשלים הקשורים להתערבות ידנית נעלמת: פריסת גרסה חדשה מתבצעת דרך הוצאה הדרגתית של צומת מהסבב, ולא דרך עצירת השירות, ובקשות שנכשלו לפעולות ניתנות לחזרה עוברות לצומת חי. חוקי הצמדת מפגש מוגדרים במפורש, ולכן שגיאות אימות „צפות“, שאחרת מחפשים אותן שבועות, אינן נוצרות כלל.
מופיע יומן אירועים שקוף: פורמט רישום אחיד מתעד זמן תגובה לפי כל שרת, קודי תשובה ואיזה צומת שירת את הבקשה. לפיו רואים מיד היכן ההידרדרות — באפליקציה או בנתיב אליה — ויכולת החיזוי של התגובה מפסיקה להיות נושא לניחושים: האחוזון ה-95 נשאר בטווח צר גם בעומס שיא.
מצבי עבודה של פרוקסי ואיזון עומס: מה לבחור לפי המשימה
מצב הפעולה של הפרוקסי קובע מה בכלל זמין בנקודת הכניסה: האם רואים רק כתובת ופורט או גם כותרות, נתיב ודומיין. מכך תלויים ניתוב לפי תוכן, סיום הצפנה (TLS termination) והרמה שבה נספרות בדיקות התקינות.
ארבעה אפשרויות שבהן באמת בוחרים:
| אפשרות | מתי מתאימה | מגבלות |
|---|---|---|
| מעבר ישיר ברמת החיבורים | פרוטוקולים לא סטנדרטיים, בסיסי נתונים, הצפנה מקצה לקצה עד השרת | תוכן הבקשה סגור: אין ניתוב לפי נתיב ואין עריכת כותרות; הבדיקה — רק זמינות הפורט |
| סיום הצפנה בנקודת הכניסה | שירותי ווב עם דומיין משותף, תעודה אחת, שימוש חוזר בחיבורים | המפתח נמצא בפרוקסי; הקטע הפנימי בלי הצפנה חוזרת דורש רשת מהימנה |
| ניתוב לפי תוכן | כמה שירותים תחת כתובת אחת, canary releases, פיצול לפי גרסאות | נדרש פירוק מלא של הבקשה: השהיה גבוהה יותר בנקודת הכניסה, כשל של שרת אחד מורגש יותר |
| שירות חיצוני מנוהל | אין נקודת כניסה עמידה בעצמך, נדרשות תעודות מוכנות | פחות שליטה באלגוריתמים ובזמני timeout, תלות בספק |
לאחר בחירת המצב נשארים חלוקת הבקשות ובדיקות התקינות: עבור חיבורים ארוכים משתלם יותר מונה מפגשים פעילים, עבור שירותים עם הצמדה למפגש — החזקת הלקוח על אותו צומת, ובדיקות אקטיביות מוציאות שרת מהסבב ומחזירות אותו רק לאחר כמה תגובות מוצלחות. כך נעלמת הסיבה העיקרית לשגיאות 500 „צפות“ תחת עומס.
סדר ההטמעה: מביקורת הארכיטקטורה ועד מסירה לתפעול
העבודה מתנהלת באופן סדרתי: עד שלא מתבררים הטופולוגיה הנוכחית ופרופיל התעבורה, כל עריכה בקונפיגורציה היא ניחוש. לכן מתחילים בביקורת הארכיטקטורה, ומעבירים את תעבורת הייצור לקונפיגורציה החדשה רק לאחר שהיא עבדה בסביבת בדיקה. להלן שלבי ההטמעה.
- ביקורת הארכיטקטורה. אוספים תמונה מצבית בפועל: היכן נמצא הפרוקסי ההפוך, אילו frontend ו-backend מוצהרים, כיצד פרושות חוקי הניתוב, במה נבדקת זמינות השרתים. משווים זאת ליומני השגיאות ולסטטיסטיקה לפי קודי תשובה.
- תכנון הארכיטקטורה היעד. מחליטים מה מאזנים — HTTP או TCP, היכן מסתיים TLS, כיצד מוחזקים מפגשי המשתמש ולפי אילו סימנים צומת נחשב לא תקין ומוצא מהסבב.
- הגדרת הקונפיגורציה. מתארים frontend ו-backend, חוקי ניתוב, זמני timeout, מגבלות חיבורים וחזרות בקשות. את הקונפיגורציה מחזיקים במערכת בקרת גרסאות — כל שינוי עובר סקירה.
- בדיקה בסביבת בדיקה. מריצים תרחישי כשל: נפילת צומת, ריסטארט הדרגתי, גידול בעומס, פקיעת תעודה. קריטריון המוכנות — כשל של צומת אחד אינו מורגש למשתמש.
- העברת תעבורה מבוקרת. מעבירים בהדרגה: תחילה חלק מהבקשות, אחר כך כל הזרימה. החזרה לארכיטקטורה הקודמת נשארת זמינה בפקודה אחת עד סוף ההעברה.
- מסירה לתפעול. מוסרים למשמרת התורנות מפת שירותים, נוהל עריכות, סט מדדים והתראות, סדר עדכון מתוכנן של תעודות ופעולות בעת הידרדרות.
מה נשאר אצל הצוות: קונפיגורציות, תרשימים ונוהלים
כל מה שאנחנו מגדירים ב-HAProxy נשאר אצלכם במאגר עם היסטוריית עריכות, ולא בהתכתבות עם מהנדס. להלן רשימת הארטיפקטים שהלקוח מקבל במסירה: לפיהם התפעול והפיתוח של השירות אינם תלויים במי שבדיוק הגדיר את הקונפיגורציה.
- קונפיגורציות עבודה — קבצים עם הערות לכל סעיף: חוקי ניתוב לפי דומיין ונתיב, זמני timeout, מגבלות חיבורים בו-זמניים, מצב איזון ופרמטרים של בדיקות זמינות השרתים.
- תרשים קישורים — מפת זרימת הבקשות: נקודות סיום הצפנה, מאגרי שרתים מאחורי הפרוקסי, סדר עמידות בכשלים, התנהגות בנפילת צומת והלוגיקה של החזרת תעבורה לאחר התאוששות.
- מדיניות גישה — חוקי הגבלה לפי כתובות ותדירות בקשות, הפרדה בין נתיבי שירות לנתיבים ציבוריים, עבודה עם כותרות לקוח ומצב תחזוקה בזמן שחרור.
- נוהל חזרה לאחור — סדר פעולות מדויק לחזרה לגרסת הקונפיגורציה הקודמת: היכן נמצאים גיבויים, כיצד בודקים את הקונפיגורציה לפני החלה, תוך כמה זמן מתבצעת החזרה.
- סדר עדכון תעודות — מי אחראי להנפקה, כיצד מתבצע חידוש אוטומטי, מה לעשות בשגיאת בדיקה וכיצד לוודא שהתעודה החדשה נקלטה בלי ניתוק חיבורים.
- הוראות למשמרת תורנות — תקלות אופייניות וסימנים להופעתן ביומנים, מה ניתן לתקן במקום ובאילו מקרים יש להסלים את המשימה, עם ציון ספי הפעלה של הניטור.
- בדיקת שינויים — תיאור סביבת הבדיקה שבה עריכות נבחנות לפני פרודקשן, וסט הבדיקות שצריך לעבור לפני החלת הקונפיגורציה על צמתי הייצור.
מקרה בוחן: כיצד נקודת הכניסה שרדה עומס שיא בלי הידרדרות
הלקוח — שירות אזורים אישיים עם קהל יומי של עשרות אלפי אנשים. נקודת כניסה הייתה אחת: כל התעבורה התקבלה בצומת בודד, שהוא גם החזיק את התעודות וסיים חיבורים מאובטחים. כל עוד העומס גדל בהדרגה, הארכיטקטורה התמודדה, אבל בשיא מבצע היא נתקלה במגבלה.
מה היה בשיא
בשעות המכירה מספר החיבורים בו-זמנית הוכפל לעומת הרגיל, והצומת החל להתקרב למגבלת החיבורים. שיעור הבקשות שהסתיימו בשגיאה הגיע עד 7%, וזמן התגובה של חלק מהמשתמשים חצה 4 שניות — עגלות קנייה לא שרדו עד התשלום. כאב נפרד: כל ריסטארט означал עד 9 דקות של אי-זמינות מלאה, כי לא היה למי לעבור.
מה שינו
הרכיבו זוג צמתים עמידים בכשלים HAProxy בשני אזורי זמינות שונים, וחיברו אותם בכתובת וירטואלית משותפת. הצומת הפעיל מקבל תעבורה, הצומת הגיבוי בודק כל הזמן את מצבו ומשתלט על הכתובת בעת כשל — בתוך שבריר שנייה, בלי לנתק חיבורים שכבר הוקמו.
בנפרד חישבו מחדש תורים וזמני timeout לפי פרופיל הבקשות בפועל, כדי שלקוח איטי לא יתפוס מקומות של צמתים בריאים.
מה התוצאה
זמן ההשבתה בעת כשל של צומת הצטמצם מ-9 דקות לפחות משנייה — המשתמש לא מספיק להבחין במעבר. שיעור השגיאות בשיא ירד מ-7% ל-0,2%, וזמן התגובה על אותו נפח תעבורה — מ-4,5 ל-0,8 שנייה. עדכונים ותחזוקה מתבצעים כעת צומת-צומת: השני מחזיק את התעבורה, ואין יותר צורך בחלון אי-זמינות נפרד.
איזון חיבורים או ניתוב לפי תוכן?
אם התעבורה אינה ניתנת לפענוח — חיבור לבסיס נתונים, broker של תורים, זרימה מוצפנת שהפרוקסי אינו מפענח — די באיזון חיבורים: ההחלטה מתקבלת פעם אחת בעת הקמת הקשר, ומכאן כל החבילות עוברות לצומת אחד.
ברגע שמופיעות בקשות HTTP עם נתיבים, מארחים וכותרות שונים, חלוקה בלבד לא מספיקה — נדרש ניתוב לפי תוכן.
איזון חיבורים זול יותר: הפרוקסי לא מפרק את הבקשה, אלא סופר חיבורים לפי צמתים — לפי סיבוב, לפי מספר פעילים, לפי כתובת הלקוח, כשצריך להחזיק את המפגש על שרת אחד. המגבלה אחת: בתוך החיבור לא רואים איפה /api ואיפה סטטי, ולכן לא ניתן לפצל עומס לפי סוגי בקשות.
ניתוב לפי תוכן פועל ברמת HTTP: הבקשה מפורקת במלואה, והחוקים מפזרים אותה בין מאגרים לפי מארח, נתיב או כותרת. מופיעות בדיקות תקינות לפי קוד תשובה ולא לפי פורט פתוח, סימון מפגש ב-cookie והעברת כתובת הלקוח המקורית. המחיר — חישוב לכל בקשה וסט כותרות משותף עם האפליקציה.
שילוב של הגישות — ארכיטקטורה עובדת: צומת מקבל תעבורת HTTP ומחלק אותה בין מאגרים, וחלק מהמאגרים משאיר חיבורים לא מפורקים — עבור בסיסי נתונים, תורים או זרימה מוצפנת שהמפתחות שלה נשארים אצל בעל השירות. בחירת המצב קובעת גם אילו בדיקות תקינות יהיו למאגרים וגם כיצד תבצעו אבחון של כשל.
| אפשרות | מתי מתאימה | מגבלות |
|---|---|---|
| איזון חיבורים | תעבורה שאינה ניתנת לפענוח: בסיסי נתונים, תורים, הצפנה בלי פענוח בפרוקסי | נתיבים וכותרות לא נראים, לא ניתן להפריד סוגי בקשות |
| ניתוב לפי תוכן | שירותי HTTP, כמה אפליקציות, סטטי בנפרד מ-API | יקר יותר בחישוב, דרושים כותרות וחוקים מתואמים |
| שילוב | קו מעורב: נתיבי HTTP כלפי חוץ, זרימות לא מפורקות בפנים | הארכיטקטורה מורכבת יותר, נדרשים מפת מאגרים ובדיקות |
ספקות טכניים נפוצים לפני שינוי הארכיטקטורה
בעת העברת הארכיטקטורה השאלה המרכזית אינה „האם זה יעבוד“, אלא מה יקרה לחיבורים שכבר פתוחים ברגע המעבר. להלן ארבעה ספקות שצפים לרוב לפני שאנחנו נוגעים במשהו בסביבת הייצור.
האם חיבורים קיימים מנותקים בעת מעבר בין צמתים?
עבור בקשות קצרות המעבר אינו מורגש: הלקוח חוזר על הפנייה, והפרוקסי ההפוך שולח אותו לצומת חי. חיבורים ארוכים — streaming, WebSocket — מוצאים דרך סיום רך: השרת מוסר מהסבב, אך ממתין לתשובה במפגשים שכבר פתוחים, ובקשות חדשות עוברות לנותרים.
האם צריך לשנות משהו באפליקציה?
ברוב המקרים לא — האפליקציה מקבלת בקשות כמו קודם, ורק נקודת הכניסה משתנה. עריכות נדרשות במקום שבו הקוד עצמו מחשב את כתובת הלקוח לפי כותרות: אם הכתובת המקורית אינה מועברת, ביומנים ובהגבלות לפי IP מופיעה כתובת מאזן העומס. זה נסגר בהגדרה, בלי הוצאת גרסת קוד חדשה.
כמה מהר מושבת צומת תקול?
תדירות הבדיקות והסף מוגדרים במפורש. בערכים מכוילים, צומת לא תקין נופל מהסבב בתוך כמה שניות — אותו זמן לוקחת גם החזרה לאחר התאוששות. בדיקות תכופות מדי יוצרות התראות שווא בשיא עומס, ונדירות מדי מאריכות את ההידרדרות, לכן הספים נבחרים לפי פרופיל התעבורה בפועל, ולא „בעין“.
כיצד לעדכן קונפיגורציה בלי השבתה?
תחילה בדיקת תחביר של הקובץ, אחר כך ריסטארט הדרגתי: תהליך חדש עולה ומקבל תעבורה, והישן מסיים מפגשים פעילים. חלון אי-הזמינות הוא אפס, ולא נוצרים מחדש sockets. חזרה לאחור — החזרת הקונפיגורציה הקודמת וחזרה על אותו הליך, ולכן מכניסים שינויים בצעדים קטנים.
נדון במשימה ונחזור עם תוכנית הטמעה
כדי לחזור עם תוכנית הטמעה ולא עם סיכומים כלליים, אנחנו צריכים מכם סט נתוני פתיחה. לפיהם נבצע הערכת זמנים, נבחר אלגוריתם איזון וסדר מעבר — כך העבודה מתבצעת בלי לעצור את השירות.
- רשימת השירותים שיעברו דרך הפרוקסי ההפוך: כתובות, פורטים, פרוטוקולים והיכן מסתיימות התעודות — במאזן העומס או בצד האפליקציות.
- הארכיטקטורה הנוכחית: מה קיים עכשיו (HAProxy או מאזן עומס אחר), כיצד מתחלקת התעבורה, אילו בדיקות זמינות צמתים מוגדרות וכיצד מתבצעת חזרה לאחור.
- עומס צפוי: בקשות שיא בשנייה, מספר חיבורים בו-זמניים, שיעור מפגשים ארוכים — מכך תלויים אלגוריתם האיזון והמגבלות.
- מגבלות על חלונות עבודה: משך מעבר מותר, האם יש סביבת גיבוי, האם ניתן לשנות רשומות DNS ובאיזו שעה.
- הרשאות גישה: שרתים, מאגר קונפיגורציות, פאנל ניהול תעודות, הרשאות לשינוי חוקי רשת.
- דרישות לתעודות: מי מנפיק, האם נדרשת החלפה אוטומטית ובדיקת שרשרת בעת עדכון.
- קריטריוני קבלה: כשל צומת, זינוק בתעבורה, החלפת תעודה מתוכננת — ומי מצד הלקוח מאשר את התוצאה.
זה מספיק כדי להכין תוכנית הטמעה עם שלבים, נקודות חזרה לאחור והערכת זמנים. תארו את המשימה — נחזור עם תוכנית והרכב העבודות.







