10

I have a dedicated server with apache/php on ubuntu serving my Wordpress blog with about 10K+ pageviews a day. I have W3TC plug in installed with APC.

But every now and then server stop responding or goes dead slow and i have to restart apache to get it back.

Heres my config what am i doing wrong?

ServerRoot "/etc/apache2"
LockFile /var/lock/apache2/accept.lock
PidFile ${APACHE_PID_FILE}
TimeOut 40
KeepAlive on
MaxKeepAliveRequests 200
KeepAliveTimeout 2
<IfModule mpm_prefork_module>
  StartServers 5
  MinSpareServers 5
  MaxSpareServers 8
  ServerLimit        80
  MaxClients         80
  MaxRequestsPerChild 1000
</IfModule>
<IfModule mpm_worker_module>
  StartServers       3
  MinSpareServers    3
  MaxSpareServers    3
  ServerLimit        80
  MaxClients         80
  MaxRequestsPerChild  1000
</IfModule>
<IfModule mpm_event_module>
  StartServers       3
  MinSpareServers    3
  MaxSpareServers    3
  ServerLimit        80
  MaxClients         80
  MaxRequestsPerChild  1000
</IfModule>
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}
AccessFileName .htaccess
<Files ~ "^\.ht">
  Order allow,deny
  Deny from all
  Satisfy all
