תוכן עניינים

שפצר את המנורה שלך - Optimize Your LAMP stack

כתריאל טראום

katriel@penguin.org.il

31.03.2007
היסטוריית גירסאות
גירסא 1.0 03-04-2007 כתריאל טראום
גירסא ראשונה
גירסה 0.1 31-03-2007 כתריאל טראום
טיוטאת שלד

סיכום

מדריך זה ידבר על הרכיבים הנפוצים בהרבה מאתרי האינטרנט היום: Linux, Apache, MySQL & PHP וכיצד ניתן למטב אותם כדי לענות לעומסים גדולים יותר.

מבוא

רובנו, כאשר אנו מקימים אתר LAMP, עושים זאת בצורה של “שגר ושכח”: מתקינים, מקנפגים בצורה בסיסית כדי שהרכיבים “ידברו” ומתחילים לעבוד על האפליקציה\תוכן שלנו. מדריך זה בא להראות שלפני התוכן, ואחרי ההתקנה, שווה לעבוד עוד קצת, כדי להוציא את המקסימום מהאפליקציות והחומרה שיש לנו. במדריך זה אעבור רכיב רכיב, ואראה דרכים לשיפור הביצועים שלו, כמובן שה”שיפצורים” יהיו מוכוונים לשרת שהולך להיות שרת Web.

זכויות יוצרים ורשיון

כל הזכויות שמורות © 2007, כתריאל טראום, הרשות ניתנת להעתיק, לשנות ולהפיץ מדריך זה תחת התנאים של רשיון ה-GFDL

Linux הוא סימן מסחרי רשום של Linus Torvalds.

הסרת אחריות

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

משוב

תגובות, תלונות, הערות והארות לכתובת בראשית הדף

תכנון מראש

בעולם שכולו טוב, משיקים אתר חדש, ובתוך יום כבר היה לנו מליוני כניסות יחודיות. בעולם פחות מושלם, צריך להתחיל בקטן, ולקוות שנגדל יום אחד. תנאי חשוב לגידול קל של המערכת שלנו הוא תכנון טוב מראש.
כבר בשלב התיכנון כדאי לתכנן את רכיבי החומרה והתוכנה שיהיו קלים להרחבה בעתיד. מומלץ תמיד להשתמש ברכיבים טובים יותר (ניתן לקרוא גם “יקרים יותר”) שיוכלו לשאת ביותר עומס ממה שהמערכת מתוכננת מראש.

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

הרכיבים, מלמטה למעלה

Linux

IO Scheduler

החל מקרנל 2.6, נתן לכוונן כיצד מערכת ההפעלה תקרא ותכתוב מהדיסק. ישנן שיטות “קיבוץ” פעולות I/O שונות אשר נותנות ביצועים טובים במצבים שונים. השיטות הן:

כדי לקבוע את סוג המתזמן (Scheduler) יש להשתמש ב-sysfs ע”י כתיבת שם המתזמן; as, cfq, deadline או noop לתוך הקובץ:

/sys/block/<devicename>/queue/scheduler

עבור שרת Apache עמוס, ה-Anticipatory Scheduler אמור לספק שיפור ביצועים באתר אשר יש בו קריאה רבה מהדיסק.

מערכת קבצים

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

בנושא הזה אין “כלל אצבע”, עליכם להכיר את האתר והתוכן שאתם מתכוונים לשרת, ולהחליט על בסיס המידע הזה איזו מערכת קבצים תתאים לכם. עצתי היא בכל מערכת קבצים שלא תבחרו, השוו תוצאות של כלי בדיקת עומסים תחת מצבים קרובים ביותר לתוכן האתר שלכם. מומלץ להשתמש בתוכנות bonnie++ ו- iozone תוך דימוי קרוב ביותר לאתרכם מול כמה מערכות קבצים שונות.

Networking

צד אחר של מערכת ווב פעילה, הוא ה-Networking. גם כאן נוכל לבצע מספר שינויים בכדי לשפר את ה-Throughput של השרת שלנו. את הערכים הבאים אפשר להוסיף לקובץ sysctl.conf או דרך סקריפט אחר שמבצע את הכיוונים האלו:

fs.file-max=5049800
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_max_orphans=1000
sys.net.core.rmem_default=262144
sys.net.core.rmem_max=262144

Apache

MPM

MPM או Multi-Processing Modules הם מודולים שונים שמנהלים את אפאצ'י והתהליכים שלו (Processes) בדרכים שונות. 2 ה-MPM-ים העיקריים הם prefork ו-worker. ה-MPM ברירת המחדל ברוב המערכות היום הוא prefork, וכך גם בנויים לרוב המודולים הנלווים ל- Apache בהפצות לינוקס.

