הגדרת Apache לעומסים: MPM, php-fpm, .htaccess

עומסי שיא הופכים אתר יציב לתור של כשלים, ולעיתים הסיבה נעוצה בהגדרות שרת האינטרנט. אנו מגדירים Apache בהתאם לפרופיל התעבורה שלכם כפתרון מלא: מצבי עיבוד בקשות, עבודה עם תוכן דינמי, כללים ומגבלות. התוצאה — תגובה צפויה וקונפיגורציה שהצוות שלכם מבין ויכול לפתח.

היכולות שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1359
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

התאמת Apache לעומס: היכן מתחילים הכשלים

תמונה אופיינית: האתר מחזיק תעבורה רגילה בשלווה, אבל במבצע, ביום פרסום חדשה או בשעת שיא בערב זמן התגובה עולה, חלק מהבקשות נופלות עם 502 ו-504, והמעבד בכלל לא עמוס באופן מלא. תצורת Apache נשארת כברירת מחדל מהיצרן — אף אחד לא חישב אותה מחדש לפרופיל הבקשות האמיתי.

ערכי ברירת המחדל מהיצרן מכוונים לתאימות, לא לעומס. מצב העיבוד מברירת מחדל מחזיק תהליך או נימה נפרדים לכל חיבור, php-fpm מקבל מאגר מטפלים קבוע, ותיקיות עם דריסה מאופשרת מכריחות את שרת האינטרנט לקרוא מחדש את .htaccess בכל בקשה — במקום שבו די בכלל אחד בתצורת vhost.

צוואר הבקבוק של שרת האינטרנט מתגלה בשרשרת. תחילה גדל התור למטפלים פנויים של PHP, אחר כך נגמרת מכסת החיבורים במקביל, לאחר מכן שכבת ה-proxy מחזירה ללקוח שגיאה — והמבקר עוזב בלי לקבל תשובה. כשלים בזמני שיא פוגעים בהמרה יותר ממה שנדמה: הולכים לאיבוד פניות, תשלומים והקלקות מפרסום.

עומס יתר על השרת לא דורש אקזוטיקה: די בכך שפרופיל השיא לא יתאים לחישוב המאגר, וכללי הניתוב יימצאו ב-.htaccess במקום ברמת ה-vhost. בהמשך נפרט כיצד סוגרים זאת — מבחירת מצב העיבוד וחישוב מאגר php-fpm ועד העברת כללים והגדרת חיבורים מתמשכים.

מה נותנת התאמה מחדש של שרת האינטרנט: השפעות מדידות

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

  • זמן תגובה תחת עומס. במקום עלייה פי כמה באותו מספר מבקרים במקביל, הוא נשמר בגבולות שהוגדרו: בקשות כבדות (ייצוא, חיפוש, אינטגרציות) מפסיקות לחסום בקשות קלות, ודף טיפוסי מגיב בזמן צפוי.
  • תפוקה. גדל מספר הבקשות שהשרת מעבד ביחידת זמן, בלי החלפת חומרה — הודות ליחס נכון בין תהליכי עבודה, הגשת סטטיקה בעקיפת האפליקציה ושמירת תשובות חוזרות במטמון.
  • שיעור הבקשות שנדחו. שגיאות הצפת תור וניתוקי חיבורים יורדות מאחוזים למקרים בודדים; כשל מתרחש כעת רק כשנגמרים המשאבים באמת, ולא בגלל מגבלות התצורה.
  • עמידות בזמני שיא. מאגר תהליכים נפרד לסקריפטים עם מרווח והגבלה על בקשות ארוכות מחליק את הזינוק: השרת לא "מתקפל" בפרק זמן קצר, אלא מעכל את המצטבר בהדרגה.
  • סוגי כשלים שנעלמים. דליפות זיכרון במודולים של השרת, פרגמנטציה והדרדרות אחרי יממה של עבודה, נעילות בגישה ל-.htaccess בכל בקשה — כל אלה מטופלים בתצורה, לא בהפעלה מחדש מתוזמנת.

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

התאמת Apache: השוואת מצבי עיבוד בקשות

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