</Files>
DefaultType text/plain
HostnameLookups Off
ErrorLog /var/log/apache2/error.log
LogLevel error
Include /etc/apache2/mods-enabled/*.load
Include /etc/apache2/mods-enabled/*.conf
Include /etc/apache2/httpd.conf
Include /etc/apache2/ports.conf
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
CustomLog /var/log/apache2/other_vhosts_access.log vhost_combined
Include /etc/apache2/conf.d/
Include /etc/apache2/sites-enabled/
Kuf
  • 449
  • 2
  • 8
  • 24
Broke artist
  • 101
  • 1
  • 1
  • 3

5 Answers5

23

My WordPress Performance and Caching Stack

This is a great WordPress performance stack for a low to mid range single server or VPS. I am classifying mid range as single core with around 1G of memory and fairly fast drives.

On your box this would be capable of serving over 10K page views per hour

Server Stack

  • Linux - Either Debian Lenny or Ubuntu
  • Nginx - Configured as reverse proxy static file cache
  • Apache - Apache will handle the PHP offloaded by Nginx on an alternate port
  • MySql - Required by WP, make sure your running the latest stable version
  • PHP - Latest stable version of 5.2 or 5.3 branch

PHP Cache

  • APC - Configure it with mmap memory and shm size of at least 128M

WordPress Performance Plugin Stack

  • Nginx proxy cache integrator
  • W3 Total Cache - Set page cache to disk enhanced, minify to disk, and object and db to APC.
  • Self Hosted CDN - Create 4 cname aliases that point to domain on the server set up just to serve static file

With W3 Total Cache we are using disk for page cache and minify because Nginx will be serving our static files very fast.

How to configure Nginx to serve static files and pass PHP to Apache

The problem with using Apache alone is that it opens up a connection and hits php on every request even for static files. This wastes connections because Apache will keep them open and when you have lots of traffic your connections will be bogged down even if they are not being used.

By default Apache listens for requests on port 80 which is the default web port. First we are going to make changes to our Apache conf and virtual hosts files to listen on port 8080.

Apache Config

httpd.conf

set KeepAlive to off

ports.conf

NameVirtualHost *:8080
Listen 8080

Per Site Virtual Host

<VirtualHost 127.0.0.1:8080>
     ServerAdmin info@yoursite.com
     ServerName yoursite.com
     ServerAlias www.yoursite.com
     DocumentRoot /srv/www/yoursite.com/public_html/
     ErrorLog /srv/www/yoursite.com/logs/error.log
     CustomLog /srv/www/yoursite.com/logs/access.log combined
</VirtualHost>

You should also install mod_rpaf so your logs will contain the real ip addresses of your visitors. If not your logs will have 127.0.0.1 as the originating ip address.

Nginx Config

On Debian you can use the repositories to install but they only contain version 0.6.33. To install a later version you have to add the lenny backports packages

$ nano /etc/apt/sources.list

Add this line to the file deb http://www.backports.org/debian lenny-backports main

$ nano /etc/apt/preferences

Add the following to the file:

Package: nginx
Pin: release a=lenny-backports 
Pin-Priority: 999

Issue the following commands to import the key from backports.org to verify packages and update your system's package database:

$ wget -O - http://backports.org/debian/archive.key | apt-key add -
$ apt-get update

Now install with apt-get

apt-get install nginx

This is much easier than compiling from source.

Nginx conf and server files config

nginx.conf

user www-data;
worker_processes  4;

error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    access_log  /var/log/nginx/access.log;
    client_body_temp_path /var/lib/nginx/body 1 2;
    gzip_buffers 32 8k;
    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;
    tcp_nodelay        on;

    gzip  on;

  gzip_comp_level   6;
  gzip_http_version 1.0;
  gzip_min_length   0;
  gzip_types        text/html text/css image/x-icon
        application/x-javascript application/javascript text/javascript application/atom+xml application/xml ;



    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Now you will need to set up your Nginx virtual hosting. I like to use the sites-enabled method with each v host sym linked to a file in the sites-available directory.

$ mkdir /etc/nginx/sites-available  
$ mkdir /etc/nginx/sites-enabled
$ touch /etc/nginx/sites-available/yourservername.conf
$ touch /etc/nginx/sites-available/default.conf
$ ln -s  /etc/nginx/sites-available /etc/nginx/sites-enabled
$ nano /etc/nginx/sites-enabled/default.conf

default.conf

Note:

The static cache settings in the following files will only work if the Nginx proxy cache integrator plugin is enabled.

proxy_cache_path  /var/lib/nginx/cache  levels=1:2   keys_zone=staticfilecache:180m  max_size=500m;
proxy_temp_path /var/lib/nginx/proxy;
proxy_connect_timeout 30;
proxy_read_timeout 120;
proxy_send_timeout 120;

#IMPORTANT - this sets the basic cache key that's used in the static file cache.
proxy_cache_key "$scheme://$host$request_uri";

upstream wordpressapache {
        #The upstream apache server. You can have many of these and weight them accordingly,
        #allowing nginx to function as a caching load balancer 
        server 127.0.0.1:8080 weight=1 fail_timeout=120s;
}

Per WordPress site conf (For multi site you will only need one vhost)

server {
        #Only cache 200 responses, and for a default of 20 minutes.
        proxy_cache_valid 200 20m;

        #Listen to your public IP
        listen 80;

        #Probably not needed, as the proxy will pass back the host in "proxy_set_header"
        server_name www.yoursite.com yoursite.com;
        access_log /var/log/nginx/yoursite.proxied.log;  

        # "combined" matches apache's concept of "combined". Neat.
        access_log  /var/log/apache2/nginx-access.log combined;
        # Set the real IP.
        proxy_set_header X-Real-IP  $remote_addr;

        # Set the hostname
        proxy_set_header Host $host;

        #Set the forwarded-for header.
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        location / {
                        # If logged in, don't cache.
                        if ($http_cookie ~* "comment_author_|wordpress_(?!test_cookie)|wp-postpass_" ) {
                                set $do_not_cache 1;
                        }
                        proxy_cache_key "$scheme://$host$request_uri $do_not_cache";
                        proxy_cache staticfilecache;
                        proxy_pass http://wordpressapache;
        }

        location ~* wp\-.*\.php|wp\-admin {
                        # Don't static file cache admin-looking things.
                        proxy_pass http://wordpressapache;
        }

        location ~* \.(jpg|png|gif|jpeg|css|js|mp3|wav|swf|mov|doc|pdf|xls|ppt|docx|pptx|xlsx)$ {
                        # Cache static-looking files for 120 minutes, setting a 10 day expiry time in the HTTP header,
                        # whether logged in or not (may be too heavy-handed).
                        proxy_cache_valid 200 120m;
                        expires 864000;
                        proxy_pass http://wordpressapache;
                        proxy_cache staticfilecache;
        }

        location ~* \/[^\/]+\/(feed|\.xml)\/? {
 # Cache RSS looking feeds for 45 minutes unless logged in.
                        if ($http_cookie ~* "comment_author_|wordpress_(?!test_cookie)|wp-postpass_" ) {
                                set $do_not_cache 1;
                        }
                        proxy_cache_key "$scheme://$host$request_uri $do_not_cache";
                        proxy_cache_valid 200 45m;
                        proxy_cache staticfilecache;
                        proxy_pass http://wordpressapache;
        }

        location = /50x.html {
                root   /var/www/nginx-default;
        }

        # No access to .htaccess files.
        location ~ /\.ht {
                deny  all;
        }

        }

Self Hosted CDN conf

For your self hosted CDN conf you only need to set it up to serve static files without the proxy pass

server {

        proxy_cache_valid 200 20m;
        listen 80;
        server_name yourcdndomain.com;
        access_log   /srv/www/yourcdndomain.com/logs/access.log;
        root   /srv/www/yourcdndomain.com/public_html/;

 proxy_set_header X-Real-IP  $remote_addr;

      location ~* \.(jpg|png|gif|jpeg|css|js|mp3|wav|swf|mov|doc|pdf|xls|ppt|docx|pptx|xlsx)$ {
                                # Cache static-looking files for 120 minutes, setting a 10 day expiry time in the HTTP header,
                                # whether logged in or not (may be too heavy-handed).

                                proxy_cache_valid 200 120m;
                        expires 7776000;
                        proxy_cache staticfilecache;
                }

location = /50x.html {
                root   /var/www/nginx-default;
        }

 # No access to .htaccess files.
        location ~ /\.ht {
          deny  all;
        }

    }

Now start the servers

$ /etc/init.d/apache2 restart  
$/etc/init.d/nginx start

The Benchmark Results

On Apache Bench this setup can theoretically serve 1833.56 requests per second

$ ab -n 1000 -c 20 http://yoursite.com/

alt text

Chris_O
  • 737
  • 4
  • 15
  • You mention having nginx handle static files and apache handle php files, but in the static files block you have "proxy_pass http://wordpressapache;". Doesn't that mean that apache is handling the static files? – srchulo May 03 '16 at 06:21
0

That looks like a standard Apache config although it seems some of it has been stripped out due to it looking like HTML.

You need to investigate what's happening when your server responds slowly. You don't say the specs of your server but you mention its dedicated and 10k/day should be easily handled.

  • What does top show?
  • Where's the bottleneck? CPU, Memory, I/O Wait ?
  • How many Apache processes are running?
  • How many connections shown in netstat ?

Guessing, CPU is probably the bottleneck caused by PHP. Using APC and a WP caching plugin are good methods to alleviate this, which you've already done. You could also try the "MPM" process model of Apache instead of "Prefork". Make sure you have enough memory allocated to APC so that it can cache your PHP scripts and not 'miss'.

It could also be MySQL - see if thats hogging the CPU or disk.

Consider turning off mod_deflate if you have it enabled - it does provide benefit to load times, but can increase CPU load. Might be worth trying.

Use a tool like 'siege' or 'ab' to benchmark your server and figure out at which point your server slows.

Here's some of my bookmarks from webserver performance tuning: http://articles.slicehost.com/2010/5/19/configuring-the-apache-mpm-on-ubuntu

http://www.thebuzzmedia.com/increase-wordpress-performance-on-apache-with-worker-mpm-php-and-mod_fcgid/

http://www.devside.net/articles/apache-performance-tuning

http://www.brandonturner.net/blog/2009/07/fastcgi_with_php_opcode_cache/

But my original advice remains the same - find out what the bottleneck is first! Otherwise you're blindly trying to improve performance (and sure, improving performance is always good) but without knowing which area to focus your attention.

Rafiq Maniar
  • 1,120
  • 9
  • 15
0

Also enable the server-status module and visit that to find out what's happening.

You might be swapping. Have you checked out vmstat while this is happening? 2GB of RAM for 80 MaxClients is only 25 MB for each (assuming the box isn't doing anything else.) Your MaxClients might be too high. The solution for this is obvious: add more RAM or lower MaxClients. If the command line is slow to respond when you restart apache, that's one indication of this situation.

It's also possible that you're spoon feeding some mobile clients (or other clients on slow connections) with 'large' files thereby consuming all of your available apache slots. Maybe you have too few MaxClients. Checking out the server-status will tell you what each of those clients are doing at that time. One solution for this situation is to increase MaxClients (but that might also turn into the situation above.) A better solution for this is to install an HTTP accelerator in front of apache (one free option is perlbal.) If your command line is normal speed when you restart apache, that's one indication of this situation.

toppledwagon
  • 4,215
  • 24
  • 15
0

Using mod_status is the way to see what is going on inside the multiple Apache instances but please note that it will really impair performance. It seems to eat memory and in one case I was not able to diagnose if it was the cause for single process lockups in a reverse-proxy-only setting where nothing was served directly. These of course are reported by users as "it takes forever to load the page". They don't even understand the difference between "it would have been 10 more seconds to wait" and "it won't ever finish" as they usually hit Stop in their browser after some (<10) seconds.

Also check if you are configuring the correct place (easy to see using mod_status since you see the amount of instances/processes). The stock config at least under ubuntu has ifdef'ed sections per MPM mode and it is easy to edit worker mode when you are running prefork (as suggested by conventional wisdom out of a fuzzy feeling that PHP is not threadsafe).

Oh and most of all: Run atop as root and watch for maxed out ressources. Memory, disc, CPU - you will see.

One more: The idea to deactivate mod_deflate may be good although your setting is not prone to the error of incorrect Content-Length information causing the browser to wait for data "forever" giving you reports of "dead slow" to "not responding".

BTW: 10K delivered pages per day plus media files on this machine should only be a problem if they all come visit in one hour.

Paul
  • 1,890
  • 3
  • 18
  • 24
0

Some advices, especially if you host a lot of media files:

  • Move your medias to a dedicated Apache (or better: nginx) instance. No PHP, no modules, only a bare http server which will deliver medias as fast as possible.
  • Cache, cache, cache! Use the super cache plugin on wordpress. It helps a lot.
  • Check your apache configuration on headers. Verify that images & other "stable" medias have a Expiry time set to a distant date & that your apache returns an HTTP 304 code when requested by clients
  • Enable mod_deflate. It may reduce performances by clients will be faster served.