Nginx and PHP-FPM for heavy load wordpress web server with high traffic 2000+ concurrent connections.
To make it to read short I will not be describing all the time I lost during last half year trying to find out the “right settings” for the high traffic WordPress web server running I was responsible for, and this was on the top of my primary job, and that’s why it took so much time of blind testing and playing parameters with live production server, and as you understand I have no ability to make sharp actions and have long downtime, while having only 30-40 minutes per day. Anyway I found all needed information, tested everything, it works, I’m happy.
So the server is quite ordinary 2 CPU (8 cores total), 16 GB RAM, SSD drive, colocated in proper data center. Ubuntu OS, PHP 5.3.5 (with PHP5-FPM), MySQL 5.5, WordPress 3.3.1, APC cache.
Just to give you some ideas what I was trying while I was loosing my time. As far as you understand I couldn’t reinstall OS, so I was playing only with the packages installed on it. So as for database I use MySQL 5.1 after 5.1a and at the end 5.5. Honestly I didn’t find any big advantages that made my life easier or brought some performance benefits only simply because of a new version.
As for PHP, the first version I’ve started with already running on this server was PHP 5.1.6, after it was 5.2.3, then 5.2.5, then 5.2.17, then 5.3.3, and at last 5.3.5. Why I’m enumerating all these versions ? Early versions of PHP do not include the PHP5-FPM package, and means it was not comfortable to apply special patch contained this package. Version PHP 5.3.8 on latest Ubuntu server has perfectly runnig PHP5-FPM out of the box.
Now about PHP cache. I’ve tried e-accelerator at the beginning, then it was xcache and at last I’ve grounded on APC. I cannot answer why APC, but I simply never had any problem with, that is what I can not tell about two others, especially about xcache.
So now you are aware about these processes that preceded my successful completion of optimization and possibly your tried something similar already to do, so now I will describe it more in details.
So first of all and most important is OS limitations, this is where I’ve lost most of my time, trying to implement somebodies advices without any success.
Here is my Linux Kernel configuration file, /etc/sysctl.conf
fs.file-max = 262144
kernel.pid_max = 262144
net.ipv4.tcp_rmem = 4096 87380 8388608
net.ipv4.tcp_wmem = 4096 87380 8388608
net.ipv4.netfilter.ip_conntrack_max = 65536
net.core.rmem_max = 25165824
net.core.rmem_default = 25165824
net.core.wmem_max = 25165824
net.core.wmem_default = 131072
net.core.netdev_max_backlog = 8192
net.ipv4.tcp_window_scaling = 1
net.core.optmem_max = 25165824
net.core.somaxconn = 65536
net.ipv4.ip_local_port_range = 1024 65535
kernel.shmmax = 4294967296
vm.max_map_count = 262144
You can easily find in the man of all the descriptions of these parameters, but what I wish to mention that this configuration is good for my 16GB RAM server. I suppose more RAM means bigger values possible.
But all these settings above are useless without fixing ulimit parameter for you Linux. For Debian based Linux it should be done in /etc/security/limits.conf
it should be updated for any user which responsible for your running package
nginx soft nofile 131072
nginx soft nofile 131072
mysql soft nofile 131072
mysql soft nofile 131072
root soft nofile 131072
root soft nofile 131072
and don’t forget to add in /etc/pam.d/common-session the following string:
session required pam_limits.so
So here you are! Your server can breathe in much freely!
Now the database.
First and the main this is to switch your MySQL into InnoDB. Yes, it take more memory than MyISAM, it harder to process defragmentation for them, but the performance is why you are here on this page, so InnoDB and no more discussions.
Before I will publish my configuration file for MySQL, there is another important step you should do. Create a RAM disk of 1-2 GB for MySQL “temp” purposes (various caches, tables, and others).
For Debian based Linux it should be done in /etc/fstab:
ramdisk /tmp tmpfs mode=1777,size=2048m
To check that it’s working use command df -h
you will get something like this:
ramdisk 2.0G 140M 1.9G 7% /tmp
Now the /etc/mysql/my.cnf file (tuned in suffers, tears and blood)
[mysqld]
user = mysql
socket = /var/run/mysqld/mysqld.sock
port = 3306
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
default-storage-engine = InnoDB
skip-external-locking
bind-address = 127.0.0.1
key_buffer = 512K
max_allowed_packet = 16M
thread_stack = 128K
thread_cache_size = 48
thread_concurrency = 8
innodb_file_io_threads = 8
myisam-recover = BACKUP
max_connections = 768
table_cache = 2048
query_cache_type = 1
query_cache_limit = 32M
query_cache_size = 384M
query_cache_min_res_unit = 256
query_prealloc_size = 65K
range_alloc_block_size = 128K
read_rnd_buffer_size = 1M
record_buffer = 1M
read_buffer_size = 4M
join_buffer_size = 2M
sort_buffer_size = 2M
bulk_insert_buffer_size = 8M
long_query_time = 2
sync_binlog = 1
log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 5
max_binlog_size = 100M
innodb_additional_mem_pool_size = 64M
innodb_buffer_pool_size = 4096M
innodb_log_file_size = 256M
innodb_log_files_in_group = 2
innodb_log_buffer_size = 32M
innodb_open_files = 16384
open-files-limit = 16384
max_tmp_tables = 1024
table_open_cache = 2048
#tmp_table_size = 512M
#max_heap_table_size = 512M
wait_timeout = 1200
As I said, explanations will be later on, all parameters could be found in on MySQL manual.
Now most annoying, but where important for overall productivity of PHP Web server – PHP5-FPM.
I will provide configuration of 3 files:
php.ini
main.conf
www.conf
I change only 4 parameters in this file /etc/php5/fpm/php.ini:
memory_limit = 256M
post_max_size = 8M
upload_max_filesize = 10M
max_file_uploads = 20
and add support of APC with the following string:
extension=/usr/lib/php5/20090626/apc.so
This is it for php.ini
Now php handler parameters which could be found in /etc/php5/fpm/main.conf:
[global]
pid = /var/run/php5-fpm.pid
error_log = /var/log/php5-fpm.log
emergency_restart_threshold = 0
emergency_restart_interval = 0
process_control_timeout = 0
daemonize = yes
include =/etc/php5/fpm/pool.d/*.conf
And now /etc/php5/fpm/pool.d/www.conf:
[www]
listen = 127.0.0.1:9000
;listen = /tmp/php5-fpm.sock
listen.backlog = 65536
user = www-data
group = www-data
pm = dynamic
pm.max_children = 16
;pm.start_servers = 12
;pm.min_spare_servers = 8
;pm.max_spare_servers = 16
pm.max_requests = 1536
pm.status_path = /status
request_terminate_timeout = 0
rlimit_files = 65536
rlimit_core = unlimited
catch_workers_output = no
Please not that all configuration files above are not for simple copy paste but for understanding the idea why the values are like these.
It’s not recommended to use PHP5-FPM via unix socket for more than 300+ concurrent connections due to you will definitely get nasty error: sock failed (11: Resource temporarily unavailable) or connect() failed (11: Resource temporarily unavailable) depending on your version, (you may need to increase start_servers, or min/max_spare_servers), and and every 5th page will be return Error 502. By using tcp/ip as a handler you will get more stability but will loose around 10 connection on each 100 concurrent connection in performance.
And the other 2 tricky things to make it to run more smoothly is to gracefully restart PHP5-FPM every 9 minutes and clear the Query Cache on MySQL every 8 minutes. Again, the time depends on your load and content of your project.
For doing that you need Cron up and running and two files with special commands to execute:
To set the cron, you need crontab -e to set the time of execute and path to the files:
*/8 * * * * /etc/cron.my/mysql_query_flash
*/9 * * * * /etc/init.d/php5-fpm reload
Here the script of the /etc/cron.my/mysql_query_flash file:
mysql -u root -pYOURPASSWORD -e “flush query cache”;
Nginx configuration. Nothing interesting and tricky here.
As for/etc/nginx/nginx.conf file here you are:
user www-data;
worker_processes 8;
pid /var/run/nginx.pid;
timer_resolution 100ms;
worker_rlimit_nofile 32768;
worker_priority -5;
events {
worker_connections 4096;
multi_accept on;
use epoll;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 60;
types_hash_max_size 2048;
server_tokens off;
server_names_hash_bucket_size 64;
server_name_in_redirect off;
client_body_buffer_size 8K;
client_header_buffer_size 2k;
client_max_body_size 16k;
large_client_header_buffers 4 2k;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log off ; #/var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
gzip_disable “msie6”;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 64 8k;
gzip_min_length 1024; gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
include /etc/nginx/conf.d/*.conf;
}
APC configuration /etc/php5/conf.d/apc.ini I left in default setting but only increased the RAM parameter:
apc.shm_size = 128M
For now this is it. I use STATUS module from nginx that is quite informative and here is the average result during the Saturday and Sunday when I have the highest load.
I’m quite proud of it!
Active connections: 2617 server accepts handled requests 62950 62950 300560 Reading: 198 Writing: 81 Waiting: 2338
Since PHP 7 all these settings are less important. PHP-FPM is now rock solid solution for any project.
You can add W3Total Cache and memcached to get things more fast.
i think here is a problem
nginx soft nofile 131072
nginx soft nofile 131072
mysql soft nofile 131072
mysql soft nofile 131072
root soft nofile 131072
root soft nofile 131072
should be like this?
nginx soft nofile 131072
nginx hard nofile 131072
Hello Sergio,
First of all, thank you for your post. I’m using a xen vps with 8 core cpu, 2gb ram & 4gb swap.
I’m on centos 5.8 32bit with nginxproxy, php53u, php-fpm, xcache.
Can you tellme what will be my optimal configs to use in this server. I don’t care if the server runs on 99% cpu usage or 1999mb ram. I just want every bit of performance.
Any help will be helpful.
Thanks.
Not so sure regarding the socks vs TCP. One of our clients has 2000+ concurrent on peaks (3.5-4million pageviews per month), with socks and we have not seen this issue. But we are using nginx as a reverse static cache proxy for Apache/PHP5-FPM/APC backend.
session required pam_limits.so
On centos you can add this in /etc/pam.d/system-auth
It works like a charm mate! Thank you so much.
Hi, nice article.
Since you are running nginx as user “www-data”, shouldnt you use
“www-data soft nofile 131072” intead of “nginx soft nofile 131072” ?
Actually, it would probably be more explanatory as “web-server-running-username soft nofile 131072”
I’m quite curious why you restart PHP-FPM every 9 minutes.
In fact, I have been considering doing this myself, after a long search of many weeks on a problem that causes PHP-FPM to hang every 0-3 times a day. Both reload and restart fix this issue, but only reload is “soft” and non-user visible.
Currently we have a monitoring script that checks a ping.php page every 10 seconds, and triggers the reload when the ping page fails.
This works okay, but now a 2nd problem has arisen, sometimes causing problems every 1 in 20 requests. For some unknown reason, the config array (containing db connection info) is empty on those requests.
For this second problem we’re looking at ulimit nofiles, as we think the xml config file of our app may be unreadable.
Only the restart will fix this 2nd issue.
Can you explain a bit more about the reasoning behind the reloads?
All this stuff happened with me without any reason during PHP-FPM versions 5.2.xxx – 5.3.xxx since I moved 5.4.xxx branch all these what you’ve described above has gone. To be more precise now I’m using mostly Debian 7 and it’s PHP-FPM 5.4 version, which also allows to install php-apc straight via apt-get (no pear + cli) required. Also keep in mind that Debian rated php-fpm package as stable only since 5.4. I was waiting for stable version for 4 years since 2009. If you have ability to use this distro you may avoid all these issues.
I would like to know also the explanation of the php-fpm reload. Is it okay to reload this every 9 mins? I set my max server to 128 but I still received a “you may need to increase start_servers, or min/max_spare_servers” error.
And also,why you need flush query cache?
php-fpm reload also helps to clean the object cahce. In versions prior of php-fpm 5.4 restart interval not function properly, so I was doing it manually via cron.
As for dynamic, I suggest you to use either static (if you got enough RAM) or ondemand value. Anyway number of children will be equal to the number of your CPU threads. So don’t spend you time on calculation of min/max_spare_servers.
Thank you a lot for sharing this with all people
you really recognize what you’re talking about!
Bookmarked. Kindly also discuss with my site =). We could have a
link alternate contract between us
encuentrocine.com site Newyorkwood.org portal
There aare considerations you need to think about if you want to sell your video content.
Optimizing a web site primarily requires editing its content material and HTML and
connected coding to both improve its relevance
to particular key phrases and to remove barriers to the
indexing activities of search engines like google. eval(ez_write_tag([[300,250],’brighthub_com-medrectangle-1′]));.
That is because the e-cigs are much more efficient inn keeping the
blood nicotine levels above the threszhold that triggers a
normal smoker to reach for another cigarette. Thiss smoke is generally a
mixtire of a number of gases like carbon dioxide, carbon monoxide, carbon hydroxide,
methane, propane, butane, isobutene, etc. After advertising on Craigslist, hhe soon had a huge
customer base iin Cerritos and tthe surrounding areas.