Setup nginx and Perl with fastcgi (and multiple Workers)

Aus Carl-Christian Sautter - Wiki
Wechseln zu: Navigation, Suche

There are many fcgiwrapper Scripts which should work with Perl and Nginx. Some does, some not...
I have tested the Debian/Ubuntu Packages called "fcgiwrap". Yes it works - but only in small environments. Multiple requests per second offer the bottleneck of this solution. (It can only handle one request at once.)

I found a solution called FCGI::Daemon which offers a fcgi wrapper for Perl with multiple Workers, Queue and none blocking.

Install FCGI::Daemon

Install the File Daemon.pm via Cpanminus

cpanm FCGI::Daemon

In Debian or Ubuntu environments the file should be placed under /usr/local/share/perl/5.18.2/FCGI/Daemon.pm". (Note: the Perl Version can differ)
Now we create an init Script for launching the Daemon under /etc/init.d/fcgi-daemon:

#!/bin/sh
### BEGIN INIT INFO
# Provides:          fcgi-daemon
# Required-Start:    $remote_fs $syslog $network
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: FastCGI daemon
# Description:       FastCGI daemon for use with nginx
### END INIT INFO

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="FastCGI daemon"
NAME="fcgi-daemon"
SCRIPTNAME="/etc/init.d/$NAME"
DAEMON="/usr/local/share/perl/5.18.2/FCGI/Daemon.pm"
PIDFILE="/var/run/${NAME}.pid"
SOCKFILE="/var/run/${NAME}.sock"
DAEMON_OPTS="-d -s $SOCKFILE -p $PIDFILE -w 12 -u www-data -g www-data"
PERL="/usr/bin/perl"

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

set -e

DAEMON="$PERL $DAEMON"

# 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.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions


# Function that starts the daemon/service
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 $PERL --startas $DAEMON --test > /dev/null \
        || ( log_warning_msg ": $NAME is already running"; return 1 )
    start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $PERL --startas $DAEMON -- $DAEMON_OPTS || return 2
    sleep 1
}

# Function that stops the daemon/service
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
    RETVAL=$?
    rm -f $PIDFILE $SOCKFILE
    return $RETVAL
}

# Function that sends a SIGHUP to the daemon/service -- not implemented
do_reload() {
    #
    # If the daemon can reload its configuration without
    # restarting (for example, when it is sent a SIGHUP),
    # then implement that here.
    #
    #start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --exec $DAEMON
    return 0
}

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
    ;;
  status)
    log_daemon_msg "Checking $DESC" "$NAME "
    pidofproc -p "$PIDFILE" >/dev/null
    case "$?" in
        0) log_success_msg "is running" ;;
        *) log_warning_msg "is NOT running" ;;
    esac
    ;;
  restart|force-reload)
    #
    # If the "reload" option is implemented then remove the
    # 'force-reload' alias
    #
    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|status|restart|force-reload}" >&2
    exit 3
    ;;
esac

:

Check if the daemon path fits your environment. Have a look at the daemon_opts. Set the Number of worker Threads "-w X" X should be the Number of your threading cores.
Set the default startup for the FCGI Daemon and start the Daemon:

update-rc.d fcgi-daemon defaults
/etc/init.d/fcgi-daemon start

Setup nginx vhost:

server {
        listen 80;
        root /var/www;
        index index.pl;
        location ~ \.pl$ {
                gzip           off;
                fastcgi_pass   unix:/var/run/fcgi-daemon.sock;
                include        /etc/nginx/fastcgi_params;
        }
        location ~ /\.ht {
               deny all;
        }
}

Relevant Settings are:

        location ~ \.pl$ {
                gzip           off;
                fastcgi_pass   unix:/var/run/fcgi-daemon.sock;
                include        /etc/nginx/fastcgi_params;
        }

Reload your Nginx Settings:

service nginx reload

Your perl scripts should be executable now!