I've got a virtual server that has been encountering some slowness. Thanks to the hard-won wisdom from a prior experience, I decided to check the log files. It looks like the issue this time is that PHP-FPM's process pool often lacks enough processes and occasionally maxes out. Here's a snippet of a much larger php-fpm log file:
[22-Jan-2019 09:15:30] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 32 children, there are 0 idle, and 45 total children
[22-Jan-2019 09:15:31] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 32 children, there are 0 idle, and 47 total children
[22-Jan-2019 09:15:32] WARNING: [pool www] server reached pm.max_children setting (48), consider raising it
[22-Jan-2019 09:16:49] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 8 children, there are 0 idle, and 11 total children
[22-Jan-2019 09:17:04] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 8 children, there are 0 idle, and 13 total children
[22-Jan-2019 09:17:05] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 16 children, there are 0 idle, and 15 total children
[22-Jan-2019 09:17:20] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 8 children, there are 0 idle, and 12 total children
Now I could surely edit /etc/php/7.0/fpm/pool.d/www.conf and change the current values:
pm = dynamic
pm.max_children = 48
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 6
But I'm hoping to do something smarter than just Try It And See (TIAS). For starters, I'd like you guys to let me know if I'm thinking about this correctly.
- when a page request comes in, apache has a pool of processes to handle the request. if these are all too busy, we can get slow connections, connection timeouts, etc.
- once apache accepts an incoming request for a php script, it will contact the php-fpm pool of processes and try to hand off the request for processing. if the php-fpm processes are all busy, what happens? Will I see an error in the apache error log? Will there be 5xx responses in the apache access log?
- assuming that every page request is for a php script, apache and php-fpm should have roughly the same number of processes running. Or might apache need more because it also handles images and other static content?
In the interest of getting numbers that are suitable for my machine, it seems logical to set them high enough to consume all the CPU & RAM capacity on the server, but not much more. If we take on too many requests, we might start swapping memory and the server could grind to a halt. To get suitable values for PHP-FPM's pm.max_children and for apache's MaxRequestWorkers settings (the latter currently set to 300??) we would ideally have some idea of how much memory each process consumes. I've massaged these commands a bit, and hope you folks might tell me if I'm overlooking anything. In particular, I'm wondering:
- are these the right values for memory consumption?
- do I need to worry about double-counting of memory usage because i'm showing parent & child processes or something weird lack that?
So to get an idea of the PHP processes running and how much RAM & CPU they consume, I can call these commands. If anyone has suggestions -- in particular, I'd like to know how to make this data tab-separated -- that'd be nice.
ps -C php-fpm7.0 -o "uname,ppid,pid,%cpu,pmem,rss,command"
Right now I see 6-8 processes, each consuming about 30MB.
For Apache, this:
ps -C apache2 -o "uname,ppid,pid,%cpu,pmem,rss,command"
Currently I see about five processes, each consuming 20MB.
So if we use my caveman logic that every apache process needs a PHP process to serve it and we want to consume all of my 8GB of RAM (and totally ignoring the CPU consumption for the moment) then we can assume 50MB per apache/php pair and that comes out to about 8000MB/50MB = 160. I'm thinking I should set apache's MaxRequestWorkers and PHP-FPM's pm.max_children to 160.
Does that sound right? Any thoughts or input here would be much appreciated.