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

כתריאל טראום
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 שונות אשר נותנות ביצועים טובים במצבים שונים. השיטות הן:

  • Anticipatory - מערכת התזמון שנטענת כברירת מחדל. המתזמן מספק מיזוג בקשות, ומחכה מעט אחרי פעולת קריאה מהדיסק אם הוא חושב שהמשתמש עומד לבקש עוד מידע. שיטה זאת מספקת תזוזת ראש דיסק ושיפור בביצועי הקריאה.
  • Complete Fair Queueing - מתזמן בקשות זה מנסה להקציב לכל מבקש זמן I/O שווה בפרק זמן נתון. הוא טוב בעיקר בשביל מערכות מרובות משתמשים
  • Deadline - מערכת תיזמון זו אוכפת זמן קצוב לכל קריאה כדי למנוע Resource Starvation. מתזמן זה הוא העדיף למערכות Database שבהן מתבצעת קריאה רבה מהדיסק.
  • NOOP - לא מבצע שום תזמון מיוחד של בקשות, רק קיבוץ של מספר בקשות בפעם.

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

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

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

מערכת קבצים

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

  • ext3 - מערכת All Around בעלת Journaling, התנהגות טובה ברוב המקרים, מושתתת על מערכת הקבצים הותיקה ext2 ולכן יציבה מאוד.
  • XFS - מערכת קבצים אשר “מתמחית” בניהול קבצים ונפחים גדולים. חסרונה העיקרי הוא בטיפול בקבצים רבים, אז היא מתחילה לאבד ממהירותה.
  • ReiserFS - מערכת קבצים אשר מטפלת בצורה טובה במספר רב של קבצים קטנים. כלל אצבע הוא שרוב הקבצים לא יהיו גדולים מ-4K.

בנושא הזה אין “כלל אצבע”, עליכם להכיר את האתר והתוכן שאתם מתכוונים לשרת, ולהחליט על בסיס המידע הזה איזו מערכת קבצים תתאים לכם. עצתי היא בכל מערכת קבצים שלא תבחרו, השוו תוצאות של כלי בדיקת עומסים תחת מצבים קרובים ביותר לתוכן האתר שלכם. מומלץ להשתמש בתוכנות 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
  • הערך fs-max יגדיל את מספר הקבצים הפתוחים שיכולים להיות במערכת. כאשר השרת עמוס, כל חיבור רשת, כל קובץ שנשלח למשתמש וכל חיבור ל-DB נחשבים כקובץ פתוח. כדי למנוע מצב של Starvation, כדאי להגדיל את המספר.
  • tcp_keepalive_time - פרק זמן ברירת המחדל לשמור חיבור TCP פתוח הוא שעתיים. בכך שאנו מורידים אותו ל-5 דקות, אנחנו גורמים לחיבורים מתים או לא פעילים להיסגר מהר יותר, וכך לשחרר את המשאבים שבשימוש מהר יותר.
  • tcp_max_orphans - ערך זה דואג לא להשאיר יותר מ-1000 פרוססים שמחכים לסגירת ה- TCP Connection, ויתחיל לסגור את החיבורים שפתוחים זמן רב אחרי שיעבור את רף ה-1000 חיבורים פעילים.
  • שני הערכים האחרונים מגדילים את ה-Buffer של כרטיס הרשת ומאפשרים לו לקבל ולשמור ב-queue יותר תעבורה, וכך לטפל ביותר קריאות לפני שהמערכת מתחילה לדחות חיבורים נכנסים.

Apache

MPM

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

  • prefork - מודול זה מריץ מראש מספר מסויים של תהליכי Apache, כאשר כל תהליך כזה יכול לשרת חיבור אחד בכל זמן. Apache מעלה גם מספר תהליכים שמוגדרים כ-Spare מעבר ל”צריכת החיבורים” הנוכחית כדי שחיבורים נכנסים חדשים לא יצטרכו לחכות ש-Apache יריץ עוד תהליך. ב-MPM זה תהליך אב אחד אחראי להריץ מספר מסויים של תהליכים ולשמור על רמת Spares כפי שהוגדר בקונפיגורציה. האופציות ששולטות ב-MPM זה הן StartServers, MinSpareServers, MaxSpareServer ו- MaxClients:
