New setup very slow
I'm new to Linode. I've setup my VPS 360 following a lot of the articles in this forum (and googleing like mad). I'm using Ubuntu 9.04. I've got an initial setup going and I've transfered one domain. Now I'm trying to optimize and clean a bit before I move the rest over.
The performance I'm getting isn't all that good.
I'm running apache as MPM-prefork, with mod-php5 and mysql. No mail on the machine (postfix installed but only dending mail out, no local delivery). I've lowered the request numbers in the config to really low values:
<ifmodule mpm_prefork_module="">StartServers 3
MinSpareServers 3
MaxSpareServers 5
MaxClients 10
MaxRequestsPerChild 2000</ifmodule>
Getting slight improvements every time I lowered them. Also increased the mysql key buffer to 86M. No queries are logged as slow with longquerytime set to 1.
The site is a WordPress site, and testing with ab (-n 1000 -c 100) from my local machine, I get:
min mean[+/-sd] median max
Connect: 88 99 26.6 90 208
Processing: 283 3734 611.8 3868 4378
Waiting: 273 3714 610.8 3846 4320
Total: 384 3833 597.3 3960 4511
with 50% of requests under 3960ms and 90% under 4132.
Disk I/O doesn't sweat (except once I got into swap and peaked at 2.5k. Don't know why but it hasn't happened again). And mem looks like this:
total used free shared buffers cached
Mem: 360 190 169 0 17 61
-/+ buffers/cache: 111 248
Swap: 255 2 253
My apache processes look like this:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 4711 0.0 2.7 37584 10316 ? S
~~So the questions are:
1. Am I missing something? I've read about lighthttpd, but I'm a long time apache user and I don't want to be stuck figuring how to do things in lighthttpd that I already know how to do in apache. Is it very different. How about .htaccess files?
2. Apache modules active: alias authbasic authnfile authzdefault authzgroupfile authzhost authzuser autoindex cgi deflate dir env mime negotiation php5 rewrite setenvif status. Basically the default. Which ones can I disable safely?
3. I've found a million articles talking about prefork vs worker, but I still can't figure out which is best for this setting. I don't expect a mad amount of traffic and have about 10 sites in total.
4. I would like apache to run as the user who owns the files for that Vhost. Wordpress auto-updates doesn't work when running as www-data and files are owned by user, even with generous privileges (777, which is not recommended anyway). I know I can do that with php as a cgi and suexec, but isn't cgi supposed to be slower than mod-php? Is there a way for the prefork to run as user (for each user)?
Well, I know it's a lot to ask for, but I've tried 1000 different settings and I know that it can get better than this by other user's stories.
Thanks for the help.
12 Replies
What does "vmstat 1" look like while you run the test?
-Chris
> Our of curiosity, what do the ab numbers look like when you match the -c setting to the MaxClients value (-n 1000 -c 10) ?
As expected (I suppose) blazing fast!
min mean[+/-sd] median max
Connect: 88 91 1.9 90 104
Processing: 247 322 63.5 304 645
Waiting: 232 304 62.8 286 629
Total: 336 412 63.7 395 736
> What does "vmstat 1" look like while you run the test?
With 10 concurrent connections:
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
0 0 2744 80316 25936 147064 0 0 0 0 66 20 0 0 100 0
5 0 2744 68012 25936 147064 0 0 0 0 4204 2032 61 3 36 0
3 0 2744 51532 25936 147064 0 0 0 0 4151 2112 81 5 14 0
6 0 2744 33916 25936 147064 0 0 0 0 4437 2225 89 5 6 0
4 0 2744 33496 25936 147068 0 0 0 0 4566 2407 90 5 4 0
8 0 2744 29704 25936 147068 0 0 0 0 4544 2148 90 4 6 0
7 0 2744 27348 25944 147076 0 0 0 372 4525 2450 91 5 4 0
6 0 2744 28464 25944 147076 0 0 0 0 4588 2364 92 5 3 0
The bo ( what is 'bo' btw? Ok, blocks out.
With 100 concurrent connections:
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
0 0 2744 78828 26096 147164 0 0 0 0 14 16 0 0 100 0
3 0 2744 65180 26096 147164 0 0 0 0 3065 1327 41 3 57 0
7 0 2744 55232 26096 147164 0 0 0 0 3660 1908 71 3 26 0
8 0 2744 43200 26096 147164 0 0 0 0 4597 2319 92 4 3 0
10 0 2744 20212 26096 147164 0 0 0 0 4548 2214 94 5 0 0
8 0 2744 26908 26096 147168 0 0 0 0 4500 2215 94 4 2 0
10 0 2744 21452 26104 147168 0 0 0 368 4495 2086 95 4 0 0
6 0 2744 25048 26104 147176 0 0 0 0 4532 2314 94 5 1 0
And the same spikes of 'bo' pattern. CPU usage seems to be higher (also logical) at 93-95%, where as before it was more variable between 89-94%.
Your Linode isn't disk bound. You have some memory left to play with, so you may want to try increasing MaxClients to 15 and running the benchmark again (with -c15) and see if you can sustain 15 clients without a drop in request time. Although from your vmstat output I don't think you're far off from the optimal setting -- since your CPU seems to max even with 10 concurrent clients…
Ask yourself: Where's the bottleneck - DB or apache/php (watch top during the test)? How can I decrease processing time? How to scale number of clients if processing time is fixed? What's more important to you: page rendering time or max number of current clients hitting php pages?
If this is a personal blog, I'd say you're doing just fine. Are you really going to be doing 26M hits/month of php files, not including associated hits for other images/meda (say 100-200M hits?).
-Chris
With higher MaxClients I hit swap as soon as I demand a bit from apache.
The bottle-neck is definitely php. Calling a plain file doesn't seem to bother apache much and gives much higher requests per second. So how do I decrease php's footprint? I've thought of killing the modules I don't need. I can always reactivate as needed. Will this benefit performance?
Also I can install a php accelerator. Any suggestions? Is it straight-forward? As in "apt-get install and forget about it" straight-forward?
But this still doesn't answer my question of whether I should switch to the worker MPM or if there is any other way to run apache prefork processes as users (or work-around that).
Again thanks for the help.
I do, however, use lighttpd instead of Apache. There are tradeoffs, but its ability to not go fork-happy to handle a large number of concurrent connections has improved my quality of life. Plus, simple-vhost is simply genius.
You would have to use php-cgi instead of the built-in module, but it sounds like this might be something you're considering anyway: mod_php is a part of Apache, so it has to run as the same user. You can spawn php-cgi as any user you'd like; I run two instances with different usernames, since one of my payloads has to run as the same uid as another application (and its installation documents recommend setting Apache's uid to the other application's uid
I also use memcached, which my primary payload supports quite effectively. This cuts down on a lot of the "thinking" that has to be done for routine hits. It does, however, require heavy integration with the application and (by definition) eats a quantity of memory. Worth it, though.
I think the biggest thing you can do to improve Wordpress response time, during both periods of light and heavy load; and overall throughput is to use a PHP opcode cache. APC is packaged for Ubuntu. Other distros might package other options, like XCache or Eaccelerator. This eliminates per-request overhead that can easily work out to 50% of wordpress page generation time which will halve response times and double throughput. It comes at the cost of some memory, but that memory is shared among apache processes.
Its true that apache can use a lot of memory, but apache+php is a really well understood combo, so its worth looking at ways to make sure you get the most of the the memory apache+php use. I've done this by putting nginx in front of apache. Nginx is, like lighttpd, a very memory and cpu efficient web server. I have it serving all the static files, which means that I'm not wasting an heavyweight apache process. This is a big deal. My stylesheet takes something like 300ms to send to a client on a DSL connection. That's 300ms where the 20-30MB each apache+php worker uses is basically wasted. In that amount of time, one worker could render ~3 dynamic requests.
It also buffers the responses back from apache, which means that once apache is done generating the page it can move quickly to the next dynamic page request while nginx does the boring work of gzipping the response and sending it out to the client. Again, this is a big win. My home page HTML takes ~250ms to send, enough time to service 2.5 more requests.
Taken together, relying on nginx for static requests and buffering of dynamically generated responses probably means I can get by with 1/10th as many apache processes which leaves a lot more memory free for caching data. This isn't going to be a big deal when my site is lightly loaded.
So, my suggestion to you is install APC, then think about setting up nginx as a front end. I can post my config if you are interested.
Yes, I already installed APC and noticed some improvement, but not so much as reported elsewhere; I suppose it depends on your scripts or setup.
My system seems quite stable and handles the traffic it gets very well, with room for an occasional burst. I suppose until a real world™ test (a digg or the sorts) we won't know how well it handles pressure.
I'll keep your Nginx tip in mind if I ever encounter problems or need some extra performance.
> So, my suggestion to you is install APC, then think about setting up nginx as a front end. I can post my config if you are interested.
Please post your config, I'd love to take a look at this as an option. Lighttpd alone just isn't cutting it. An app I use just relies on Apache and the way it does stuff too much.
I'm on ubuntu 9.04. Nginx isn't in the official repositories, but there is a recent version in Universe, so I edited /etc/apt/sources to enable those repositories. I'm running apache2 with mpm-prefork and their packaged php5 and apc modules. I'm sure you can get to the same starting point on other distributions with a little googling.
Starting from the default config, I disabled mod_deflate on Apache, since I want nginx to handle compression. I then enabled the rpaf module so that apache reads the headers nginx sets in order to find out the real client IP for logging, etc.
Next, I edited /etc/apache2/ports.conf to have Apache listen on port 8080, rather than port 80. I will probably go further and just have it listen on localhost.
Similarly, for each VirtualHost entry in /etc/apache2/sites-enabled/*, I set the listening port to 8080.
For nginx, starting with the provided config files:
/etc/nginx/nginx.conf
user www-data;
worker_processes 1;
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;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
tcp_nodelay on;
gzip on;
server_names_hash_bucket_size 64; # added this because some of my domain names were too long.
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
The nginx package for ubuntu follows the pattern used for apache (I think this approach comes from Debian) of breaking peices of config out into files and including whole directories, so I took advantage of this to break out my installation wide tweaks for compression and reverse proxying.
/etc/nginx/conf.d/gzip.conf
#by default, nginix only gzips text/html, and maybe text/plain
gzip_types text/html application/javascript application/x-javascript text/javascript text/css text/xml text/plain application/atom+xml;
gzip_min_length 512; #probably not that important, but it didn't seem worth even trying to gzip small responses.
/etc/nginx/conf.d/
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering on;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
/etc/nginx/sites-enabled/mysite:
server {
listen 72.14.177.137:80; # your server's public IP address
server_name mysite.com www.mysite.com; # your domain name
access_log /var/log/nginx/mysite.access.log;
location / {
root /var/www/mysite; # absolute path to your WordPress installation
index index.php index.html index.htm;
# makes sure that all .php files are served by apache
if ($request_filename ~* "\.php$") {
break;
}
# this serves static files that exist without running other rewrite tests
if (-f $request_filename) {
#expires 30d;
break;
}
# this sends all non-existing file or directory requests to apache
if (!-e $request_filename) {
proxy_pass http://localhost:8080;
}
}
location ~ \.php$ {
proxy_pass http://local host:8080;
}
}
I derived this from someone elses config for fcgi that I found on line. I think it could probably be a little cleaner, but I was deliberately a little cautious.
That's pretty much it. Restart apache, start nginix and you should be good to go. You can check the response headers and the server log files to make sure requests are being handled as expected.
Oh, I did tweak the apache config a little more to reduce the maximum # of apache processes to 6 or 8 or so. I figured at least 4 so there could be one running on each core, and then some extras in case there were any waiting for I/O. Not too many though, because it wastes memory and leads to greater variance in response times.
I also ended up tweaking my apc configuration a little to give it more memory and to enable expiration of cached items (by default it would clear the whole cache if it filled).
There is more tweaking you can do. For example, you can set some cache header policies for both static and dynamic content, and nginx can also cache the output of dynamic pages and serve the cached copy without hitting apache.
server {
listen 80;
server_name xxxxxxxx.com;
access_log /var/www/log/access.log;
error_log /var/www/log/error.log;
location / {
proxy_pass http://127.0.0.1:8080;
}
location ~* ^.+.(jpe?g|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js|swf|avi|mp3)$ {
expires 30d;
root /var/www/;
}
}
It seems to work. I disabled Apache and the site went down the images were still being served.
These rules are used to proxy requests from nginx @ port 80 to apache @ port 8080.
You must setup apache to listen to port 8080 and you should use module rpaf to pass the real ip to apache logs.
sudo /etc/init.d/apache2 stop
I wanted to make sure that nginx was serving up static files using the rules that I mentioned. I stopped Apache. My sites went down, but direct links to static files still worked. Test done.
Sorry if it sounded like I was confused.