Rolling Deployment: איך זה באמת עובד מתחת למכסה המנוע

מאת צוות מדיה דיל · 04.08.2026 · DevOps · 7 דק׳

פירוק מעמיק של rolling deployment: health checks, PodDisruptionBudget, graceful shutdown וטעויות שגורמות לתקריות פרודקשן.

rolling deployment היא כנראה אסטרטגיית הדיפלוי הנפוצה ביותר בפועל, כי היא ברירת המחדל של Kubernetes, ECS ורוב פלטפורמות ה-orchestration המודרניות — אבל דווקא בגלל זה היא גם האסטרטגיה שהכי הרבה צוותים מפעילים בלי להבין באמת את הפרמטרים שקובעים אם היא תעבוד חלק או תגרום לתקרית. במאמר הזה נצלול לתוך המנגנון הפנימי של rolling deployment: איך הוא עובד ברמת ה-instances, אילו פרמטרים קובעים את קצב ההחלפה, ומה קורה כשגרסה חדשה תקועה באמצע.

המנגנון הבסיסי: Replace Incrementally

rolling deployment עובד על עיקרון פשוט: מתוך N instances שרצים את הגרסה הישנה, מורידים k מהם (כאשר k מוגדר על ידי maxUnavailable), מעלים k instances חדשים עם הגרסה החדשה, ממתינים ש-health check יאשר שהם בריאים, ורק אז ממשיכים לקבוצה הבאה. זה חוזר על עצמו עד שכל ה-instances רצים על הגרסה החדשה. ברוב המימושים (Kubernetes Deployment, למשל) התהליך מנוהל אוטומטית על ידי ה-controller, שמשווה כל הזמן בין המצב הרצוי (desired state) למצב בפועל ומתקדם בהדרגה.

הפרמטר maxSurge מאפשר להרים instances נוספים מעבר לכמות המקורית לפני שמורידים ישנים, כך שבמקום להקטין קיבולת (maxUnavailable) אפשר להגדיל אותה זמנית — Kubernetes מרים instance חדש נוסף, ורק אחרי שהוא בריא מוריד instance ישן. שילוב נכון בין שני הפרמטרים קובע האם הדיפלוי יגרום לירידה זמנית בקיבולת (מסוכן בזמן עומס שיא) או ישמור על קיבולת קבועה או אף עודפת במחיר עלות compute נוספת זמנית.

Health Checks: הלב של המנגנון

rolling deployment תלוי לחלוטין באיכות ה-health checks. Kubernetes מבחין בין readiness probe (האם ה-instance מוכן לקבל תעבורה כרגע) לבין liveness probe (האם התהליך עדיין חי ולא תקוע וצריך restart). הבלבול הנפוץ ביותר: הגדרת readiness probe שבודקת רק "השרת עונה ל-HTTP" בלי לבדוק תלויות קריטיות כמו חיבור למסד נתונים או cache. במקרה כזה, instance "בריא" לכאורה מתחיל לקבל תעבורה אמיתית לפני שהוא באמת מסוגל לשרת אותה, מה שגורם לגל שגיאות 500 בדיוק בזמן הדיפלוי שהיה אמור להיות "בטוח".

פרמטר נוסף שקריטי לכוונן נכון הוא initialDelaySeconds ו-periodSeconds — אם ה-readiness probe נבדק מוקדם מדי (לפני שהאפליקציה סיימה לעלות), ה-instance עלול להיכשל בבדיקה הראשונה ולהיכנס למעגל של restart מיותר, גם אם בפועל הוא היה מגיע למצב בריא תוך עוד כמה שניות. הכיוונון הנכון דורש הבנה אמיתית של זמן ה-startup בפועל, ולא ניחוש.

שיקול נוסף שקשור ישירות לאיכות ה-health checks הוא ההבחנה בין startup probe ל-liveness probe באפליקציות עם זמן עלייה ארוך. ללא startup probe נפרד, ה-liveness probe עלול להתחיל לבדוק את התהליך לפני שהוא סיים לעלות, לפרש את זה כתקיעה, ולהרוג אותו (restart) עוד לפני שקיבל הזדמנות אמיתית להתחיל לשרת תעבורה — מעגל שגורם לאפליקציה איטית-לעלייה להיכנס ללולאת restart אינסופית שנראית כמו "באג בקוד" אך למעשה היא תוצאה ישירה של הגדרת probes לא מותאמת.

PodDisruptionBudget ומניעת קריסה בזמן דיפלוי

