מ-47 דקות ל-7 דקות - איך שברנו את ה-Pipeline וחסכנו הון
CI/CD איטי גונב פוקוס וכסף. שחר פולק מפרק מעבר משרת יחיד ל-Kubernetes, GitHub Actions, בדיקות, Canary ו-rollback - והדרך מ-47 ל-7 דקות בלי להפוך לקוחות לניסוי.
צפו בהרצאה · 42:12הסיפור שמאחורי ההרצאה
- המעבר לא היה אופטימיזציה של פקודה אחת. הוא החליף פריסה ידנית על שרת יחיד בתהליך שאפשר לצוות לשחרר, לבדוק ולחזור אחורה בבטחה.
- GitHub Actions, סביבת staging ובדיקות אוטומטיות עצרו כשל לפני שהגיע ללקוח והורידו את ה'שייקומטר' לפני merge.
- לא כל בדיקה צריכה לרוץ על כל commit: כ-1,400 בדיקות unit ו-component רצות במקביל, E2E קצר בודק את השרתים, ובדיקות כבדות נשארות לילה.
- Canary deployment העביר תעבורה בהדרגה מ-3% ל-100%, ובדיקת עלייה ב-500 אפשרה auto rollback עם Prometheus.
- המחיר של שרתים כפולים וזמן פריסה ארוך יותר הוא tradeoff מכוון כדי לא להפוך לקוחות לשדה הניסוי.
CI/CD לא נהיה בעיה כשה-build ארוך. הוא נהיה בעיה כשמפתח כבר שכח מה כתב, הלקוח מחכה, והדרך היחידה לשחרר היא להתחבר ב-SSH לשרת אחד ולקוות שלא טעינו ב-branch. שם נעלמת המהירות שסטארטאפ אמור לקבל בחינם.
בהרצאה הזו שחר פולק, Head of Engineering ב-Imagen AI, מספר איך הצוות עבר משרת EC2 יחיד ופריסות ידניות לתהליך עם Kubernetes, GitHub Actions, staging, בדיקות, Canary ו-auto rollback. המספרים בכותרת - מ-47 דקות ל-7 - הם התוצאה. הסיפור המעניין יותר הוא כל מה שנדרש כדי שאפשר יהיה לסמוך על המהירות הזאת.
למי שרוצה קודם לפרק את מושגי היסוד, יש גם את השיחה על CI/CD עם ניב יונגלסון. כאן הפוקוס הוא לא על ההגדרות. הוא על מה שנשבר כשהמערכת והצוות כבר לא קטנים.
שרת אחד הוא התחלה לגיטימית. הוא לא תכנית צמיחה
כששחר הצטרף ל-Imagen AI, החברה הייתה בתחילתה. היו בערך מאה לקוחות משלמים, וכל המוצר רץ על EC2 אחד. גם הקוד וגם MySQL היו על אותה מכונה. כדי לפרוס, מתחברים לשרת, מוודאים שנמצאים על ה-branch הנכון, מושכים קוד ומפעילים מחדש. בזמן הזה יש downtime. אם משכו משהו לא נכון או שברו שלב בדרך, אין שער אוטומטי שעוצר את זה.
זו לא ביקורת בדיעבד על סטארטאפ מוקדם. ההחלטה הייתה tradeoff: קודם נותנים ערך, אחר כך משקיעים בתשתית. אבל חברה לא נשארת באותו שלב. כשהצוות גדל וכמות הלקוחות עלתה מעשרות אלפים בודדים למעל 30 אלף, השרת היחיד הפך גם למגבלת קיבולת וגם למגבלה על קצב הפיתוח.
Trade-off זה לא מילה גסה.
הפרדה של הדאטאבייס מהשרת, Kubernetes כאורקסטרטור ויכולת לעשות scale down בשעות שקטות ו-scale up בעומס נתנו לצוות בסיס להתרחב. זה לא קסם של Kubernetes. זה מעבר ממכונה שצריך לגעת בה ידנית למערכת שמסוגלת להריץ services, instances ותעבורה בצורה מנוהלת.
Pipeline הוא סדר החלטות, לא רק YAML
לאחר שהוגדר הכיוון, הצוות היה צריך להחליט מה קורה בין commit לפרודקשן. שחר בחר ב-GitHub Actions, בין השאר בגלל תבניות, פיצ’רים והתחושה שהכלי נע עם המיינסטרים במקום להשאיר את הצוות תלוי בשרת Jenkins או בשירות שנעלם. אבל ה-tool הוא רק האורקסטרציה. השאלה היא מה מכניסים לתהליך.
התשובה הראשונה הייתה staging. קוד צריך להיבדק בסביבה שדומה לפרודקשן, אבל בלי לקוחות. שם אפשר להפציץ את המערכת ב-load test, לראות מתי היא קורסת, וללמוד לפני שלקוח משלם מאבד אמון. staging אינו פרודקשן קטן. הוא הגבול שמונע מהצוות להפוך פריסה לניסוי על משתמשים.
הצעד הבא היה בדיקות אוטומטיות. בתחילת הדרך מפתח אחד יכול לחיות עם בדיקות ידניות. ברגע שיש צוות ולקוחות, טסט שנכשל חייב לעצור פריסה ולשלוח התראה. המטרה אינה רק לתפוס bug. היא להחזיר למפתח ביטחון ללחוץ merge.
זה כמה היד שלך רועדת לפני שאתה לוחץ merge.
שחר קורא לזה “שייקומטר”. המדד אינו פורמלי, אבל הוא אמיתי: אם כל merge מרגיש כמו הימור על הלקוח, הצוות לא באמת מדלוור מהר.
לא כל E2E צריך לעכב כל commit
טסטים נותנים ביטחון, אבל E2E יכולים להיות יקרים ואיטיים. לכן הצוות לא בחר ב-100% coverage ולא בהרצת כל סוג בדיקה בכל commit. השרת הכבד ביותר מריץ כ-1,400 בדיקות unit ו-component במקביל, בכ-2.5 דקות. מעליהן יש קומץ בדיקות E2E של השרתים, שאורכות כ-28 עד 30 שניות.
בצד אחר של המוצר יש אפליקציות Windows ו-Mac. שם בדיקת E2E מלאה מרימה מכונה, מתקינה אפליקציה, מעלה קבצי תמונה, מורידה אותם ובודקת תוצאה. היא יכולה לקחת 40 עד 45 דקות. לכן היא רצה בלילה או ידנית לפני שחרור, לא על כל שינוי.
זו החלוקה החשובה: להריץ מוקדם ומהר בדיקות שמונעות טעות יקרה, ולשמור את הבדיקות הכבדות למקום שבו המחיר שלהן מוצדק. אפשר לקשר את זה גם להרצאה על בדיקת API, שבה בדיקות אינן מדד לקישוט אלא קו הגנה מול שינוי אמיתי.
בדיקה זולה יכולה לעצור deploy יקר
אחת הדוגמאות הפרקטיות בהרצאה מגיעה מהמיגרציות של הדאטאבייס. הצוות עבד עם ORM, ובעבר תהליך הפריסה עדכן את הסכמה אוטומטית. בשלב מסוים זה הפסיק להיות בטוח: שינוי בטעות של סוג עמודה יכול לפגוע בדאטה קיים.
הפתרון לא היה להפסיק לשנות סכמות, אלא להוציא את האחריות מה-deploy. מפתח שיוצר שינוי מוסיף שורת migration, מריץ אותה מקומית, ב-dev ובפרודקשן. במקביל, בדיקת E2E מפיקה מה-ORM סכמת JSON, משווה אותה ל-information schema של הדאטאבייס, ומאפשרת דברים נוספים בדאטאבייס - אבל לא קוד שמנסה לשלוף שדה שאינו קיים או מצפה לסוג אחר.
הבדיקה הזאת נמשכת בערך עשר שניות. אם היא נכשלת, ה-build נעצר ואין deploy. זה לא coverage מרשים למצגת. זו בדיקה קטנה שמחזיקה גבול קריטי בין קוד לדאטה.
Blue-Green לא מבטל סיכון. הוא מקטין את הרדיוס שלו
גם אחרי staging וטסטים, פריסה יכולה לשבור משהו. כאן נכנס Blue-Green deployment: מעלים instances חדשים לצד הקיימים, ו-load balancer מעביר אליהם תעבורה בהדרגה. אין רגע שבו מכבים את הכול בתקווה שהגרסה החדשה כבר מוכנה לקבל 100% מהעומס.
הצוות לקח את הרעיון צעד נוסף עם Canary. הגרסה החדשה מתחילה אצל 3% מהלקוחות. אם הכול תקין, היא עולה ל-10%, ל-40% ול-100%. “תקין” לא אומר שאין אף error. פרומיתיוס סופר תגובות HTTP של 200, 400 ו-500; החשד האמיתי הוא עלייה ב-500 ביחס לגרסה הקודמת בחלון זמן מוגדר. במקרה כזה מופעל auto rollback אל ה-instances הקודמים שנשארו באוויר.
אנחנו לא רוצים שהמשתמשים שלנו יהיו שם.
העלות ברורה: למשך זמן מסוים מחזיקים יותר שרתים, ופריסה מלאה לוקחת בערך שבע עד שמונה דקות. שחר מציג את זה ישירות: זה מחיר שהוא מוכן לשלם כדי שלקוח לא יספוג את התקלה. גם auto scaling ממשיך את אותה לוגיקה - מוסיפים instances כשהעומס עולה, במקום לשלם תמיד על מכונה גדולה במיוחד.
השורה התחתונה
CI/CD מהיר אינו היעד אם הוא רק מעביר כשל לפרודקשן מהר יותר. בהרצאה של שחר, הדרך מ-47 דקות ל-7 נבנתה משכבות שכל אחת מורידה סוג אחר של סיכון:
- פריסה ידנית על שרת יחיד הוחלפה בתשתית שמסוגלת לגדול עם המערכת.
- staging ובדיקות אוטומטיות עוצרים כשל לפני שהוא פוגש לקוח.
- בדיקות E2E מחולקות לפי עלות וזמן, לא לפי אידאולוגיה של coverage.
- Canary ו-auto rollback הופכים תקלה אפשרית לאירוע עם רדיוס קטן יותר.
- בכל שלב יש מחיר: זמן, עלות תשתית או אחריות של מפתח על migration.
זה בדיוק מה שהופך את ה-pipeline הזה לשימושי גם מחוץ ל-Imagen AI. אל תעתיקו את הכלים כצ’קליסט. התחילו מההמתנה שמפרקת לכם את ה-flow, מהכשל שמגיע מאוחר מדי, ומהלקוח שלא אמור לגלות ראשון שהגרסה החדשה נשברה. את שאר ה-tradeoffs אפשר כבר לבחור במודע.
"Trade-off זה לא מילה גסה."
- שחר פולק
"זה כמה היד שלך רועדת לפני שאתה לוחץ merge."
- שחר פולק
"אנחנו לא רוצים שהמשתמשים שלנו יהיו שם."
- שחר פולק
- 00:03:43 DX הוא חלק מהמהירות העסקיתשחר מחבר בין Developer Experience, היכולת לשחרר ערך מהר, והיתרון של סטארטאפ שיכול לזוז מהר יותר ממתחרה גדול.
- 00:07:12 המצב ההתחלתי: שרת יחיד ו-SSH לפרודקשןכל הקוד והדאטאבייס חיו על EC2 אחד, והפריסה דרשה התחברות ידנית, משיכת branch והפעלה מחדש עם downtime וסיכון לטעות.
- 00:11:28 Kubernetes ו-GitHub Actions במקום תהליך ידניהמעבר לאורקסטרציה, scale up ו-scale down ול-GitHub Actions בנה תשתית שמסוגלת לגדול עם המערכת והצוות.
- 00:15:39 staging ובדיקות לפני הלקוחסביבת staging אפשרה להריץ עומסים וניסויים בלי לפגוע באמון הלקוחות, ובדיקות אוטומטיות חסמו פריסה אחרי כשל.
- 00:19:29 E2E זול, בדיקות מקביליות ומה לא רץ בכל commitשחר מפריד בין בדיקות E2E קצרות של השרתים לבין E2E כבד של אפליקציות desktop שרץ בלילה או לפני שחרור.
- 00:28:57 בדיקת סכמת הדאטאבייס בעשר שניותהשוואת הסכמה שמופקת מה-ORM למבנה הדאטאבייס תופסת חוסר התאמה לפני deploy, בלי לסכן טבלאות קיימות במיגרציה אוטומטית.
- 00:33:23 Blue-Green, Canary ו-auto rollbackגרסה חדשה עולה לצד הקיימת, התעבורה עוברת בהדרגה, ועלייה ב-500 ביחס לגרסה הקודמת מחזירה מיד לגרסה הידועה כתקינה.
- 00:41:06 auto scaling והמחיר של חוויית לקוחהמערכת מוסיפה instances לפי העומס במקום להחזיק מכונות גדולות כל הזמן, ומקבלת במודע עלויות זמניות כדי לצמצם פגיעה בלקוחות.
מה כוסה בהרצאה
שאלות מההרצאה
למה פריסה ידנית לשרת יחיד הפכה לבעיה?
בהתחלה כל הקוד רץ על EC2 יחיד, והפריסה דרשה SSH, מעבר ל-branch הנכון, משיכת הקוד והפעלה מחדש של השרת. התהליך יצר downtime, סיכון לטעויות ידניות, וחסר בו שער בדיקות אוטומטי. כשהצוות והעומס גדלו, הוא כבר לא איפשר לשחרר מהר ובביטחון.
למה צריך סביבת staging אם יש בדיקות אוטומטיות?
בהרצאה שחר מתאר staging כסביבה שמדמה פרודקשן בלי לקוחות. היא מאפשרת להריץ stress ו-load tests ולגלות איפה המערכת קורסת. בדיקות אוטומטיות חשובות, אבל אינן סיבה להפוך משתמשים משלמים לניסוי.
איך בוחרים אילו בדיקות להריץ בכל commit?
הבחירה אינה להריץ הכול תמיד. בדיקות unit ו-component של השרת הכבד רצות במקביל בכ-2.5 דקות, ו-E2E קצר של השרתים רץ בכ-28 עד 30 שניות. E2E כבד שמתקין אפליקציות desktop, מעלה ומוריד קבצים, נשאר לילה או רץ ידנית לפני שחרור.
איך Canary deployment מגן על פריסה תקולה?
במקום להעביר את כל התעבורה לגרסה החדשה, שחר מתאר התחלה ב-3%, עלייה ל-10% ול-40%, ורק אחר כך ל-100%. Prometheus סופר תגובות 200, 400 ו-500; עלייה ב-500 ביחס לגרסה הקודמת מפעילה auto rollback אל ה-instances הקודמים שעדיין חיים.