StartServers       8
MinSpareServers    2
MaxSpareServers   20
ServerLimit      256
MaxClients       256
MaxRequestsPerChild  4000
StartServers         2
MaxClients         150
MinSpareThreads     25
MaxSpareThreads     75 
ThreadsPerChild     25
MaxRequestsPerChild  0

השימוש ב-Prefork יתרום לרוב במערכות בעלות מעבד אחד או-2, והוא יותר עמיד בפני נפילות של תהליכים ומודולים בעיתיים. Worker MPM מתאים יותר למערכות מרובות מעבדים אשר יוכלו לנצל טוב יותר את האופי ה-Multithreaded שלו ואת ניצול הזכרון הנמוך יחסית. מצד שני, מכיוון שהוא Threaded ברגע שתהליך אחד עף, כל ה-threads שהוא מנהל ימותו גם כן.

MaxClients = <Apache RAM> / Process Size

לא רצוי לעלות מעבר לכמות ה-RAM שיש לשרת, כי אז יתחיל תהליך של swapping וביצועי המערכת יפגעו קשות.

כרגיל, אין כאן כלל אצבע, שניהם צריכים להיבחן לצרכים שלכם.

File access Method

כדי לאפשר אופציה זאת יש לאפשר את האופציה Sendfile:

EnableSendfile On
Configurations
<Directory /srv/www/html>
AllowOverride all
</Directory>

יחפש את הקובץ .htaccess בספריות:

במידה והקובץ .htaccess לא נחוץ, קריאה נוספת לדיסק בעבור כל ספריה היא מעמסה מיותר על מערכת ה-I\O.

Caching

Caching של מידע נעשה ברמות שונות בזמן מעבר התוכן מהשרת ללקוח: ב-RAM של השרת, Proxy שיושב בתווך, הדפדפן של המשתמש ועוד. שכבת Cache נוספת שעשויה לשפר את הביצועים של שרת Apache היא לבצע caching של תוכן שמשרת שרת ה-Apache על דיסקים מהירים או בזכרון. החל מ-Apache 2.0 ועם שיפורים ניכרים ב Apache 2.2 נוסף מודול שנקרא mod_cache אשר מסוגל לבצע caching של תוכן בזכרון המכונה או על דיסקים מהירים יותר ולשרת כך תוכן נפוץ בצורה מהירה יותר. מידע נוסף ניתן למצוא בקישור http://httpd.apache.org/docs/2.0/mod/mod_cache.html

Content Compression

ב- Apache 1.3 קראו למודול mod_gzip והוא הגיע כמודול חיצוני, החל מ- Apache 2.0 הוא מגיע כחלק מהחבילה ונקרא mod_deflate. מודול זה מאפשר לכווץ את המידע לפני שהוא נשלח ללקוח הקצה, כמובן במידה והלקוח תומך בתוכן מכווץ. אופציה זו מאפשרת זמני חיבור קצרים יותר מצד הלקוחות, וכך בעצם מאפשרת ל-Apache לשרת יותר לקוחות. בונוס נוסף הוא כמובן חיסכון ברוחב פס.

כדי לאפשר את mod_deflate, נשים את הקונפיגורציה הבאה:

 AddOutputFilterByType DEFLATE text/html text/plain text/xml
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
Header append Vary User-Agent env=!dont-vary

אופציה זו אומרת ל-Apache לכווץ תוכן טקסטואלי בלבד (אין טעם לכווץ תמונות שבד”כ באות גם ככה בפורמט מכווץ כל שהוא), ומוסיפה כמה תנאים כדי לפתור בעיות עם דפדפנים ישנים יותר. ניתן לשים את האופציה בקונפיגורציה הכללית או בתוך הגדרה של Virtual Server.

נושא אחד שיש לשים לב אליו הוא שכיווץ לא בא ללא מחיר. המחיר הוא שימוש גדול יותר ב- CPU לצורך כיווץ המידע. לכן גם פה, אין כלל אצבע, ויש לעקוב אחרי ההשפעה של הפעלת mod_deflate על ביצועי השרת.

MySQL

שרת ה-SQL בכל אתר תוכן דינאמי יכול להיות צוואר בקבוק במידה ולא נטפל בו כמו שצריך. העיצה הכי טובה שאני יכול לתת היא, במידה ואפשר, לשים את שרת ה-MySQL על מחשב משלו על מנת שלא יצטרך לחלוק ולהתחרות על משאבים עם שרותים אחרים על המחשב (בעיקר Apache שהוא צרכן זכרון ו-I/O לא קטן).

