How to tune PHP-FPM for super server performance
PHP-FPM tuning for enhanced server performance
You've set up a new server, installed PHP above your database of choice, and your site has been running happily in the background. But now your SEO work and recent marketing campaign is funnelling more traffic to your server. You are at the stage of business growth where having a stable website is vital and your online popularity is having the opposite effect. Visitors to your website will experience either a 'This website took too long to respond' message or, worse, an Error 500 (internal server error).
If your website is struggling to support extra traffic then speak to us about the technical support we can provide. We offer competitively priced hosting packages to suit any business - click here to learn more.
We build it. We host it. We keep it safe.
Apache mod_php
By default, Apache serves PHP pages using mod_php. This in itself is not necessarily a bad thing. Essentially Apache will spawn ('pre-fork') a new process for each connection starting with, and keeping, a pool of processes available for any page being served. Each spawned process is 'self contained', meaning it contains its own copy of the PHP interpreter. On the one hand this results in efficient serving of 'PHP heavy' websites (WordPress and Moodle are good examples) but, on the other, each spawned process can consume a good deal of memory. As your site becomes popular (and again we're thinking here of both wanted and unwanted visits) you'll see memory use climb. It is possible to tune mod_php to ensure Apache doesn't attempt to consume more memory than you have available and we'll study this is a subsequent post. But for now let's take a look at php-fpm.
What is PHP-FPM?
For our more popular PHP-based sites we employ PHP-FPM - the FastCGI Process Manager - to serve PHP files. How does this work? When Apache receives a request for a .php file it passes this on to the (separate) process manager service. This service then processes the request and hands it back to Apache to serve. PHP-FPM maintains pools of worker threads to achieve more efficient processing.
Tuning PHP-FPM
Leave your server running for a couple of days and check the PHP-FPM log. Run the following command (note that we're currently running PHP 7.3 and the '7.3' in 'php7.3' in the examples below may need to be replaced with the version number of PHP you're running):
# sudo nano /var/log/php7.3-fpm.log
You might see the following:
[05-Feb-2020 08:28:31] WARNING: [pool www] server reached pm.max_children setting (10), consider raising it [05-Feb-2020 08:28:31] 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 9 total children
Our server was beginning to get busy just as the working day starts - which is expected - and so clearly we need to don our overalls and open our server toolbox.
Doing the Math
Run the following command:
# ps -ylC php-fpm7.3 --sort:rss
This will print something similar to:
S UID PID PPID C PRI NI RSS SZ WCHAN TTY TIME CMD S 0 9030 1 0 80 0 31244 120002 - ? 00:00:05 php-fpm7.3 S 33 7511 9030 1 80 0 130636 148827 - ? 00:00:04 php-fpm7.3 S 33 7479 9030 2 80 0 133724 150336 - ? 00:00:16 php-fpm7.3 S 33 7504 9030 2 80 0 136272 150365 - ? 00:00:08 php-fpm7.3
Looking at these figures we're seeing roughly 150000KB, or 0.15GB, given to each php-fpm7.3 process.
Next we need to estimate how much RAM is being consumed by other processes. Run:
# top
Press the '<' key three times to sort by RES. The output may look something like the following (this is an extract):
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1114 mysql 20 0 1624004 296388 18780 S 0.0 7.3 16:36.53 mysqld 8656 www-data 20 0 604176 148324 96236 S 0.0 3.7 0:14.98 php-fpm7.3 8840 www-data 20 0 603624 146836 94808 S 0.0 3.6 0:12.41 php-fpm7.3 8474 www-data 20 0 604004 146308 94884 S 0.0 3.6 0:24.84 php-fpm7.3 416 root 19 -1 153176 65056 62980 S 0.0 1.6 0:07.50 systemd-journal 9111 root 20 0 539516 35168 27912 S 0.0 0.9 0:09.57 apache2 9030 root 20 0 480008 31244 25436 S 0.0 0.8 0:06.25 php-fpm7.3 11852 root 20 0 1237268 24372 10108 S 0.3 0.6 5:50.86 fail2ban-server 8300 www-data 20 0 542652 21868 13880 S 0.0 0.5 0:00.54 apache2 8086 www-data 20 0 542636 21832 13868 S 0.0 0.5 0:00.71 apache2 8424 www-data 20 0 542600 21804 13876 S 0.0 0.5 0:00.37 apache2 8683 www-data 20 0 542600 21804 13880 S 0.0 0.5 0:00.27 apache2 8594 www-data 20 0 542584 21796 13884 S 0.0 0.5 0:00.31 apache2 8444 www-data 20 0 542592 21784 13860 S 0.0 0.5 0:00.38 apache2 8600 www-data 20 0 542576 21756 13852 S 0.0 0.5 0:00.24 apache2 8943 www-data 20 0 542596 21720 13808 S 0.0 0.5 0:00.11 apache2 8951 www-data 20 0 542520 21520 13720 S 0.0 0.5 0:00.01 apache2 8944 www-data 20 0 542584 20896 13020 S 0.0 0.5 0:00.06 apache2 1014 root 20 0 187680 20088 12172 S 0.0 0.5 0:00.12 unattended-upgr 25696 www-data 20 0 173904 9200 4232 S 0.0 0.2 0:00.37 apache2
From this data we are looking at needing to reserve at least 300,000KB, or 0.3GB to MySQL (and obviously better to allocate more). We are also spawning Apache processes through mod_php and the maximum number of these (at approx. 14000KB per instance) will need to be limited and taken account of (we'll take a look at this in a separate post).
For now, let us allocate 2GB to PHP-FPM. At 0.15GB per process that's 2 divided by 0.15, giving space for just over 13 child processes. Let's make it a maximum of 12.
Configuring PHP-FPM
To configure PHP-FPM run the following command
# sudo nano /etc/php/7.x/fpm/pool.d/www.conf
The key setting to look for are:
pm = dynamic
We could consider setting this to 'static' but, as we have other services running on this server (namely mysqld and Apache forks) we are going to leave it as dynamic and let it flex.
And, based on how many php-fpm7.x processes seem to be running throughout the working day, we are going to specify the following:
pm.max_children = 12 pm.min_spare_servers = 2 pm.max_spare_servers = 4 pm.start_servers = 12
We are also aware of the potential for memory leaks in the WordPress plugins employed on this particular site so we are going to set:
pm.max_requests = 500
Save the file and then restart PHP-FPM:
# sudo service php7.3-fpm restart
Continue to carefully monitor memory use.
Lumina Consultancy are software programming, web development and web security specialists.
We build it, we host it, and we keep it safe.
Please get in touch to learn more about us and our work.
© 2017-2024 Lumina Consultancy Limited | UK Company Registration No: 10627969