תצורה מתי מתאימה מגבלות
prefork + מודול PHP מובנה תעבורה נמוכה, קוד מדור קודם עם מצב גלובלי, הרחבות שדורשות בידוד תהליכים מלא תהליך נפרד לכל חיבור: צריכת הזיכרון גדלה לינארית, חיים במקביל מעט בקשות, keep-alive ממושך לא משתלם
worker + php-fpm דרך proxy עומס יציב ומעורב: דינמיקה עוברת למאגר המטפל, סטטיקה — לשרת נימים חולקים מרחב כתובות: מודול בלי תמיכה ב-multithreading יפיל את התהליך
event + php-fpm דרך proxy הרבה בקשות קצרות וחיבורים מתמשכים פעילים, הגשת סטטיקה מאותו שרת דורש זמני פסק, חוצצים ושימוש חוזר בחיבורים בבלוק ה-proxy בקפידה
event + proxy לשירות אפליקציות נפרד Backend בשפה אחרת, הפרדת האפליקציה מנקודת הכניסה, פיזור על כמה שרתים נדרשים כללי בדיקת זמינות וניסיונות חוזרים, אחרת שגיאת Backend עוברת ללקוח כמו שהיא

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

סדר העבודות: מאיסוף מדדים ועד מסירת התצורה

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

  1. אודיט תצורה — מתעדים את המצב הנוכחי: מצב עיבוד בקשות, מגבלות תהליכים, מאגרי php-fpm, תוכן .htaccess, סט המודולים המופעלים. התוצאה — דוח שאיתו נשווה כל צעד הבא.
  2. איסוף מדדים תחת פרופיל אמיתי — כמה חיבורים במקביל מחזיק השרת, היכן מתחיל לגדול התור, באיזה עומס מופיעים כשלים.
  3. סכמה יעד — בוחרים מצב MPM ב-Apache לפי אופי הבקשות, מחשבים מספר תהליכי עבודה ומאגרים, מעבירים כללים מ-.htaccess לתצורת השרת.
  4. עריכות — התאמה לעומס מתבצעת בבלוק פרמטרים אחד לכל צעד. אחרי כל אחד בודקים מדדים: רואים את תרומת העריכה ונשארת נקודת החזרה.
  5. בדיקת עומס — חוזרים על פרופיל האודיט ומשווים זמן תגובה, שיעור כשלים, צריכת זיכרון. לא הגענו לערכי היעד — חוזרים לחישוב.
  6. מסירת תצורה — קבצים, סכמה והסברים לפרמטרים: מדוע נבחר מספר כזה של תהליכים ומה ישתנה אם להעלות או להוריד אותו.
  7. הדרכת הצוות — מראים כיצד לאסוף מדדים ולשנות פרמטרים בלעדינו, כדי שהתצורה תמשיך לחיות.

תצורות, סכמות ונהלים שנשארים אצלכם

שרת מוגדר כשלעצמו — תוצאה חלשה: השינויים חיים על מארח אחד, ואף אחד מלבד המחבר לא יודע למה הוגדרו דווקא המגבלות האלה. לכן אנחנו סוגרים את העבודה לא בגישת SSH, אלא בסט ארטיפקטים במאגר — אפשר לפרוס אותם על מכונה חדשה ולהעביר למהנדס אחר.

  • קובצי תצורה במערכת בקרת גרסאות. מצב MPM, מספר עובדים ותור בקשות, פרמטרי keepalive, מאגרי php-fpm, כללי .htaccess — עם היסטוריית עריכות וסקירה, ולא עריכות על שרת חי.
  • סכמת תשתית. נתיב הבקשה ממאזן העומסים ועד מאגרי האפליקציות, נקודות כשל והמקום שבו תיתקלו קודם כול כשהעומס יגדל.
  • מדיניות גישה. מה סגור ב-.htaccess, אילו תיקיות ומדורים פנימיים לא נמסרים החוצה, אילו הגבלות כתובות ומתודות חלות.
  • נהלי תחזוקה. סדר טעינה מחדש של התצורה בלי לנתק חיבורים פעילים, כיצד לתחזק צמתים בזה אחר זה ומה לבדוק אחרי פריסה.
  • פרופילי עומס. תוצאות הרצות במספר שונה של חיבורים במקביל והנקודה שבה מתחיל לגדול התור — הספק מתוכנן, ולא על סמך ניחושים.
  • מפת ניטור עם ספים. תור php-fpm, עובדים תפוסים, זמן תגובה, שיעור תשובות שגויות — והערכים שבהם התורן מתערב.
  • סדר החזרה. התצורה הקודמת נשמרה, נתיב החזרה מתואר, זמן השחזור ידוע.
  • ניתוח עם הצוות שלכם. מראים מה ומדוע הוגדר, כדי שעריכות ההמשך תעשו בעצמכם ולא תהיו תלויים בנו.