ב-Kubernetes, PodDisruptionBudget (PDB) קובע כמה pods מותר להיות לא זמינים בו-זמנית מכל סיבה — לא רק rolling deployment, אלא גם node drains, autoscaling events ותחזוקת cluster. ללא PDB, אירועים אלה יכולים להצטבר יחד עם דיפלוי פעיל ולגרום לירידת קיבולת חמורה בהרבה ממה שתוכנן. הגדרת minAvailable או maxUnavailable ברמת ה-PDB יוצרת רשת ביטחון שמונעת מ-Kubernetes להוריד יותר מדי pods בו-זמנית, גם אם כמה תהליכים שונים מנסים לבצע שינויים על אותו deployment באותו זמן.

יתרון נוסף של PDB שלרוב מתגלה רק בדיעבד: הוא מגן גם מפני node maintenance אוטומטי בענן (למשל spot instance reclamation ב-AWS או node auto-repair ב-GKE). בלי PDB, ספק הענן עלול לפנות מספר גדול מדי של nodes בבת אחת לצורך תחזוקה, ואם הפינוי הזה חופף עם rolling deployment פעיל, שתי הפעולות מתחרות על אותה קיבולת ויוצרות ירידת שירות שקשה לאבחן בדיעבד כי היא נובעת משילוב של שני תהליכים עצמאיים שכל אחד מהם, בפני עצמו, נראה תקין.

לבסוף, כדאי לזכור ש-PDB לא מונע מ-rollout להתקדם — הוא רק מגביל כמה pods יכולים להיות לא זמינים בו-זמנית ממקורות שונים. אם ה-PDB מוגדר קפדני מדי (למשל minAvailable: 100%), הוא עלול לחסום גם דיפלוי רגיל וגם תחזוקת cluster לחלוטין, ולגרום ל-deadlock שבו שום שינוי לא יכול להתקדם עד שמישהו מתערב ידנית.

Rollback: כשמשהו משתבש באמצע

מה שהופך rolling deployment לבטוח יותר מ-recreate הוא רשת הביטחון המובנית: אם ה-health check של instance חדש נכשל שוב ושוב, ה-controller עוצר את ההתקדמות (או, בהגדרת progressDeadlineSeconds, מסמן את ה-rollout כנכשל) ולא ממשיך להחליף עוד instances. אבל rollback אוטומטי מלא — חזרה לגרסה הקודמת — לרוב אינו קורה מעצמו; הוא דורש כלי חיצוני (Argo Rollouts, Flagger) שמנטר metrics ומפעיל kubectl rollout undo אוטומטית כשמזהה בעיה. בלי כלי כזה, ה-rollout פשוט "נתקע" באמצע במצב מעורב של גרסה ישנה וחדשה, וממתין להתערבות ידנית.

Canary כתוספת ל-Rolling: Analysis Steps

כלים כמו Argo Rollouts מרחיבים את מודל ה-rolling הבסיסי בכך שהם מוסיפים "analysis steps" בין כל צעד של החלפת instances — לא רק בדיקת health check טכנית, אלא שאילתה אמיתית ל-Prometheus או מערכת ניטור אחרת שבודקת אם ה-error rate או ה-latency p99 של הגרסה החדשה נמצאים בטווח הסביר לעומת הגרסה הישנה, ורק אם כן ממשיכים לצעד הבא. זה הופך rolling deployment ליישום מעשי של progressive delivery בלי לבנות מערכת canary נפרדת מאפס, ומאפשר לעצור אוטומטית באמצע rollout אם מדד עסקי מתדרדר, גם אם ה-pods עצמם עוברים health check תקין.

יש כאן מלכודת שכיחה: אם ה-analysis step בודק מדד שמושפע גם מגורמים חיצוניים (למשל עומס פתאומי בלתי קשור לדיפלוי), הוא עלול לעצור rollout תקין בטעות. הפתרון הנפוץ הוא שימוש בהשוואה יחסית בין הגרסה הישנה לחדשה באותו חלון זמן, ולא בסף מוחלט — כך שעומס שמשפיע על שתי הגרסאות באותה מידה לא נתפס בטעות כרגרסיה של הגרסה החדשה.

Session Affinity ומצב מבוזר

rolling deployment מניח שכל instance הוא stateless — שאפשר להרוג אותו ולהרים אחר בלי לאבד מידע קריטי. אפליקציות עם WebSocket connections ארוכי טווח או session state מקומי צריכות טיפול מיוחד: או ניתוק חלק (graceful shutdown) שמחכה לסיום חיבורים פעילים לפני סגירה, או drain מבוקר שמפנה תעבורה חדשה מה-instance הישן בלי לסגור חיבורים קיימים בכוח. terminationGracePeriodSeconds ב-Kubernetes קובע כמה זמן ה-instance מקבל לסיים עבודה בצורה מסודרת לפני שהוא נהרג בכוח (SIGKILL) — ברירת המחדל של 30 שניות לרוב לא מספיקה לחיבורי WebSocket ארוכים או לעיבוד batch שכבר החל.

