Friday, January 25, 2013

Dancer + Nginx + FastCGI

Bring together the parts

I was able to find complete documentation on running dancer with nginx using FastCGI. In the dancer deployment it discusses using Apache and fastcgi or nginx with proxy. Often, using nginx as proxy to starman(or some other plack webserver) is probably the right thing. But sometimes using fastcgi is good idea too :) I'm running some benchmarks but it seems like memory usage is a bit smaller with FCGI but I still need to do more digging on this.

Make it so

Lets get all the parts install and then configure them

Install the Parts

Note: These parts will be very dependent on your system. I'm assuming a debian/ubuntu system below.

First you need to have nginx and dancer installed. For nginx i would recommend doing something like:

apt-get install nginx

or building and install from source. 

Next, install need Perl modules:

cpanm + Dancer's Makefile.PL

I like to install using Dancer's app generated Makefile.PL along with cpanm to manage Perl dependencies.

Setup your Makefile.PL:

use strict;
use warnings;
use ExtUtils::MakeMaker;

WriteMakefile(
    NAME                => 'AppName',
    AUTHOR              => q{your name},
    VERSION_FROM        => 'lib/ExApp.pm',
    ABSTRACT            => 'example dancer app',
    ($ExtUtils::MakeMaker::VERSION >= 6.3002
      ? ('LICENSE'=> 'perl')
      : ()),
    PL_FILES            => {},
    PREREQ_PM => {
        'Test::More' => 0,
        'YAML'       => 0,
        'Dancer'     => 1.3095,

        # required to run in fcgi mode
        'Plack'      => 0,
        'FCGI'       => 0,
        'FCGI::ProcManager' => 0,
 
        # application specific
        'Template'   => 0,
        'Dancer::Plugin::Database' => 0,
        'DateTime' => 0,
        'DateTime::Format::MySQL'  => 0,
         
    },
    dist                => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
    clean               => { FILES => 'example-app-name-*' },
);

You can do: cpanm --installdeps .

Other options....

cpanm Dancer Plack FCGI FCGI::ProcManager
Or
cpan install Dancer Plack FCGI FCGI::ProcManager

Configure nginx

Now, lets configure nginx nginx.conf. It isn't a large change from http://wiki.nginx.org/NginxFcgiExample. There are three main changes below.

  1. Configure nginx to serve the static content (in dev this isn't a huge deal but in production you will save some major time and resources).
  2. Plack's FCGI handler requires SCRIPT_NAME to be empty. I'm not sure why but i'm sure its a very smart security thing or due the fact that it isn't using a dispatch script.
  3. Plack's FCGI handler uses PATH_INFO and needs it to be configured. See:set $path_info $uri; line

Example Configuration:

    server {
        listen       80;
        server_name  fastcgi-example.leecarmichael.com;
        root /home/user/dancer_app/public;
        access_log  logs/fcgi-example.access.log  main;

        # serve static content from nginx not dancer
        location = /favicon.ico {
        }

        location /images {
        }

        location /js {
        }

        location /css {
        }

        # serve all other stuff from appserver
        location / {
                set $script "";
                set $path_info $uri;
                fastcgi_param   QUERY_STRING        $query_string;
                fastcgi_param   REQUEST_METHOD      $request_method;
                fastcgi_param   CONTENT_TYPE        $content_type;
                fastcgi_param   CONTENT_LENGTH      $content_length;

                fastcgi_param   SCRIPT_FILENAME     $request_filename;

                # plack FCGI handler requires these changes
                fastcgi_param   SCRIPT_NAME         $script;
                fastcgi_param   PATH_INFO           $path_info;

                fastcgi_param   REQUEST_URI         $request_uri;
                fastcgi_param   DOCUMENT_URI        $document_uri;
                fastcgi_param   DOCUMENT_ROOT       $document_root;
                fastcgi_param   SERVER_PROTOCOL     $server_protocol;

                fastcgi_param   GATEWAY_INTERFACE   CGI/1.1;
                fastcgi_param   SERVER_SOFTWARE     nginx/$nginx_version;

                fastcgi_param   REMOTE_ADDR         $remote_addr;
                fastcgi_param   REMOTE_PORT         $remote_port;
                fastcgi_param   SERVER_ADDR         $server_addr;
                fastcgi_param   SERVER_PORT         $server_port;
                fastcgi_param   SERVER_NAME         $server_name;

                # can use port as well
                fastcgi_pass  unix:/tmp/myapp-fcgi.sock;
        }
}

Start, Restart and Go

Start or restart nginx.

On debian: /etc/init.d/nginx restart

Startup your app server (make sure you'ave changed to /home/user/dancer_app):
plackup -E development -s FCGI -a bin/app.pl -S /tmp/myapp-fcgi.sock -D

See plackup for all the options. I added -D and usually add --error-log but a start up script is probably a better long term choice

After writing all this, i stumbled across a pretty helpful reference in the Plack docs on Plack::Handler::FCGI which replicates the fast-cgi nginx.conf above.

__END__