מקרה בוחן: איך הסרנו כשלים בזמני שיא והאצנו את התגובה

לפני: השיא הפך לתור

הפרויקט — חנות מקוונת עם שיאים חדים: תחילת מבצע ושעות הערב נתנו עלייה פי חמישה במבקרים במקביל. Apache עבד במצב prefork, שבו לכל חיבור מוקצה תהליך נפרד: בשיא התהליכים הגיעו למגבלה, הבקשות נעמדו בתור וחלקן נסגרו בשגיאה 502/504.

שיעור הכשלים הגיע עד 7% מהפניות, וזמן התגובה של הקטלוג התארך מ-1.2 ל-6–7 שניות — קונים עזבו לפני שהגיעו לתשלום.

מה שינו ומה נהיה על אותו פרופיל

עיבוד החיבורים הועבר למודל מבוסס אירועים, ו-PHP הוצא למאגרי php-fpm נפרדים, תוך חלוקה לפי אתרים ולפי סוגי בקשות: תרחישים כבדים הפסיקו לחסום את האחרים.

פירוק .htaccess בכל בקשה הושבת — הכללים הועברו לקונפיג הראשי, ומגבלות keep-alive, מספר תהליכי העבודה והתורים נבחרו לפי פרופיל שנמדד, ולא לפי התיעוד.

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

בדיקה זו הועברה לנהלים: כל שינוי בתצורה מאושר בהרצה על הפרופיל השמור, אחרת צוואר הבקבוק יחזור במבצע הבא.

במה שונה prefork מ-worker ומ-event תחת עומס?

המצבים נבדלים בכך מה בדיוק תופס משאב לכל חיבור: תהליך, נימה או מטפל אסינכרוני. Apache HTTP Server מקבע את המודל הזה בעת ההפעלה, ובחירה בו קובעת עד כמה בחדות תגדל צריכת הזיכרון בשיא וכיצד יתנהג השרת עם לקוחות איטיים.

prefork מרים תהליך נפרד לכל חיבור. זהו המודל היקר ביותר בזיכרון: במאות רבות של מבקרים במקביל התהליכים מוכפלים, והמכונה נתקלת במגבלת זיכרון לפני שהיא נתקלת במגבלת מעבד. מנגד, הבידוד מלא — בקשה כושלת לא פוגעת בשכנים, ומודולים בלי תמיכה בנימים ממשיכים לעבוד. לכן prefork נשאר בחירה לקוד ישן עם מטפל PHP מחובר, שאי אפשר להעביר ל-php-fpm.

worker מחזיק מאגר תהליכים, ובתוך כל אחד — מאגר נימים, וחיבור מטופל על ידי נימה. צריכת הזיכרון לכל חיבור יורדת פי כמה, והתפוקה עולה. הצד השני — מרחב כתובות משותף: גישה לא בטוחה למצב משותף מפילה את התהליך יחד עם כל החיבורים שלו, ולכל הרחבה מתווספת דרישת בטיחות נימים.

event עובד כמו worker, אבל החזקת חיבורים מתמשכים (keep-alive) מועברת לנימה מאזינה נפרדת. בזמן שלקוח קורא לאט את התשובה, הערוץ שלו לא תופס נימת עבודה — השרת מגיש אותו אסינכרונית.

בדיוק זה סוגר את התרחיש עם המון חיבורים חצי-פתוחים מלקוחות ניידים, שבו prefork אוכל את כל התהליכים עוד לפני שיא העומס.

בחירת המצב נקבעת לפי שלוש שאלות: איזה מטפל PHP משמש, האם יש הרחבות לא בטיחותיות לנימים, וכמה חיבורים מתמשכים מחזיק העומס. סטאק מודרני עם php-fpm עובר ל-event, קוד מיושן עם mod_php — ל-prefork. לאחר מכן מחשבים את מספר המטפלים, מגבלות החיבורים וזמני הפסק — על כך בהמשך.

ספקות טכניים שאנו מסירים לפני תחילת העבודות