StartServers       8
MinSpareServers    2
MaxSpareServers   20
ServerLimit      256
MaxClients       256
MaxRequestsPerChild  4000
  • StartServers - כמות התהליכים של Apache שיש להריץ בזמן עליית המערכת.
  • MinSpareServers - כמות התהליכים שיש לנסות לשמור בצורה מינימאלית כדי לספק מענה לקפיצות בכמות הדרישות.
  • MaxSpareServers - הכמות המקסימאלית של תהליכים אשר מוגדרים כ-Spare אשר יכולים להיות במערכת.
  • MaxClients -הכמות המקסימאלית של תהליכי Apache שניתן להריץ במערכת. Apache יוסיף תהליכים מעבר ל-StartServers לפי דרישה, ככל שהעומס יעלה, יריץ Apache יותר תהליכים.
  • MaxRequestsPerChild - ערך זה מגדיר את כמות הבקשות שתהליך בודד אחד יענה להן לפני שהתהליך יהרג. אופציה זו באה למנוע מצב של זליגת זכרון. היא רלוונטת בעיקר שאנו מריצים אתר בעל תוכן דינאמי.
  • worker - בדומה ל-Prefork, גם worker מריץ בעת העליה מספר מסויים של תהליכים, אך כל תהליך הוא בעצם Multi-Threaded. דבר זה מאפשר לטפל ביותר בקשות, עם פחות Processes וכך לחסוך במשאבים. הבעיה הגדולה איתו היא שלא תמיד הספריות והאפליקציות ש-Apache עובד איתן הן “Thread Safe” ולכן לא ניתן לעבוד איתו תמיד. מודול זה נשלט ע”י האופציות הבאות:
StartServers         2
MaxClients         150
MinSpareThreads     25
MaxSpareThreads     75 
ThreadsPerChild     25
MaxRequestsPerChild  0
  • StartServers - כמות התהליכים (לא ה Threads) להריץ.
  • MaxClients - כמות ה-Threads להריץ סה”כ במערכת.
  • MinSpareThreads - הכמות המינימאלית של Spare threads שיש להחזיק במערכת כדי לענות על קפיצות ברמת הפניות.
  • MaxSpareThreds - כמות המקסימאלית של Spare threads.
  • ThreadsPerChild - כמות ה-Threads שכל תהליך (Process) מריץ

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

  • הערכות גודל - בהערכה גסה, תוכן סטטי אומר שכל תהליך (Process) של Apache יצרוך כ-2 עד 3 מגה, ואילו תהליך שמריץ תוכן דינאמי (PHP\Perl) יצרוך עד כ-20M. כמובן שהערכים ישתנו מאתר לאתר ומשרת לשרת, זהו ערך שצריך לעקוב אחריו כדי לתכנן את כמות התהליכים\נימים(Threads) המקסימאלית שמערכת תוכל לנהל. כמות התהליכים המקסימאלית צריכה להיות בערך:
MaxClients = <Apache RAM> / Process Size

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

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

File access Method
  • sendfile()- כאשר מדובר בתוכן סטאטי בעיקרו, מהירות העברת המידע מהדיסק לרשת היא נושא קריטי כדי להשגת ביצועים טובים. Apache מסוגל להשתמש בקריאת מערכת (System call) אשר נקראת “sendfile()”. קריאה זו חוסכת את העבודה של פתיחת הקובץ, הקצאת חוצץ (Buffer) לקריאתו ושליחתו למשתמש. קריאה זו גורמת לקרנל לשלוח את הקובץ ישירת, ללא תיווך וקריאה של תוכן הקובץ. ישנם כמה מצבים שלא כדאי להשתמש ב-sendfile , וכאשר השימוש עשוי אף לפגוע בביצועים:
    • כאשר הקבצים הנשלחים למשתמש נמצאים מעל Networked Filesystem כמו NFS או smb/cifs.
    • ישנן מערכות הפעלה אשר היישום של sendfile הוא “שבור” משהו, ולא מומלץ להשתמש בו (לא ממש יישים לגבי לינוקס)
    • ישנן לפעמים בעיות עם שילוב של sendfile ושימוש ב- IPv6.

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