Database Connections ו-Connection Pool Exhaustion

תופעה נפוצה שמפתיעה צוותים: rolling deployment שמכפיל זמנית את מספר ה-instances (בגלל maxSurge) מכפיל גם את מספר החיבורים הפתוחים למסד הנתונים, אם כל instance מחזיק connection pool קבוע משלו. אם מסד הנתונים מוגדר עם מגבלת חיבורים מקסימלית שלא לוקחת בחשבון את השיא הזמני הזה, הדיפלוי עצמו יכול לגרום ל-connection pool exhaustion ולשגיאות "too many connections" בדיוק בזמן שהמערכת אמורה להיות הכי יציבה. הפתרון הוא תכנון קיבולת חיבורים שמביא בחשבון את השיא (peak) בזמן דיפלוי, לא רק את המצב היציב.

ניטור קצב ההתקדמות ו-Deployment Timeout

פרמטר שקל לשכוח אך משמעותי מאוד הוא progressDeadlineSeconds — הזמן המקסימלי שה-controller ממתין לפני שהוא מסמן rollout כתקוע. ללא הגדרה מפורשת, דיפלוי שנתקע (למשל בגלל image שלא מצליח להימשך מ-registry, או health check שלעולם לא עובר) יכול להישאר תקוע לנצח במצב ביניים, כשחלק מהתעבורה מגיעה לגרסה הישנה וחלק לחדשה, בלי שאף אחד יקבל התראה. הגדרת deadline סביר, יחד עם alerting שמחובר אליו, מבטיחה שצוות התפעול יידע תוך דקות ולא יגלה את התקלה רק כשלקוח מתלונן.

מעבר להגדרת ה-deadline עצמו, חשוב לחבר את ה-alert ל-runbook ברור: כשה-rollout נתקע, הצעד המיידי הוא לא תמיד rollback אוטומטי (לפעמים הבעיה זמנית, כמו עומס רגעי על ה-registry), אלא בדיקה אנושית מהירה של הסיבה. ארגונים שמגדירים alert בלי runbook מגלים שהצוות מבזבז זמן יקר בניסיון להבין מה בדיוק תקוע ולמה, בדיוק ברגע שכל דקה חשובה.

טעויות נפוצות בפרודקשן

הטעות הראשונה היא readiness probe רדוד שלא בודק תלויות אמיתיות. השנייה היא היעדר PodDisruptionBudget, שמאפשר לאירועי תחזוקה להצטבר עם דיפלוי ולגרום לירידת קיבולת חמורה. השלישית היא graceful shutdown לא מוגדר, שגורם לניתוקי חיבורים פתאומיים בכל דיפלוי. הרביעית היא היעדר automated rollback שמזהה עלייה ב-error rate — במקרים רבים דיפלוי "נכשל בשקט": הגרסה החדשה עולה, לא נכשלת ב-health check הטכני, אבל גורמת לעלייה במדדי שגיאה עסקיים שאין להם ניטור אוטומטי מחובר ל-rollout.

מתי Rolling הוא הבחירה הנכונה

Rolling deployment מתאים כברירת מחדל לרוב השירותים stateless שרצים על מספר instances — הוא לא דורש תשתית כפולה כמו blue-green, ומספק zero-downtime אמיתי כשמוגדר נכון. הוא פחות מתאים כשצריך ודאות מלאה של rollback מיידי (blue-green עדיף), או כשהשינוי מסוכן מספיק שצריך לבדוק אותו על אחוז קטן מהתעבורה לפני חשיפה מלאה (canary עדיף). בפרויקטים שאנחנו בונים במדיה דיל, rolling deployment עם health checks מדויקים ו-PDB מוגדר נכון הוא לרוב הבסיס שעליו בונים שכבות נוספות של בטיחות. הצוותים שמשקיעים בפרטים הקטנים — probes מותאמים, PDB מדויק, timeout מוגדר — הם אלה שלא צריכים לחשוב על דיפלוי כאירוע מלחיץ, אלא כפעולה שגרתית שקורית עשרות פעמים ביום בלי שאף אחד שם לב.

סיכום

rolling deployment נראה פשוט אך מכיל פרטים קריטיים שקובעים אם הוא באמת zero-downtime או רק "נראה ככה" עד שמשהו משתבש. health checks מדויקים, PodDisruptionBudget, graceful shutdown נכון, ותכנון קיבולת חיבורים למסד הנתונים הם ההבדל בין דיפלוי שקורה בשקט לבין אירוע שמעיר את כל הצוות בשעה 2 בלילה.

תגיות: rolling deployment · Kubernetes · health checks · PodDisruptionBudget · zero-downtime · DevOps · graceful shutdown

← חזרה לבלוג · צור קשר