Thursday, July 8, 2010

PHP 5 with Tomcat 6 as FastCGI (Part 2)

In my previous article I had described how to run PHP5 as a FastCGI CGI script on Tomcat6 using Mac OS X as a sample. Actually bundled with Tomcat6 org.apache.catalina.servlets.CGIServlet is not FastCGI, but CGI servlet and PHP5 was set up to run in interactive mode. Today will be the real deal describing how to run PHP5 as a FastCGI with Tomcat6 using Ubuntu Server as a sample.

We will use the FastCGI servlet by Julien Rialland called jFastCGI and great thanks to Chu Yeow for his article Nginx, PHP and a PHP FastCGI daemon init script and Kurt Zankl for the init script.

First we need to setup the jFastCGI servlet. As described in the reference add it to your web application:

<servlet>
<servlet-name>FastCGI</servlet-name>
<servlet-class>net.jr.fastcgi.FastCGIServlet</servlet-class>
<init-param>
<param-name>server-address</param-name>
<param-value>127.0.0.1:6666</param-value>
</init-param>
<load-on-startup>5</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>FastCGI</servlet-name>
<url-pattern>*.php</url-pattern>
</servlet-mapping>


This is pretty obvious. What is not obvious is that our old trick with context.xml will not work on default Tomcat6 setup in Ubuntu. As it uses security manager. The servlet would not be able to connect to FastCGI manager and the easy way out is to add a policy to /etc/tomcat6/policy.d/03catalina.policy

grant codeBase "file:${catalina.base}/webapps/test/-" {
permission java.security.AllPermission;
};

Where test is the name of our application. The other and more universal way would be to put jFastCGI-2.0.jar to Tomcat6's classpath & grant required priviledges. You can figure it out easily from the mentioned above policy file :)

Now the fun part: we want to launch out FastCGI manager on start-up so it shoudl survive reboots, so we need an init script. You can find it below - tweak the init script a little, put it in /etc/init.d/php-fastcgi,
sudo chmod +x /etc/init.d/php-fastcgi
, run
sudo update-rc.d php-fastcgi defaults
and place the configuration for the script in /etc/default/php-fastcgi, and you’re done. You now have a PHP5 FastCGI init script that spawns and kills your PHP5 FastCGI processes.

The init script: /etc/init.d/php-fastcgi

#! /bin/sh
### BEGIN INIT INFO
# Provides: php-fastcgi
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start and stop php-cgi in external FASTCGI mode
# Description: Start and stop php-cgi in external FASTCGI mode
### END INIT INFO

# Author: Kurt Zankl <[EMAIL PROTECTED]>

# Do NOT "set -e"

PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="php-cgi in external FASTCGI mode"
NAME=php-fastcgi
DAEMON=/usr/bin/php-cgi
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
PHP_CONFIG_FILE=/etc/php5/cgi/php.ini

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions

# If the daemon is not enabled, give the user a warning and then exit,
# unless we are stopping the daemon
if [ "$START" != "yes" -a "$1" != "stop" ]; then
log_warning_msg "To enable $NAME, edit /etc/default/$NAME and set START=yes"
exit 0
fi

# Process configuration
export PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS
DAEMON_ARGS="-q -b $FCGI_HOST:$FCGI_PORT -c $PHP_CONFIG_FILE"

do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
--background --make-pidfile --chuid $EXEC_AS_USER --startas $DAEMON -- \
$DAEMON_ARGS \
|| return 2
}

do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE > /dev/null # --name $DAEMON
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
restart|force-reload)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
exit 3
;;
esac

Config file for init script (the init script looks for this): /etc/default/php-fastcgi

START=yes

# Which user runs PHP? (default: www-data)

EXEC_AS_USER=tomcat6

# Host and TCP port for FASTCGI-Listener (default: 127.0.0.1:6666)

FCGI_HOST=127.0.0.1
FCGI_PORT=6666

# Environment variables, which are processed by PHP

PHP_FCGI_CHILDREN=4
PHP_FCGI_MAX_REQUESTS=1000

0 comments: