Optimizing LAMP 101, step through tutorial guide

LAMP definition by wikipedia:

LAMP is an acronym for a solution stack of open source software: Linux, Apache, MySQL and PHP, principal components to build a general purpose web server.

This article describes basics of benchmarking and configuration optimisation for fairly recent hardware. Most tips fall in the “common sense” category of senior sysadmins. The main objective of this article is to make junior sysadmins sensible to the requirement of reviewing configurations of whatever installed software.

This article steps through the basic process of optimizing each software of the LAMP stack:

  • benchmarking with wbox,
  • Linux optimisations,
  • Apache optimisations,
  • PHP optimisations,
  • MySQL optimisations.

The author recommends a different software stack when choosing is possible:

Benchmarking with wbox

Wbox is used in the next chapters of this articles for HTTP benchmarking.

What is wbox:

Wbox aims to help you having fun while testing HTTP related stuff. You can use it to perform many tasks, including the following.

* Benchmarking how much time it takes to generate content for your web application.
* Web server and web application stressing.
* Test if the HTTP compression is working and if it is actually serving pages faster.

Wbox is free software under the GPL version 2 license and was written in ANSI C (POSIX runtime required) by Salvatore ‘antirez’ Sanfilippo.

Example installation on FreeBSD:

jpic@natacha:~/src/wbox$ ls
AUTHORS     Makefile    anet.c      sds.h       wbsignal.h
COPYING     README      anet.h      wbox.c
Changelog   TODO        sds.c       wbsignal.c
jpic@natacha:~/src/wbox$ make
cc -c -O2 -fno-strict-aliasing -pipe -march=prescott -g  anet.c
cc -c -O2 -fno-strict-aliasing -pipe -march=prescott -g  sds.c
cc -c -O2 -fno-strict-aliasing -pipe -march=prescott -g  wbsignal.c
cc -c -O2 -fno-strict-aliasing -pipe -march=prescott -g  wbox.c
cc -o wbox -O2 -fno-strict-aliasing -pipe -march=prescott -g anet.o sds.o wbsignal.o wbox.o
jpic@natacha:~/src/wbox$ ./wbox 
Usage: wbox  [options ...]

options:

             — stop after  requests
compr                — send Accept-Encoding: gzip,deflate in request
showhdr              — show the HTTP reply header
dump                 — show the HTTP reply header + body
silent               — don't show status lines
head                 — use the HEAD method instead of GET
http10               — use HTTP/1.0 instead of HTTP/1.1
close                — close the connection after reading few bytes
host       — use  as Host: field in HTTP request
timesplit            — show transfer times for different data chunks
wait         — wait  seconds between requests. Default 1.
clients      — spawn  concurrent clients (via fork()).
referer         — Send the specified referer header.
cookie    — Set cookie name=val, can be used multiple times.
-h or --help         — show this help.
-v                   — show version.

SERVER MODE

Usage: wbox servermode webroot  [serverport  (def 8081)]

options:

maxclients   — Max concurrent clients in server mode (default 20).

EXAMPLES