התאמת שרת אינטרנט לעומס נעשית כמעט תמיד על פרויקט חי, שבו כבר פרוסות שכבות משלו: מאזן עומסים, מטמון, פאנל ניהול, קונטיינרים. לכן מתחילים לא מעריכת קונפיגים, אלא ממדידות — היכן בדיוק הולך לאיבוד זמן התגובה ואילו הגדרות בכלל משתתפות בעיבוד הבקשה. להלן שאלות טכניות שכדאי להסיר לפני תחילת העבודות.

האם יהיה צורך לעצור את השירות?

החלפת מצבי MPM ומאגרי php-fpm נעשית לסירוגין על צמתים מאחורי מאזן העומסים: בזמן שאחד נטען מחדש, התעבורה עוברת לאחרים, והמבקרים לא מבחינים בכך. אם יש צומת אחד, נתאם חלון זמן ונכין מראש חזרה לקונפיגים הקודמים — החזרה אורכת דקות.

כבר מוגדרים אצלנו מטמון ומאזן עומסים — לא תשברו אותם?

תחילה ממפים: מה פרוס, אילו פרמטרים נדרסים, והיכן עובר גבול האחריות של השירותים. עריכות מבוצעות בבידוד — ב-vhost נפרד ובמאגר תהליכים נפרד, בלי לגעת בלוגיקה של אחרים. הנחיות מ-.htaccess מועברות לקונפיג השרת, כדי לא לקרוא את הקובץ בכל בקשה.

ומה אם צוואר הבקבוק אינו בשרת האינטרנט?

מקרה נפוץ: הבקשה תלויה בפנייה למסד נתונים או בשירות חיצוני איטי, ושרת האינטרנט לא קשור לכך. לכן לפני עריכות בודקים את פיזור הזמן לפי שכבות, ורק אחר כך נוגעים ב-MPM וב-php-fpm. אם הצוואר במקום אחר — נאמר זאת ישירות ונציע משימה נפרדת במקום התאמה חסרת תועלת.

מה עם ליווי לאחר המסירה?

מוסרים קונפיגים תחת בקרת גרסאות, הוראות לפעולות טיפוסיות ורשימת פרמטרים שצריך לעקוב אחריהם כשהעומס גדל. בתקופה הראשונה לאחר ההפעלה נשארים בקשר: אם פרופיל התעבורה ישתנה והמדדים יסטו, נחזור למדידות ונתאים את המצבים.

נדון במשימה שלכם ונחזור עם הערכה ותוכנית

כדי שהשיחה תהיה ענייני, ולא "ספרו לי על Apache", די בכמה נתוני פתיחה. אנו בוחנים את התצורה הנוכחית, פרופיל התעבורה והלוגים — וחוזרים עם הערכת עבודה ותוכנית לפי לוחות זמנים.

  • קונפיגים: הקובץ הראשי, vhost-ים נכללים, מצב MPM הנבחר (prefork, worker, event) והמגבלות שלו על תהליכי צאצא וחיבורים.
  • php-fpm: מצב המאגר, max_children, מגבלות זיכרון, זמני פסק לעיבוד בקשות ואורך תור ההמתנה.
  • .htaccess: אילו הנחיות מבוצעות בכל בקשה והיכן AllowOverride מוגדר רחב מדי.
  • פרופיל עומס: בקשות שיא לשנייה, יחס בין דינמיקה לסטטיקה, גרף יומי, גודל תגובה ממוצע.
  • לוגים: access ו-error ליממה אופיינית, קטעים מ-slow log של php-fpm, שיעור 5xx, זמן תגובה, צריכת זיכרון.
  • סביבה: מאזן עומסים לפני השרתים, מספרם, מטמון ו-CDN, סכמת עמידות לתקלות.

בתשובה תקבלו הערכת היקף: היכן די בעריכת פרמטרים, והיכן נדרש מעבר ל-event MPM עם עיבוד דינמיקה דרך proxy_fcgi. לאחר מכן — סכמת עבודה עם סדר השינויים ונקודות חזרה, תוכנית לפי לוחות זמנים ורשימת בדיקות תחת עומס: התנהגות בשיא, הדרדרות בכשל של צומת, התאוששות לאחר הפעלה מחדש.

אם חלק מהנתונים חסר, נאסוף אותם במקום. תארו את המשימה — נחזור עם הערכה עניינית ותוכנית.