EnableSendfile On
  • mmap - כאשר Apache כן צריך להשתמש בתוכן של קובץ (למשל דרך mod_include), מיפוי הקובץ לזכרון ייתן גישה יותר מהירה למידע ויאפשר עיבוד מהיר יותר של התוכן. גם כאן, ישנם מצבים שלא כדאי להשתמש באופציה זו:
    • כאשר התוכן הוא מעל NFS/SMB, אפאצ'י יקרוס אם קובץ שפתוח ב mmap() ימחק או ישונה בזמן שהוא פתוח ע”י Apache.
    • ישנן מערכות SMP ששימוש ב mmap יאט את המערכת.
Configurations
  • AllowOverride - במידה ו AllowOverride לא יהיה None, אפאצ'י יחפש בכל ספריה עד ספריית היעד את הקובץ .htaccess, לדוגמא:
<Directory /srv/www/html>
AllowOverride all
</Directory>

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

  • /srv/.htaccess
  • /srv/www/.htaccess
  • /srv/www/html/.htaccess

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

  • FollowSymlinks/SymLinksIfOwnerMatch. - האופציה FollowSymlinks תעקוב אחרי symbolic links בתוך ספריה, ואילו האופציה FollowSymlinksIfOwnerMatch תעקורב אחרי symbolic links רק אם תיקיית היעד היא בבעלות אותו הקובץ כמו הלינק אליה. בדיקה זו מצריכה קריאות מערכת נוספות כדי “לפענח” למי שייכת ספריית היעד. השתמשו ב FollowSymlinks בכל מקום, אך הפעילו את FollowSymlinksIfOwnerMatch רק בספריות שהכרחי לעשות כך.
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.

  • key_buffer_size - שולט על גודל ה Index buffer. בכל שאילתא אשר מסתמכת על index מסויים בטבלה, מתבצע שימוש ב index של אותה טבלה. במידה וה-index לא ב-cache כרגע, צריכה להתבצע פעולה “יקרה” של גישה לדיסק. ככל שנגדיל את ה key_buffer_size, כך נבצע פחות פעולות קריאה לדיסק, וכמובן, נשפר ביצועים. ערך ברירת המחדל הוא 88M, כאשר מומלץ להגדיל את הערך (במידה והשרת הוא שרת MySQL יעודי) עד לרבע (1/4) מה- RAM של המערכת.
  • query_cache_size\query_cache_limit - ערכים אלו שולטים על גודל ה-buffer שיוקצה על מנת לבצע caching לשאילתות שנשלחות לשרת בצורה חוזרת. query_cache_size שולט על גודל ה-buffer והערך השני query_cache_limit שולט על הגודל המקסימאלי של שאילתא שתיכנס ל-cache. גם כאן, יש להיזהר בכמות ה-RAM שמקצים, מכיוון שיותר מדי זכרון יעלה גם את הכמות הזמן שהשרת צריך לנהל את ה-cache ולנקות אותו משאילתות שאינן רלוונטיות יותר.

שילוב של הערכים ב-/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_free_memory - המקום הפנוי שנשאר ב- query cache.
  • qcache_inserts - מספר השאילתות שהיו במערכת.
  • qcache_not_cached - מספר השאילתות שלא נכנסו ל-cache.
  • Qcache_queries_in_cache - מספר השאילתות שנמצאות כרגע ב-cache.
  • Qcache_hits הוא מספר הפעמים שהמערכת השתמשה בשאילתות מתוך ה-cache.

הערך שעליו יש להסתכל עבור אופטימיזציה עתידית הוא 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
ערכים נוספים

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

  • Open Tables
  • Sort\Join Buffers

PHP

opcode cache

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

מידע נוסף

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

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

  • יריב גרף
מדריכים/שפצר_את_המנורה_שלך_optimize_your_lamp_stack.txt · שונה לאחרונה ב: 2008/06/19 18:34 (עריכה חיצונית)
chimeric.de = chi`s home Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0