The point of no return

Nginx with PHP-FPM on CentOS 6

30th June 2013 by Ali Erdinç Köroğlu

We are going to use Nginx as a HTTP server (or you can use as reverse proxy server for HTTP, SMTP, POP3, and IMAP protocols). As a general definition, HTTP or Web server is a computer delivers web page to the request of clients using the Hypertext Transfer Protocol (HTTP). Which means that you can create and serve static HTML files while your Nginx server is running. How about generating dynamic web pages or retrieving information from a database? We need a program running on web server to change the web pages, this is called Server-side scripting. It’s about embedding scripts into HTML source codes or executing the code on the server before being delivered to the client. There are many server-side scripting languages available such as ASP, Java Server Pages, Perl, Python, PHP, Ruby etc..


A server-side scripting (PHP+MySQL) from Wikipedia

Until now, everything is okay. I’m assuming that we do have the interpreter (will cover in installation) which is PHP in our entry, but how Nginx will communicate with PHP? You can say that the way Apache does :) But how Apache is doing it? Yes with mod_cgi, mod_fcgi or mod_php.. But what are they? Mod_php is an Apache module which allows Apache to interpret PHP files, mod_cgi is an Apache module which allows execution of CGI scripts and mod_fcgid is a high performance alternative to mod_cgi or mod_cgid.

FCGI or uWSGI?

Since we have no official uWSGI package and PHP plugin in CentOS-Base or EPEL repositories for now FCGI will be our buddy. Bytheway FastCGI is a protocol for interfacing interactive programs with a web server.

PHP-FPM or Spawn-fcgi ?

There are many spawn-fcgi examples in internet which you have to deal with to create init or/and bash scripts etc. but why trying to reinvent the wheel? The newer PHP (>= 5.3.3) using the included PHP-FPM and this package is in CentOS-6 Base repository. PHP-FPM (FastCGI Process Manager) is an alternative PHP FastCGI implementation. It’s not only makes configuration of FastCGI pools, but also enhances some of the FastCGI internals and increases error reporting, script termination, etc. Let’s take a look at this simple comparison table* between the most popular methods of managing FastCGI pools.

 Description PHP Spawn-fcgi + daemontools PHP-FPM
PHP daemonization: pid file, log file, setsid(), setuid(), setgid(), chroot() (-) (+) (+)
Process Management. Ability to “gracefully” stop and start PHP workers without losing any queries. php4 (-), php5 (only graceful completion) (-) (+)
Restricting IP addresses from which requests can come from php4 (-), php5 (+) (since 5.2.2) (-) (+)
Dynamic number of processes, depending on the load (adaptive process spawning) (-) (-) in SVN, PHP 5.3.3RC1+
Starting the workers with different uid/gid/chroot/environment and different php.ini options. (No need for safe mode.) (-) (-) (+)
Logging stdout and stderr (-) (-) (+)
Ability to emergency restart all the processes in the event of an accidental destruction of the shared memory opcode cache, if using an accelerator (-) (-) (+)
Forcing the completion of process if set_time_limit() fails (-) (-) (+)
 Features
Error Header (+)
Accelerated Upload Support (+)
fastcgi_finish_request() (+)
Slowlog (with backtrace) (+)

* Taken from http://php-fpm.org/about

Unix Domain Sockets or TCP Loopback Sockets ?

This is a very good question.. We’ll cover both but before you deploy anything I recommend to read those analysis..
My choice is Unix domain sockets of course, also keep in mind that you’re connecting to MySQL through Unix socket :)

Performance Analysis of Various Mechanisms for Inter-process Communication – Kwame Wright (Cooper Union), Kartik Gopalan and Hui Kang (Binghamton University)

Unix domain sockets have proven to deliver the highest throughput when compared to the other mechanisms. While its dominance is still unclear for transfers of small amounts of data, it is otherwise the best mechanism to use within a single machine.

Unix domain sockets vs. internet sockets – Robert N M Watson

In general, the argument for implementing over TCP is that it gives you location independence and immediate portability you can move the client or the daemon, update an address, and it will “just work”. The sockets layer provides a reasonable abstraction of communications services, so it’s not hard to write an application so that the connection/binding portion knows about TCP and UNIX domain sockets, and all the rest just uses the socket it’s given. So if you’re looking for performance locally, I think UNIX domain sockets probably best meet your need. Many people will code to TCP anyway because performance is often less critical, and the network portability benefit is substantial.

nginxphpfcgi
The big picture :)

Installation

Before we begin to installation we should add Nginx repository to /etc/yum/repos.d. EPEL repo has Nginx too but it’s very old version. To add EPEL repository please read read this.

 nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1

Let’s start..

yum install nginx php-common php-cli php-pdo php-mysql php-fpm

Configuration

We’ll just add a new config file into /etc/nginx/conf.d