wbox wikipedia.org                  (simplest, basic usage)
wbox wikipedia.org 3 compr wait 0   (three requests, compression, no delay)
wbox wikipedia.org 1 showhdr silent (just show the HTTP reply header)
wbox wikipedia.org timesplit        (show splitted time information)
wbox 1.2.3.4 host example.domain    (test a virtual domain at 1.2.3.4)
wbox servermode webroot /tmp/mydocuments  (Try it with http://127.0.0.1:8081)

More docs? there is a tutorial at http://hping.org/wbox

Linux

Optimising the Linux kernel is a tedious task. The objective is to reduce it’s footprint. Kernel binaries shipped with most distribution (Debian, Ubuntu, Red Hat …) have a large footprint because it should be able to “boot the world”. On the other hand, a server with a precise purpose does not need “the world” to boot its system.

It is better to start with a vanilla (unpatched) stable version of the kernel. The process is simple and tedious. The help screen of each single option should be read, and understood in most cases.

  • if the option is not needed then it can be disabled: [ ],
  • if the option is needed at boot time then it should be compiled in the kernel binary: <*>,
  • if the option is needed at run time then it can be compiled as module: [M],

Example options that should be compiled in:

  • disk access drivers (SATA, PATA, SCSI, RAID…), like “Intel ESB, ICH, PIIX3, PIIX4 PATA/SATA support” for an intel “mobo” (motherboard).
  • file system drivers for root, like “Ext3 journalling file system support” if the root partition is formated as ext3,

Example options that should be compiled as modules:

  • Network devices like “Intel(R) PRO/1000 PCI-Express Gigabit Ethernet support” for the ethernet card or “Dummy net driver support” for virtual systems,
  • Hardware sensors like “Intel Core (2) Duo/Solo temperature sensor”,

Example options that should not be compiled at all:

  • PCI Debugging and any verbose/debug option which bloat the kernel with strings,
  • Kernel .config support, for security,

Detailing the kernel compile process or each kernel option is out of the scope of this article. Gentoo Kernel configuration manual contains some basics.

Apache

Apache HTTPD default configuration also loads many modules which are not needed for server specific purpose. Again the objective is to reduce its footprint so that it serves whatever HTTP application and nothing more.

Footprint

For example, a default Gentoo GNU/Linux Apache 2 configuration loads many modules which are not needed in most cases:

LoadModule authn_anon_module modules/mod_authn_anon.so
LoadModule authn_dbd_module modules/mod_authn_dbd.so
LoadModule authn_dbm_module modules/mod_authn_dbm.so
LoadModule authn_default_module modules/mod_authn_default.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authz_dbm_module modules/mod_authz_dbm.so
LoadModule authz_default_module modules/mod_authz_default.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_owner_module modules/mod_authz_owner.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule autoindex_module modules/mod_autoindex.so

Apache 2 provides a configtest command which won’t pass if the configuration (like vhost configurations) declare a setting of a module which is not loaded. A quick and easy Apache 2 basic configuration process is:

  • comment out a LoadModule line in httpd.conf,
  • run the configuration tests,
  • uncomment the LoadModule line if the tests fail,
  • move on to the next module if the tests pass.

Running the configuration tests is possible with the -t switch of the Apache 2 binary. Note that the path to the httpd.conf must be specified like so:

/usr/sbin/apache2 -f /etc/apache2/httpd.conf -t

The tests pass if the output of this command is:

Syntax OK

Example failure, if the following is commented:

LoadModule log_config_module modules/mod_log_config.so

And if a command defined by this module is called somewhere else in the configuration, like a vhost file:

LogFormat "%h %l %u %t \"%r\" %>s %b" common

Then the configuration tests will fail claiming a “Syntax error”:

yourhost / # /usr/sbin/apache2 -f /etc/apache2/httpd.conf -t
Syntax error on line 9 of /etc/apache2/vhosts.d/01_yourvhost.conf:
Invalid command 'LogFormat', perhaps misspelled or defined by a module not included in the server configuration

Important modules to compress the output are mod_gzip mod_deflate

Like for Linux kernel, it is best to read and understand the documentation of each module.

Compression

Compression of static files is handled by Apache mod_deflate which is shipped by default.

The following configuration enabled compression for css, javascript and html (be it dynamically generated or not):

LoadModule deflate_module modules/mod_deflate.so
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/x-javascript

Before:

$ ./wbox http://trikapalanet.feeder.ww7.be/squelettes/screen_aqua.css compr 5
WBOX trikapalanet.feeder.ww7.be (94.23.20.157) port 80 [compr]
0. 200 OK    7684 bytes    326 ms
1. 200 OK    7684 bytes    325 ms
2. 200 OK    7684 bytes    324 ms
3. 200 OK    7684 bytes    326 ms
4. 200 OK    7684 bytes    325 ms
--- 5 replies received, time min/avg/max = 324/325.20/326 ---
$ # with PHP generated contents
$ ./wbox http://trikapalanet.feeder.ww7.be/ compr 5
WBOX trikapalanet.feeder.ww7.be (94.23.20.157) port 80 [compr]
0. 200 OK    17292 bytes    602 ms  
1. 200 OK    17292 bytes    573 ms  
2. 200 OK    17292 bytes    566 ms  
3. 200 OK    17292 bytes    568 ms  
4. 200 OK    17292 bytes    567 ms
--- 5 replies received, time min/avg/max = 566/575.20/602 ---

After:

$ ./wbox http://trikapalanet.feeder.ww7.be/squelettes/screen_aqua.css compr 5
WBOX trikapalanet.feeder.ww7.be (94.23.20.157) port 80 [compr]
0. 200 OK    2185 bytes    206 ms    compr
1. 200 OK    2185 bytes    205 ms    compr
2. 200 OK    2185 bytes    206 ms    compr
3. 200 OK    2185 bytes    206 ms    compr
4. 200 OK    2185 bytes    207 ms    compr
--- 5 replies received, time min/avg/max = 205/206.00/207 ---
$ # with php generated contents
$ ./wbox http://trikapalanet.feeder.ww7.be/ compr 5
WBOX trikapalanet.feeder.ww7.be (94.23.20.157) port 80 [compr]
0. 200 OK    5383 bytes    369 ms    compr
1. 200 OK    5383 bytes    354 ms    compr
2. 200 OK    5382 bytes    365 ms    compr
3. 200 OK    5384 bytes    437 ms    compr
4. 200 OK    5384 bytes    599 ms    compr
--- 5 replies received, time min/avg/max = 354/424.80/599 ---

PHP

PHP opcode cache

Wikipedia page about opcodes:

An opcode (operation code) is the portion of a machine language instruction that specifies the operation to be performed. Their specification and format are laid out in the instruction set architecture of the processor in question (which may be a general CPU or a more specialized processing unit like PHP).

The APC module for PHP is responsible for bytecode caching.

Installation procedure of the APC module depends of the operating system. On Choco GNU/Linux:

emerge -K pecl-apc

It is important to configure APC, the configuration file is in /etc/php/cgi-php5/ext-active/apc.ini by default on Gentoo GNU/Linux, or in /etc/php/apache2-php5/ext-active/apc.ini for Apache mod_php.

Example settings for a Sysadmin II virtual server with an application like Magento:

apc.enabled = 1
apc.shm_segments = 1
apc.shm_size = 128
apc.max_file_size = 32M
apc.stat=1

Output compression

There is no need to configure PHP level compression if Apache is already configured to use HTTP compression, which one can check with the compr switch of wbox. In that case it would just consume CPU for nothing.

Compression at PHP level is enabled by the following variables in PHP.ini, which are rarely set by default:

zlib.output_compression = 1
zlib.output_handler = On

One may believe that this kind of zlib PHP level HTTP compression is dangerous although it looks safe according to the manual:

zlib.output_compression boolean/integer

 Whether to transparently compress pages. If this option is set to "On" in php.ini or the Apache configuration, pages are compressed if the browser sends an "Accept-Encoding: gzip" or "deflate" header. "Content-Encoding: gzip" (respectively "deflate") and "Vary: Accept-Encoding" headers are added to the output. In runtime, it can be set only before sending any output.

It is recommended to enable HTTP compression at the webserver level which does more exception checks (such as Accept-Encoding). The http server run by ChocolatePistachio is configured to do that well. Users with their own IP and webserver should enable it at their webserver level.

Enabling HTTP compression for other dynamic content generator than PHP is described in the Lighttpd mod_compress documentation.

Misc

It is possible to optimize the PHP parser with the following options:

short_open_tag = Off
asp_tags = Off
zend.ze1_compatibility_mode = Off

Detailing each option of PHP is out of the scope of this article. Like with Linux and Apache, a good sysadmin reads each option of the configuration file and the associated documentation in order to understand them.

MySQL

The default configuration shipped with MySQL on Gentoo GNU/Linux is suitable for servers with 64M of RAM. This is cute but probably far under what a recent server can offer (and far under your expectations).

Default configuration:

key_buffer                  = 16M
max_allowed_packet          = 1M
table_cache                 = 64
sort_buffer_size            = 512K
net_buffer_length           = 8K
read_buffer_size            = 256K
read_rnd_buffer_size        = 512K
myisam_sort_buffer_size     = 8M
# the rest of the innodb config follows:
# don't eat too much memory, we're trying to be safe on 64Mb boxes
# you might want to bump this up a bit on boxes with more RAM
innodb_buffer_pool_size = 16M
# this is the default, increase it if you have lots of tables
innodb_additional_mem_pool_size = 2M
# you may wish to change this size to be more suitable for your system
# the max is there to avoid run-away growth on your machine
innodb_data_file_path = ibdata1:10M:autoextend:max:128M

Configuration example for a Sysadmin II virtual server with an application like Magento with around 5000 products:

key_buffer                  = 256M
max_allowed_packet          = 2M
table_cache                 = 64
sort_buffer_size            = 2M
net_buffer_length           = 8K
read_buffer_size            = 2M
read_rnd_buffer_size        = 2M
myisam_sort_buffer_size     = 64M 
innodb_buffer_pool_size     = 128M
innodb_data_file_path       = ibdata1:128M:autoextend:max:128M
innodb_additional_mem_pool_size = 64M

Also, note that the default fstab for virtual servers mounts a 16M tmpfs in the guest /tmp directory. The host system administrator should definitively change it. Setting it to 128M in the case of a Sysadmin II VPS is fair.

Detailing each option of MySQL is out of the scope of this article. Like with Linux, Apache and PHP: a good sysadmin reads each option of the configuration file and the associated documentation in order to understand them.

An excellent open source script helps for application specific optimizations, Mysqltuner:

» What can MySQLTuner do?

MySQLTuner is a script written in Perl that will assist you with your MySQL configuration and make recommendations for increased performance and stability. Within seconds, it will display statistics about your MySQL installation and the areas where it can be improved.

Credits

  • stbuehler from #lighttpd@irc.freenode.net about HTTPds,
  • Zurgutt about MySQL,
  • Michael Maclean about Apache,
  • Kore Nordmann about HTTP,
  • Derick Rethans about PHP,
  • William Waisse, for lending an apache server,

Errors? Comments?

Please post a comment or email the author directly any inaccuracy is found in this article.

Add post to: Delicious Reddit Slashdot Digg Technorati Google
(already: 1) Comment post

Comments

No comments for this post

Comment form for «Optimizing LAMP 101, step through tutorial guide»

Required. 30 chars of fewer.

Required.

captcha image Please, enter symbols, which you see on the image