כמו שאר הרכיבים במדריך זה, ההשפעה הגדולה ביותר על ביצועי MySQL היא כמות הזיכרון אשר נקצה ל-Caches & Buffers .

ל-MySQL יש 2 מנועי מידע ראשיים ונפוצים: MyISAM ו- InnoDB. לכל אחד התנהגות שונה וחוזקים שונים. להשוואה קצרה בין ה”מנועים” עברו לקישור הבא: http://dev.mysql.com/tech-resources/articles/storage-engine/part_3.html

MyISAM

מנוע זריז, מתאים בעיקר לאתרים שצריכים לבצע כמות גדולה של “SELECT” ולאו דווקא לעדכן הרבה את תוכן הטבלה. MyISAM משתמש ב-Cache של מערכת ההפעלה כדי לבצע Table Caching, לכן רוב הכיוונונים שיש לעשות לו נוגעים ל Query Caching ו- Index Caching. 2 המשתנים שמשפיעים על הנ”ל הם “key_buffer_size” אשר שולט על גודל ה Index Cache ו-“query_cache_size\query_cache_limit” אשר שולטים על ה Query Caching buffer.

שילוב של הערכים ב-/etc/my.cnf יראה כך:

[mysqld]
key_buffer_size = 128M
query_cache_size = 32M
query_cache_limit = 2M

כדי לעקוב אחרי השפעת הפרמטרים נשתמש בפקודה SHOW STATUS בתוך mysql prompt:

mysql> SHOW STATUS LIKE 'key_read%';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Key_read_requests | 837   |
| Key_reads         | 103   |
+-------------------+-------+
2 rows in set (0.00 sec)

הערך key_read_requests הוא כמות הקריאות ל Table Indexes שהתבצעה מאז עליית המערכת. הערך key_reads הוא כמות השאילתות שבוצעו וניגשו לדיסק (בניגוד ל-Cache) כדי לשלוף את הנתונים. ככל שנעלה את ערך ה-cache, אמור להשתפר היחס של קריאות מה-RAM מול קריאות מהדיסק.

mysql> SHOW STATUS LIKE 'qcache%';
+-------------------------+----------+
| Variable_name           | Value    |
+-------------------------+----------+
| Qcache_free_blocks      | 50       |
| Qcache_free_memory      | 32106552 |
| Qcache_hits             | 8854     |
| Qcache_inserts          | 1816     |
| Qcache_lowmem_prunes    | 0        |
| Qcache_not_cached       | 65       |
| Qcache_queries_in_cache | 631      |
| Qcache_total_blocks     | 1426     |
+-------------------------+----------+
8 rows in set (0.00 sec)

הערך שעליו יש להסתכל עבור אופטימיזציה עתידית הוא Qcache_lowmem_prunes, אשר אומר מתי היה צריך MySQL למחוק שאילתות מה-cache מכיוון שנגמר לו המקום והיה צריך לפנות שאילתות ישנות לטובת חדשות. עם הערך הנ”ל עולה במהירות, סימן שלא הוקצה מספיק זכרון וסביר שנצטרך להגדיל את הערך query_cache_size. מידע נוסף על הנושא ניתן למצוא בקישור http://www.databasejournal.com/features/mysql/article.php/3110171

InnoDB

בניגוד ל-MyISAM, המנוע InnoDB עושה Caching לא רק לאינדקסים אלא גם למידע מתוך טבלאות, לכן מומלץ להקצות כמות גדולה יותר של RAM ל-MySQL. הערך ששולט על כמות ה-RAM נקרא innodb_buffer_pool_size. בשרת יעודי ל-MySQL מומלץ להקצות עד 70-80 אחוז מה-RAM של המערכת:

[mysqld]
innodb_buffer_pool_size = 256M

כדי לעקוב אחרי השימוש של המנוע ב-RAM נשתמש בפקודה:

SHOW INNODB STATUS\G
ערכים נוספים

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

PHP

opcode cache

בכל ריצה של סקריפט PHP, התוכן של הסקריפט מפוענח (Interpeted) ומקומפל ל opcodes, פקודות ברמה נמוכה.
ברגע שהסקריפט הודר (Compiled) פעם אחת, במידה והוא לא שונה, אין טעם לקמפל אותו שוב. Opcode cachers תופסים את אותו קוד מקומפל ומריצים אותו במקום לקמפל שוב את הסקריפט. ישנם כמה פתרונות, מסחריים וחופשיים אשר מבצעים את העבודה. לא נרחיב על הנושא עוד במדריך זה. להלן רשימה חלקית של PHP Opcode cachers:

מידע נוסף

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

תורמים למדריך \ תודות