/etc/nginx/conf.d/aekoroglu.conf
server {
        server_name ae.koroglu.org;
        root /aekoroglu/wordpress;
        add_header "X-UA-Compatible" "IE=Edge,chrome=1";
        access_log /var/log/nginx/aek.access.log main;
        error_log /var/log/nginx/aek.error error;
        index index.php ;
 
        location / {
                try_files $uri $uri/ /index.php?$args;
        }
 
        location = /favicon.ico {
                log_not_found off;
                access_log off;
        }
 
        location = /robots.txt {
                allow all;
                log_not_found off;
                access_log off;
        }
 
        location ~* \.(ico|css|js|gif|jpg|jpeg|png)(\?[0-9]+)?$ {
                expires max;
                log_not_found off;
                access_log off;
        }
 
        location ~ \.php$ {
                try_files $uri = 404;
                include /etc/nginx/fastcgi_params;
                fastcgi_pass unix:/tmp/php-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME /aekoroglu/wordpress$fastcgi_script_name;
        }
}

PHP-FPM comes with 2 configuration file, /etc/php-fpm.conf and /etc/php-fpm.d/www.conf. php-fpm.conf file has global definitions for FPM so there is no need to edit, we’ll modify www.conf.

/etc/php-fpm.d/www.conf
; Start a new pool named 'www'.
[ae.koroglu]
listen = /tmp/php-fpm.sock
 
; Set listen(2) backlog. A value of '-1' means unlimited.
; Default Value: -1
;listen.backlog = -1
 
; Unix user/group of processes
; RPM: apache Choosed to be able to access some dir as httpd
user = nginx
; RPM: Keep a group allowed to write in log dir.
group = nginx
 
; Choose how the process manager will control the number of child processes.
pm = dynamic
pm.max_children = 50
 
; The number of child processes created on startup.
pm.start_servers = 5
 
; The desired minimum number of idle server processes.
pm.min_spare_servers = 5
 
; The desired maximum number of idle server processes.
pm.max_spare_servers = 35
 
; The log file for slow requests
slowlog = /var/log/php-fpm/www-slow.log
 
; Set open file descriptor rlimit.
; Default Value: system defined value
;rlimit_files = 1024
 
; Set max core size rlimit.
; Possible Values: 'unlimited' or an integer greater or equal to 0
;rlimit_core = 0
 
php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 128M
 
; Set session path to a directory owned by process user
php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/session

The configurations of nginx and php-fpm for unix domain sockets, if you want to use TCP socket instead, you should change configurations as shown..

/etc/nginx/conf.d/aekoroglu.conf
        location ~ \.php$ {
                try_files $uri = 404;
                include /etc/nginx/fastcgi_params;
                fastcgi_pass 127.0.0.1:9000;                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME /var/www/html/aekoroglu/wordpress$fastcgi_script_name;
        }
/etc/php-fpm.d/www.conf
; Start a new pool named 'www'.
[www]
listen = 127.0.0.1:9000 
; Set listen(2) backlog. A value of '-1' means unlimited.
; Default Value: -1
;listen.backlog = -1
 
listen.allowed_clients = 127.0.0.1

Before you start there is one important thing to do.. By default /var/lib/php/session directory comes with root:apache privileges. So you have to change it..

chown -R nginx:nginx /var/lib/php/session

WordPress, php-rss reader ve simpleXML üzerine

10th June 2008 by Ali Erdinç Köroğlu

Şu aralar YDÜ-IBM innovasyon merkeziyle haşır neşir yollarda seyyah misali dolaşıyorken web temelli bir projede içerik yönetim sistemi içine başka bir kaynaktan rss çekip göstermek gerekti, php5 ile birlikte gelen aşağıda bir örneğini gördüğünüz simpleXML’i kullanayım dedim.

1
2
3
4
5
6
7
<?php
$rss =  simplexml_load_file('http://erdinc.neu.edu.tr/?feed=rss2&cat=8');
$title =  $rss->channel->title;
foreach ($rss->channel->item as $item) {
  echo "<a href='" . $item->link . "'>" . $item->title . "</a>";
  echo "<p>" . $item->content . "</p>";}
?>

Buraya kadar her şey normal ancak wordpress “Description” tag’ın da belirli bir karakter sayısından sonra feed içine […] kokuyor. Dönüp wordpress koduna baktıktan sonra bu işi en kısa yoldan aşağıdaki gibi çözebilirim diye düşündüm. Böylelikle simpleXML’de kısıtlama olmayan içeriği çok rahat alabildim.

1
2
3
4
5
6
7
8
9
10
11
--- wp-includes/feed-rss2.php.org   2008-06-10 03:08:48.000000000 +0300
+++ wp-includes/feed-rss2.php       2008-06-10 03:45:25.000000000 +0300
@@ -43,9 +43,9 @@
<?php else : ?>
                <description><![CDATA[<?php the_excerpt_rss() ?>]]></description>
        <?php if ( strlen( $post->post_content ) > 0 ) : ?>
-               <content:encoded><![CDATA[<?php the_content() ?>]]></content:encoded>
+               <content><![CDATA[<?php the_content() ?>]]></content>
        <?php else : ?>
-               <content:encoded><![CDATA[<?php the_excerpt_rss() ?>]]></content:encoded>
+               <content><![CDATA[<?php the_excerpt_rss() ?>]]></content>

Belki bir gün birini işine yarar, şimdilik google’ın tozlu raflarından birine yerleşsin :)