Update 1/27/07: MT 3.34 is out with improved FastCGI support and between that and some experience, I’ve tweaked my setup some. I need to write up a better set of instructions, but right now my computer is in for repairs and I’m not up to writing a lengthy entry on a borrowed computer right now. The main things that I’ve changed are that I’m now having LightTPD spawn only one dispatch process and I’ve added one important line to the dispatch script as follows:
Just inside the eval block right above the line that reads: while (my $q = new CGI::Fast) {
I’ve added in require MT;
and right above the line that reads: $app->init_request(CGIObject => $q) unless $app->{init_request};
I’ve added the line: MT->set_instance($app);
These changes are to more closely match how the bootstrap module handles starting up a script and adding that line seems to allow comments to be reliably published through FastCGI.
However I am still getting periodic out of memory errors when I do more than a couple of big actions in a row such as a rebuild a comment and another rebuild. That makes me worry about what will happen as the blog grows.
Original text below:
Since it seems to be a bit of a moving target (and just so I remember myself), I thought I’d share what I did in order to get my Movable Type installation running with LightTPD and FastCGI at TextDrive. Needless to say, your milage may vary, especially at other hosts (and really I’m not sure that any other host will let you make this setup).
Why did I do this and was it worth it?
If you want to skip the background and get straight to the step by step instructions skip to the extended entry.
Unfortunately the real answer is probably a not so high minded “because I could” or “to see if I could” but in general if there’s a better way to do things, I like to at least try to take advantage of it. To be certain, I’m seeking to solve a problem that I don’t suffer from all that much. The problem being that standard CGI is slow. Every request for the CGI program will cause the perl (in this case) script to be compiled and loaded into memory even if you just used the same program a few seconds ago. This means that the user waits and the server gets the load of repeatedly starting new processes. It’s easy to see how a stateless protocol like http would encourage this, but it can suck, especially when your blog gets hammered by spammers (or users for that matter) generating a huge number of requests to the comment or trackback script in quick succession. Even in normal operation, it simply slows things down and is one reason some people may find Movable Type less appealing than blogging applications which take a different approach, such as using php. The “standard” approach for fixing this in recent years has been to compile a script interpreter into Apache via a module like mod_php, but for a number of reasons that I don’t entirely understand, mod_perl is not as secure or common as mod_php. FastCGI seeks to remedy this problem by starting one or more persistent processes which start up once and handle requests for scripts, staying active between requests. Unfortunately, Apache’s implementation of FastCGI isn’t very good (again for reasons that I do not entirely understand), however LightTPD was designed with good FastCGI support built in. Some people may favor using LightTPD anyway, even for serving static content because it apparently scales much better than Apache for handling high levels of traffic without requiring unreasonable amounts of resources.
So… Was it worth it? Well, as I said, I have neither a high traffic site nor one that has so far attracted (much) spamming, but the second at least is inevitable with time, and who knows, I could see more traffic someday. The bottom line, though, is that where I see the most difference is in the responsiveness of the application, and there you really can tell the difference. You click and the new page pops open. To be sure, there are still delays sometimes, and it isn’t as if the rebuilds have gone away, but I never, anymore, have to wait for a link to open longer than I would expect from any static website.
Disadvantages? Well, anytime you stray out of the standard or increase the complexity of your setup, you do risk something unforeseen going wrong. You should probably keep a bit of a closer eye on things set up this way than you would otherwise. At first I did have a couple of major ongoing complaints with my setup: If you want to avoid having the port number in all of your links to the Movable Type application, you will need to proxy through from Apache to LightTPD which, in my hands anyway, causes Movable Type to see all incoming connections to be from localhost, confusing the spam filters. Also, the proxy setup means that you can’t adjust mod_security yourself (you’d have to file a ticket to get them to disable it or modify the rules for you). Because of these problems, I’ve scrapped the proxy setup and simply have links to the application go directly to my LightTPD port. I’m waiting to hear from anyone about whether this could be a bad idea for some reason, but I’m happy so far.
Update 12/10/06: Thanks to a comment from Brad Choate, I’ve now fixed the problem with Movable Type seeing the wrong IP address and so I am now using the proxying method instead of pointing directly to the LightTPD port, and I’ve described that setup below.
Ok, here’s the goods. Read on for the detailed version of how I got things set up:
Before we begin
Before we begin we’re going to need to decide where Movable Type is going to live. It simplifies the setup if Movable Type lives in your main web/public/
directory rather than your cgi-bin
so if you don’t have it there, I recommend moving it. This shouldn’t be much of a hardship. TextDrive doesn’t mind you keeping script files in your main web root (and with php, we’ve already pretty much crossed that bridge anyway) and the only people who should be holding on to URLs that link to your Movable Type setup are you, so you shouldn’t be breaking any links. The only potential exception to this is the feature that allows a user to perform a search and hold on to an xml feed of the results. If for that—or any—reason you would like to keep your mt
directory in your cgi-bin
you’ll have to figure out how to map your cgi-bin
into space that LightTPD can see.
Alternatively, if broken links are your only concern, you may be able to use mod_rewrite to redirect requests from the old location to the new one. The code in your .htaccess
file would probably look something like this:
RewriteEngine On
RewriteBase /cgi-bin/
RewriteRule ^mt-search.cgi http://your.blog.com/mt/mt-search.fcgi [R]
For the sake of this article, I’ll be assuming that your mt
directory is at web/public/mt/
.
Step 1: familiarize yourself with TextDrive’s approach.
While there isn’t much in the way of documentation about Movable Type, TextDrive does provide a good tutorial for setting up Rails with LightTPD and FastCGI. Go read that. In the tutorial they provide you with a few important skeleton configuration files for you to copy and edit to match your situation. We’ll be editing them a bit more than usual to make them suitable for movable type, but the basics are all there. TextDrive has broken things up to be nicely modular, allowing you to easily host multiple applications with a single LightTPD setup, so there’s a configuration file for LightTPD itself and a configuration file for your application. Furthermore, they encourage you to spawn your FastCGI process external to LightTPD, however I’ve found for Movable Type it’s better not to do that, and I think some of the reasons why you would want to don’t apply to us here. It’ll become clearer what all this means as we go along.
Step 2: Setting up LightTPD
Go to the knowledge base tutorial section on setting up LightTPD follow their instructions. For this step we don’t need to deviate at all:
1. First request a port from TextDrive for your connection to LightTPD. Do this by submitting a ticket. Also let them know what you are intending to do. They let you do things here that no other host I know of would, so it’s only fair to keep them posted.
2. Create the following directories:
~/var/
~/var/log/
~/var/run/
~/etc/rc.d/
~/etc/lighttpd/
~/etc/lighttpd/vhosts.d/
Do this at the base of your account directory even if you are running your blog from a domain in your ~/domains
directory. This is a global configuration, and you’ll adjust where specific applications live later.
3. Download the lighttpd.conf file from them and edit it replacing USERNAME with your TextDrive username, PORTNUMBER with the port number you just got, and APPNAME with “mt” or something else suitable.
Here’s what the file looks like as of now:
### Lighttpd Configuration File
#-- Lighttpd modules
server.modules = ( "mod_rewrite",
"mod_redirect",
"mod_access",
"mod_cgi",
"mod_fastcgi",
"mod_compress",
"mod_accesslog" )
#-- CGI configuration
cgi.assign = ( ".pl" => "/usr/local/bin/perl",
".cgi" => "/usr/local/bin/perl" )
#-- Mimetypes
include_shell "cat /usr/local/etc/lighttpd_mimetypes.conf"
#-- Default domain
#
# Replace USERNAME with your TextDrive user name, PORTNUMBER with
# your assigned port number (you'll need only one).
server.username = "USERNAME"
server.port = PORTNUMBER
server.groupname = server.username
var.base = "/users/home/" + server.username
server.document-root = base + "/web/public/"
server.pid-file = base + "/var/run/lighttpd.pid"
server.tag = "Lighttpd | TextDriven"
server.indexfiles = ( "index.php", "index.html",
"index.htm", "default.htm" )
url.access-deny = ( "~", ".inc", ".ht" )
#-- Logging
accesslog.filename = base + "/var/log/lighttpd.access.log"
server.errorlog = base + "/var/log/lighttpd.error.log"
#-- VHOSTS
#
# Uncomment the line bellow (remove the #) and replace APPNAME with
# the name of your Rails Application.
# If you have more than one Rails application, you'll need to add
# another include "vhosts.d/APPNAME.conf" and replace APPNAME with
# for each of your applications.
# include "vhosts.d/APPNAME.conf"
You should uncomment the last line and if you are going to run multiple applications this way you should duplicate it giving each application a different name.
Step 3. Setting up your rails application Movable Type
1. Go to the next section of the knowledge base tutorial and download the “APPNAME.conf” file. This is the file you will edit to get LightTPD to interface with Movable Type.
Here’s the source of the skeleton APPNAME.conf file as it is now:
### Rails Application Configuration File
#
# Replace yourdomain with the name of your domain, and APPNAME with the
# name of your Rails application.
#
# If you're Rails application is running under a secondary domain for your
# account, then 'server.document-root' should look something like this:
#
# base + "/domains/secondarydomain.tld/web/public/"
#
$HTTP["host"] =~ "(www\.)?yourdomain\.com" {
server.document-root = base + "/web/public/"
server.error-handler-404 = "/dispatch.fcgi"
fastcgi.server = (
".fcgi" => ( "localhost" => ( "socket" => base + "/var/run/APPNAME-0.socket" ) )
### PHP
# Uncomment everything bellow (remove the #'s) to enable PHP for your domain. Each of
# your domains/APPNAME.conf files will use the same php-fastcgi.socket, so you
# won't need to rename it as above.
# If you've switched your account to use php4, you need replace 'php5-fcgi'
# with 'php4-fcgi'
# ,".php" =>
# ( "localhost" =>
# (
# "socket" => base +"/var/run/php-fastcgi.socket",
# "bin-path" => "/usr/local/www/cgi-bin/php5-fcgi",
# "bin-environment" =>
# ( "PHP_FCGI_CHILDREN" => "1", "PHP_FCGI_MAX_REQUESTS" => "2500" ),
# "min-procs" => 1,
# "max-procs" => 1,
# "max-load-per-proc" => 4,
# "idle-timeout" => 20
# )
# )
)
}
This is the part to focus on:
$HTTP["host"] =~ "(www\.)?yourdomain\.com" {
server.document-root = base + "/web/public/"
server.error-handler-404 = "/dispatch.fcgi"
fastcgi.server = (
".fcgi" => ( "localhost" => ( "socket" => base + "/var/run/APPNAME-0.socket" ) )
2. Like they tell you to in the tutorial, change “yourdomain” in the line $HTTP["host"] =~ "(www\.)?yourdomain\.com" {
to your domain name. For instance mine reads: $HTTP["host"] =~ "(www\.)?blog.tapirtype\.com" {
. This tells LightTPD that for requests to your domain, it should use the following configuration variables (in my case I probably don’t need the (www\.)?
part because I’m working with a subdomain, but it doesn’t hurt.
The next line is where you tell LightTPD where the root of your domain is in the local filesystem. “base” is just a variable defined in the main lighttpd.conf file that points to your home directory. So if you are using your primary domain (the one you told TextDrive about when you first signed up) you can leave this line as is. However, if you are using a domain in your ~/domains/
directory like I am, you need to change this line to point to your /web/public/
directory there like: server.document-root = base + "/domains/blog.tapirtype.com/web/public/"
in my case.
3. The next line re-routs not found errors to your application so it can do URL rewriting, but since Movable Type won’t know what to do with this, comment this line out.
4. Next is where the action is. Turn to Brad Choate’s instructions on how to get Movable Type 3.2 working with LightTPD and FastCGI. His lighttpd.conf setup looks like this:
fastcgi.server = ( ".fcgi" =>
( "localhost" =>
(
"socket" => "/path/to/lighttpd-fcgi.socket",
"bin-path" => "/path/to/mt/dispatch.fcgi",
"bin-environment" => ( "PERL5LIB" => "/path/to/mt/lib",
"MT_HOME" => "/path/to/mt",
"MT_CONFIG" => "/path/to/mt-config.cgi" )
)
)
)
This line is configuring the FastCGI setup for this domain, saying (line by line) that it applies to files ending in “.fcgi”, will run on localhost, will communicate with the program using a socket file stored here, will run the program stored here which will handle requests, and will set the following environment variables when it runs the program. The program we’ll be using is (very slightly modified) the dispatch file Brad Choate gives further down, which will wait for requests from the server and start a new FastCGI process, farming them off to the correct parts of the Movable Type application. (More on that in a minute.)
5. Mash them together and you get my setup which looks like this:
### Rails Application Configuration File, modified for Movable Type
#
# Replace YOURDOMAIN with the name of your domain, and APPNAME with the
# name of your Rails application.
#
# If you’re Rails application is running under a secondary domain for your
# account, then 'server.document-root' should look something like this:
#
# base + "/domains/secondarydomain.tld/web/public/"
#
$HTTP["host"] =~ "(www\.)?blog\.tapirtype\.com" {
server.document-root = base + "/domains/blog.tapirtype.com/web/public/"
# server.error-handler-404 = "/dispatch.fcgi"
fastcgi.server = ( ".fcgi" =>
( "localhost" =>
( "socket" => base + "/var/run/mt-0.socket",
"bin-path" => base + "/domains/blog.tapirtype.com/web/public/mt/dispatch.fcgi",
"bin-environment" => (
"PERL5LIB" => base + "/domains/blog.tapirtype.com/web/public/mt/lib",
"MT_HOME" => base + "/domains/blog.tapirtype.com/web/public/mt",
"MT_CONFIG" => base + "/domains/blog.tapirtype.com/web/public/mt/mt-config.cgi"
),
"min-procs" => 1,
"max-procs" => 2,
"max-load-per-proc" => 4,
"idle-timeout" => 20
)
)
### PHP
# Uncomment everything bellow (remove the #'s) to enable PHP for your domain. Each of
# your domains/APPNAME.conf files will use the same php-fastcgi.socket, so you
# won't need to rename it as above.
# If you've switched your account to use php4, you need replace 'php5-fcgi'
# with 'php4-fcgi'
# ,".php" =>
# ( "localhost" =>
# (
# "socket" => base +"/var/run/php-fastcgi.socket",
# "bin-path" => "/usr/local/www/cgi-bin/php5-fcgi",
# "bin-environment" =>
# ( "PHP_FCGI_CHILDREN" => "1", "PHP_FCGI_MAX_REQUESTS" => "2500" ),
# "min-procs" => 1,
# "max-procs" => 1,
# "max-load-per-proc" => 4,
# "idle-timeout" => 20
# )
# )
)
}
6. I’ve added a few directives below Brad Choate’s code to control how many FastCGI processes LightTPD dynamically spawns. Without these settings (in my hands), LightTPD will immediately spawn four instances of the dispatch.fcgi script, so as a compromise, I’ve used the “max-procs” directive to limit it to two processes, allowing it to simultaneously process, say, a long rebuild and a search on the blog, but on the other hand not sucking up virtual memory with needless instances of the process. I’ve also kept in, but left commented out the code to get php working through LightTPD. I don’t need it because, Apache will be serving everything but the application, but for flexibility’s sake, I’ll leave it in.
Update 12/10/06: Thanks to Brad Choate’s suggestion I’m now proxying from Apache to LightTPD rather than making the links point directly to the LightTPD port, so this is a good time to set that up:
7 To get Apache to proxy requests to your mt
directory over to your LightTPD port, we’ll use a variation on the instructions in the tutorial here. Just as the tutorial says, login to webmin and go to “Apache Webserver” in the “servers” menu and click on the Virtual Server that you want to edit (the one that is hosting Movable Type). Go to “Aliases and Redirects.”
This is where we diverge slightly from the tutorial: Under “Map local to remote URLs” enter “/mt” (rather than just “/” as the tutorial instructs) in the “Local URL path” field. Under “Remote URL” click the radio button so that instead of pointing to “None” it points to the empty text box. In that box type “http://127.0.0.1:PORT/mt” where PORT is your port number for LightTPD (rather than simply typing “http://127.0.0.1:PORT/”). Apache should now forward all requests to http://your.blog.com/mt
to LightTPD.
Step 4. The dispatch.fcgi file
1. Brad Choate’s dispatch.fcgi file reads as follows:
#!/usr/bin/perl -w
use strict;
use MT::Bootstrap;
use CGI::Fast;
# preload app packages
use MT::App::CMS;
use MT::App::Comments;
use MT::App::Trackback;
use MT::App::Search;
## uncomment if necessary, but this adds a lot of
## overhead since it loads up LibXML.
##use MT::AtomServer;
my $handlers = {
'mt.fcgi' => { class => 'MT::App::CMS', name => 'AdminScript' },
'mt-comments.fcgi' => { class => 'MT::App::Comments', name => 'CommentScript' },
'mt-tb.fcgi' => { class => 'MT::App::Trackback', name => 'TrackbackScript' },
'mt-search.fcgi' => { class => 'MT::App::Search', name => 'SearchScript' },
## See note above about this...
## 'mt-atom.fcgi' => { class => 'MT::AtomServer', name => 'AtomScript' },
};
eval {
while (my $q = new CGI::Fast) {
my $cgi = $q->script_name;
$cgi =~ s!.*/!!;
my $pkg = $handlers->{$cgi}{class};
die "Invalid handler for $cgi" unless $pkg;
my $app = $pkg->new(CGIObject => $q) or die $pkg->errstr;
local $SIG{__WARN__} = sub { $app->trace($_[0]) };
$app->init_request(CGIObject => $q) unless $app->{init_request};
fixup_script_names($app);
$app->run;
my $mode = $app->mode || '';
if ("$pkg->$mode" eq 'MT::App::CMS->plugin_control') {
exit; # allows server to recycle after changing plugin switches
}
}
};
if ($@) {
print "Content-Type: text/html\n\n";
print "Got an error: $@";
}
sub fixup_script_names {
my ($app) = @_;
$app->config($handlers->{$_}{name}, $_) foreach keys %$handlers;
}
2. The only thing I’m going to change is to add a handler for the Activity Feeds that were introduced after he wrote this post. All we need to do to achieve that is to add the following lines: use MT::App::ActivityFeeds;
and 'mt-feed.fcgi' => { class => 'MT::App::ActivityFeeds', name => 'ActivityFeedScript' },
so that the whole thing now looks like this:
Update on 12/10/06: Brad Choate suggested a further change that allows Movable Type to see the originating IP address of requests when they are proxied from Apache to LightTPD. I’ve updated the dispatch.fcgi
below to include that code.
#!/usr/bin/perl -w
use strict;
use MT::Bootstrap;
use CGI::Fast;
# preload app packages
use MT::App::CMS;
use MT::App::Comments;
use MT::App::Trackback;
use MT::App::Search;
use MT::App::ActivityFeeds;
## uncomment if necessary, but this adds a lot of
## overhead since it loads up LibXML.
##use MT::AtomServer;
my $handlers = {
'mt.fcgi' => { class => 'MT::App::CMS', name => 'AdminScript' },
'mt-comments.fcgi' => { class => 'MT::App::Comments', name => 'CommentScript' },
'mt-tb.fcgi' => { class => 'MT::App::Trackback', name => 'TrackbackScript' },
'mt-search.fcgi' => { class => 'MT::App::Search', name => 'SearchScript' },
'mt-feed.fcgi' => { class => 'MT::App::ActivityFeeds', name => 'ActivityFeedScript' },
## See note above about this...
## 'mt-atom.fcgi' => { class => 'MT::AtomServer', name => 'AtomScript' },
};
eval {
while (my $q = new CGI::Fast) {
my $cgi = $q->script_name;
$cgi =~ s!.*/!!;
my $pkg = $handlers->{$cgi}{class};
die "Invalid handler for $cgi" unless $pkg;
## fix from Brad Choate so MT can see originating ip when proxied.
if ($ENV{'REMOTE_ADDR'} eq '127.0.0.1') {
# Select last value in the chain -- original client's ip
if (my ($ip) = $ENV{HTTP_X_FORWARDED_FOR} =~ /([^,\s]+)$/) {
$ENV{REMOTE_ADDR} = $ip;
}
}
my $app = $pkg->new(CGIObject => $q) or die $pkg->errstr;
local $SIG{__WARN__} = sub { $app->trace($_[0]) };
$app->init_request(CGIObject => $q) unless $app->{init_request};
fixup_script_names($app);
$app->run;
my $mode = $app->mode || '';
if ("$pkg->$mode" eq 'MT::App::CMS->plugin_control') {
exit; # allows server to recycle after changing plugin switches
}
}
};
if ($@) {
print "Content-Type: text/html\n\n";
print "Got an error: $@";
}
sub fixup_script_names {
my ($app) = @_;
$app->config($handlers->{$_}{name}, $_) foreach keys %$handlers;
}
3. Save this file in your mt
directory, making sure to set its permissions so it is executable (in a terminal cd
to your mt
directory and run chmod 755 dispatch.fcgi
), and make sure that you pointed to the right place in your mt.conf
file. This dispatch file provides the one central process that FastCGI will keep running, pre-loading the application files into memory and waiting for LightTPD to forward requests for .fcgi files to it.
Step 5. Tweaking your Movable Type setup.
We’re almost done now, but Movable Type still needs a couple of changes in order to be able to use this setup.
Update 12/10/06: I’ve modified this section to reflect the fact that we’re proxying from Apache to LightTPD now.
1. As is said in Brad Choate’s post, the .fcgi
files referenced in the dispatch file need to exist, though they can be empty (Another option was mentioned in the comments, if you prefer). They just serve as targets for LightTPD to serve up to your dispatch.fcgi
file, so it will trigger the right parts of the application code. Actually if you look inside the .cgi
files you are replacing, they weren’t much more than that, either, simply using the MT::Bootstrap
module to launch the appropriate scripts. You can create these files from the shell using touch
, e.g.:
cd ~/path/to/mt/
touch mt.fcgi
touch mt-comments.fcgi
touch mt-tb.fcgi
touch mt-search.fcgi
touch mt-feed.fcgi
Where ~/path/to/mt/
is the path from your home directory to your mt
directory.
I don’t know if it’s necessary, but I figured they should be executable as well, so in addition I ran: chmod 755 mt.fcgi
repeated for each of the new files.
2. While we’re talking about the MT::Bootstrap
module, this is a good time to replace your MT::Bootstrap
module with the new FastCGI aware one in the “Wheeljack” development branch of the Movable Type code, that Brad Choate mentions here. You can download it here, and the module is located in your mt
directory at: mt/lib/MT/Bootstrap.pm
. If you are using a future version of Movable Type (I’m writing with reference to MT 3.33), you might want to check to make sure that your existing Bootstrap.pm file isn’t already FastCGI aware. Just open it up and search for “FastCGI.” If the file mentions FastCGI, it should be the updated version and you can ignore this step.
This step isn’t necessarily required as the dispatch.fcgi
file should take care of starting the perl processes in a FastCGI aware way, however I’ve discovered that some things seem to work better with the new file. In particular, the unofficial MT-Akismet plugin written by Stepan Riha ceased to work for me until I upgraded to the new Bootstrap.pm file. Conversely, with this new file there may be a way to simplify this setup, shifting some of the responsibility of the dispatch.fcgi
file to the new MT::Bootstrap
module, but I’m not nearly savvy enough to understand all the ramifications, and this seems to work fine for me.
3. Next, Movable Type needs to know to link to the new .fcgi
files, so that trackback, comment, and search links as well as links within the application go to the new, nonstandard place. This is set in the mt-config.cgi
in your mt
directory, so open it up.
3.1 First make sure that your StaticWebPath
directive points correctly to your mt-static
directory (depending on your setup, you might have moved it when you moved your mt
directory out of the cgi-bin
).
3.2 Next you’ll need to make sure that CGIPath
points to your mt
directory in the right place (remember that you may have moved it out of your cgi-bin
and into your main web path).
CGIPath http://your.blog.com/mt/
This assumes that you are proxying requests for your mt
directory from Apache to LightTPD. If you prefer you can simply point to LightTPD directly:
CGIPath http://your.blog.com:PORT/mt/
Where PORT is your port number.
3.3 Finally you need to tell Movable Type to link to the non standard .fcgi
files by adding the following directives:
AdminScript mt.fcgi
CommentScript mt-comments.fcgi
TrackbackScript mt-tb.fcgi
SearchScript mt-search.fcgi
ActivityFeedScript mt-feed.fcgi
Step 6. Starting LightTPD
Movable Type should be all set up now, so the only thing left to do is to start up your LightTPD server and make sure everything is working.
1. TextDrive has a nifty little script for you to start, stop, and restart LightTPD and has documented it in their tutorial here. Go there and download the lighttpd.sh
file. Upload it to ~/etc/rc.d/
and use chmod
to set its permissions to 750
, so it is executable, but not to everyone.
2. Now’s the time to see if it’s working, so cd ~/etc/rc.d/
and run:
./lighttpd.sh start
You should see the response:
Starting Lighttpd
Now run:
ps aux
and you should see the following items listed (you may want to make sure you widen your terminal window so the lines don’t get cut off):
/usr/local/sbin/lighttpd -f /users/home/USERNAME/etc/lighttpd/lighttpd.conf
/usr/bin/perl -w /users/home/tapirtype/domains/YOUR.DOMAIN.COM/web/public/mt/dispatch.fcgi (perl5.8.7)
/usr/bin/perl -w /users/home/tapirtype/domains/YOUR.DOMAIN.COM/web/public/mt/dispatch.fcgi (perl5.8.7)
The items may not appear in this order. The first line I’ve shown is your instance of LightTPD and the second two are the two instances of your dispatch.fcgi
program. You’ll also see the amount of resident memory and virtual memory associated with each process, the processor time used, and the PID #.
If you got an error, check that your dispatch.fcgi file has execute permissions set and that you edited all the paths correctly in your configuration files.
3. Finally, direct your browser to your Movable Type application through your LightTPD port, e.g.: http://your.blog.com:PORT/mt/mt.fcgi
Make sure that you can log in and navigate around the administration interface.
4. You’ll need to rebuild your individual entry archives, indexes with search boxes, and any pages that link to the cgi
files. Test the waters by rebuilding one individual entry archive file to make sure everything goes fine, the go ahead and rebuild everything.
5. Assuming that everything works alright, it’s time to ensure that your LightTPD instance gets restarted when the server reboots, so follow the instructions in the Starting Lighttpd Tutorial for setting up a cron job (It’s at the bottom of the page) reproduced below:
1. Login to Webmin (https://webmin.server.textdrive.com/)
Note: You’ll need to substitute server with the name of the actual server for your account.
2. Under the System section, click Scheduled Cron Jobs
3. Click Create a new scheduled cron job.
In the Command field, enter the following (don’t forget to replace USERNAME with your username!):/users/home/USERNAME/etc/rc.d/lighttpd.sh start
Note: You may find it easier to copy this to a text editor first, make your changes, and then paste it into the Command field.
4. In the Description field, enter Lighttpd reboot (or something equally descriptive)
5. Under When to execute, select Simple schedule .. and then select When system boots from the adjacent drop down menu.
6. Click Create at the bottom of the page to create you cron job.
You should now see your newly created cron job listed in Scheduled Cron Jobs.
6. Congratulations! You should be done with the setup.
Some considerations to keep in mind
Bugs and memory leaks: I’ve noticed a few things, running Movable Type this way. First of all, Movable Type wasn’t designed from the ground up to be run persistently, and it looks like there are some memory leaks. If you check ps aux
periodically, you’ll notice that the virtual memory taken up by your dispatch.fcgi
processes only increases with time, never running back down (though the real memory will, fortunately), meaning that it may sooner, rather than later, hit the memory limit of 80 MiB of Virtual Memory and be killed. Started dynamically by LightTPD, a new process will be started immediately, with its memory back down.
If something goes wrong and your application becomes unresponsive, you may find you need to restart LightTPD using the lighttpd.sh
script. This should kill the dispatch processes and start new ones, but it’s a good idea to check by running ps aux
just to make sure that you don’t have a lingering dispatch process which you can kill by typing kill -9 PID
replacing PID with the process id number you got from ps aux
.
Update 12/10/06: I’ve had some problems with Movable Type complaining about out of memory errors sporadically and having to be restarted in order to keep working. In order to get around this and hopefully prevent prevent memory leaks from becoming a problem, I’m experimenting with having the dispatch.fcgi
process maintain a counter that will cause it to quit after a certain number of requests have been processed (right now I’ve got it set to 100). Since I’m not using external spawning, LightTPD happily starts up a new process as soon as the next request comes. Here’s the code I’m using in the dispatch.fcgi
file right now:
#!/usr/bin/perl -w
use strict;
use MT::Bootstrap;
use CGI::Fast;
# preload app packages
use MT::App::CMS;
use MT::App::Comments;
use MT::App::Trackback;
use MT::App::Search;
use MT::App::ActivityFeeds;
## uncomment if necessary, but this adds a lot of
## overhead since it loads up LibXML.
##use MT::AtomServer;
my $handlers = {
'mt.fcgi' => { class => 'MT::App::CMS', name => 'AdminScript' },
'mt-comments.fcgi' => { class => 'MT::App::Comments', name => 'CommentScript' },
'mt-tb.fcgi' => { class => 'MT::App::Trackback', name => 'TrackbackScript' },
'mt-search.fcgi' => { class => 'MT::App::Search', name => 'SearchScript' },
'mt-feed.fcgi' => { class => 'MT::App::ActivityFeeds', name => 'ActivityFeedScript' },
## See note above about this...
## 'mt-atom.fcgi' => { class => 'MT::AtomServer', name => 'AtomScript' },
};
my $killcount = 0;
eval {
while (my $q = new CGI::Fast) {
my $cgi = $q->script_name;
$cgi =~ s!.*/!!;
my $pkg = $handlers->{$cgi}{class};
die "Invalid handler for $cgi" unless $pkg;
## fix from Brad Choate so MT can see originating ip when proxied.
if ($ENV{'REMOTE_ADDR'} eq '127.0.0.1') {
# Select last value in the chain -- original client's ip
if (my ($ip) = $ENV{HTTP_X_FORWARDED_FOR} =~ /([^,\s]+)$/) {
$ENV{REMOTE_ADDR} = $ip;
}
}
my $app = $pkg->new(CGIObject => $q) or die $pkg->errstr;
local $SIG{__WARN__} = sub { $app->trace($_[0]) };
$app->init_request(CGIObject => $q) unless $app->{init_request};
fixup_script_names($app);
$app->run;
my $mode = $app->mode || '';
if ("$pkg->$mode" eq 'MT::App::CMS->plugin_control') {
exit; # allows server to recycle after changing plugin switches
}
# quit after 100 requests to prevent becoming bloated
$killcount++;
if ($killcount > 100) {
exit;
}
}
};
if ($@) {
print "Content-Type: text/html\n\n";
print "Got an error: $@";
}
sub fixup_script_names {
my ($app) = @_;
$app->config($handlers->{$_}{name}, $_) foreach keys %$handlers;
}
We’ll see how this holds up.
Optional: external spawning: You can spawn externally instead, if you wish, however there are some bugs associated with that. In particular, enabling or disabling a plugin will cause the Movable Type application to die and need to be restarted, if it is spawned externally. In order to spawn externally, you would modify the “rails.sh” script mentioned in the TextDrive Rails tutorial so it looks like this:
Note: Ok, Movable Type is eating the operative line in the code below and I can’t be bothered to figure out how to fix it right now, so suffice it to say that there’s supposed to be a line just like in the rails script except setting all the environment variables from the bin.env
directive in the mt.conf
file above instead of the RAILS_ENV
environment variable for the rails version. I’ll try to get it fixed eventually…
#!/bin/sh
### Script for starting your Movable Type based on TextDrive's script for starting Rails apps
# replace YOUR.DOMAIN.COM with your domain name and
# USERNAME with your username.
#
PERL5LIB='/users/home/USERNAME/domains/YOUR.DOMAIN.COM/web/public/mt/lib' MT_HOME='/users/home/USERNAME/domains/YOUR.DOMAIN.COM/web/public/mt' MT_CONFIG='/users/home/USERNAME/domains/YOUR.DOMAIN.COM/web/public/mt/mt-config.cgi' /usr/local/bin/spawn-fcgi -f /users/home/USERNAME/domains/YOUR.DOMAIN.COM/web/public/mt/dispatch.fcgi -s /users/home/USERNAME/var/run/mt-0.socket
I’ve shown a version for a subdomain setup as that’s how I’ve done it. For your main domain just delete /domains/YOUR.DOMAIN.COM
everywhere above.
Proxy issues:
Update 12/10/06: As I’ve noted repeatedly above, what I’m complaining about below is now fixed and I’ve switched back to having Apache proxy my mt
folder to LightTPD.
The remaining issue to keep in mind is how you direct requests to your Movable Type application to go through LightTPD port. I’ve settled on (and described above) the most direct method, simply making all the links point directly to the LightTPD port number. This is a bit crufty, but I don’t mind since I’m really the only one who uses the links with any frequency and I’m certainly the only one who holds on to them in bookmarks. The only user visible cruft will be in the trackback links and the URLs accessed when searching and commenting, and those are pretty crufty as it is. It also means that you are going to loose any protection from mod_security which, of course, is working through Apache. You may or may not care about this, especially because if you were proxying through from Apache instead, you’d loose all direct control over tweaking mod_security yourself without starting a support ticket.
The alternative is to set up a proxy though from Apache to LightTPD as they describe in the tutorial. You can either do it for the whole site as described in the tutorial, or you can proxy only the mt
directory over. Note that if you proxy the whole site, you will need to enable php under LightTPD if you have any php code on your site and you’ll need to replicate any rewrite rules, etc, that you had in your .htaccess
files in your lighttpd.conf
file.
Another method is to simply proxy only your /mt
directory to your LightTPD port and leave the rest of the site to Apache. You can do this by doing everything as the tutorial suggests except for using /mt
and http://127.0.0.1:XXXX/mt
instead of /
and http://127.0.0.1:XXXX/
.
Both of these methods have problems, however. In my hands this caused Movable Type to see all incoming actions as coming from 127.0.0.1
. This caused the spam filter to always see a mismatch between the originating IP and the host name for trackbacks, so I had to white-list 127.0.0.1
for any trackbacks to work and that meant that I lost all ability to judge whether trackbacks or comments were spam based on information from the originating IP. Because of this problem, I started having problems with spam for the first time, so I switched to using the direct method I’ve described above.
Conclusion
Well, it was a long time coming, and it’s probably too long winded, but here it is, my Movable Type FastCGI setup. Please leave a comment if you see anything that is inaccurate or that you think is unwise or if you try to follow these instructions and run into a snag.
That said, these instructions are provided as is, and I’m no expert, so don’t expect expert advise from me!
Happy hacking! Here’s to a snappier Movable Type experience!
Comments (12)
On December 6, 2006 12:40 PM,
Comment by Brad Choate:
Cool article. Sorry it's still so complicated... we're working on making it easier. Anyway, to counter the 127.0.0.1 issue, you might try amending your dispatch script. Within the 'while' loop that fetches the next CGI::Fast object, you should have something like this:
This should let MT see the actual IP address of the original request.
On December 6, 2006 1:04 PM,
Comment by John:
I have recently done this setup too, I was really glad to read your post. It gives me the idea I might have done it right.
You might want to add a line to the fastcgi.server socket description in your app.conf file. It will remove the need to create the empty files *.fcgi that Brad suggests. "check-local" => "disable" will cause lighttpd to service the requests without seeing if the files exist.
fastcgi.server = ( ".fcgi" =>
( "localhost" =>
( "socket" => "/var/run/mt-0.socket",
"bin-path" => "/mt/dispatch.fcgi",
"bin-environment" => (
"PERL5LIB" => "/mt/lib",
"MT_HOME" => "/mt",
"MT_CONFIG" => "/mt/mt-config.cgi"
),
"check-local" => "disable",
"min-procs" => 1,
"max-procs" => 2,
"max-load-per-proc" => 4,
"idle-timeout" => 20
)
)
Jay Allen talks about the bootstrap.pm module being important to MT. have used it to load the required modules in dispatch.fcgi...
http://jayallen.org/journey/2006/10/dreamhost_movable_type_and_fastcgi#comment-54604
use MT::Bootstrap App => 'MT::App::CMS';
use MT::Bootstrap App => 'MT::App::Comments';
use MT::Bootstrap App => 'MT::App::Trackback';
use MT::Bootstrap App => 'MT::App::Search';
## uncomment if necessary, but this adds a lot of
## overhead since it loads up LibXML.
##use MT::Bootstrap App => 'MT::AtomServer';
use MT::Bootstrap App => 'MT::App::ActivityFeeds';
use MT::Bootstrap App => 'MT::App::Upgrader';
I really don't know if it makes a difference, but since it was Jay, and it works I thought I would pass it on.
Thanks again for a great post. It would have saved me a lot of time, if only I had found it last week.
On December 6, 2006 6:42 PM,
Comment by Michael James Boyle:
Thanks for the comments!
Of course, as luck would have it, I just had one of my first big failures under this system. I found that John's comment wasn't published even though Movable Type thought that it was and when I tried to publish it I got an out of memory error. Eventually I had to manually kill the server and dispatch scripts and restart it. Then it worked fine. I guess the lesson is that you shouldn't attempt this if you aren't willing to do things like that from time to time. It makes me wonder if there's a way to automatically kill and restart Movable Type every day or so to keep it happy and under memory limits.
Anyway...
Brad: Thanks so much for the suggestion! I'll have to try that out, and I'll update if it works. I appreciate everything you're doing to get these things working.
John: I think that, for myself, it's a wash as far as which solution to the missing .fcgi files problem is more hackish... Since I already made them, and it's working, I don't think I'll stir the pot up by trying the fix.
As far as the link about using the MT::Bootstrap directly, avoiding the dispatch.fcgi mess... That's how I tried things first, but it resulted in server errors for me and when I asked for help I got a big thumbs down on using FastCGI through Apache from the TextDrive people.
Also, I want to reiterate, I am not an expert. I am barely even competent. I still don't entirely understand how FastCGI works. The dispatch.fcgi method makes sense to me, because it seems to match up well with the goal of FastCGI: Start one central, persistent process and pre-load libraries. Doing it by handling all .fcgi files through FastCGI, would each .fcgi get it's own persistent process? Would they start up when first requested and then stay active, or would they start up on a server restart and stay active from there? I'm a little more confused as to how that would work, probably because I haven't seen it in action myself.
As far as using MT::Bootstrap in the dispatch.fcgi script... Like I said, I don't really know enough to understand the differences between the methods. Maybe when I get a chance, I'll try it that way and see if there's a difference in stability, but it's working for me as is.
Thanks for the input!
On December 7, 2006 6:11 AM,
Comment by John:
I was unable to get Jay's instructions even close to working on lighttpd either. There does not seem to be a lighttpd equivalent to Apache's AddType directive for FastCGI handling of all *.fcgi. I'm pretty sure Jay relies on this Apacheism, even though he didn't mention it. I tried lots of things, nothing worked on lighttpd to handle perl like it is supported to handle *.php.
And it did seem to me too that there would be a lot of persistent processes around in that case...at least one for each command.
Like you, I found that Brad's instructions work fine. I threw in the bootstrap.pm way of loading because it works, and I am not sure what perl judo the SixApart developers may try there in the future. On the other hand, if it ain't broke, there's no reason for you to fix it now. (I did not get the new "wheeljack" branch bootstrapper either; I like to run "out of the box" MT.)
You use "hackish" like its an insult. [grin] You correctly state that in your configuration, the *.fcgi files are never read anyway. Lighttpd starts one handler to handle all those different script requests (actually two single handler processes in this case).
In your configuration lighttpd checks to see if the file exists before handing the request off to the dispatch.fcgi anyway. Your dispatch.fcgi handles loading all the modules and applications.
"check-local" is a configuration directive the use of which is described in the manual. By disabling check-local lighttpd does not bother to check for existence of a local *.fcgi (which it won't use anyway), it just hands the request off directly to dispatch.fcgi.
http://lighttpd.net/documentation/fastcgi.html
Error handling might be one difference here. In your case it is up to lighttpd to throw a 404 error. If I try to access a non-existent fcgi, the dispatch script handles it...none too gracefully.
Please let us know if you figure out the issue with with comments. Thanks again for sharing your information. In my experience, the difference between barely competent and expert is a matter of doing ... which you are.
On December 7, 2006 10:28 AM,
Comment by Michael James Boyle:
Hey, I certainly didn't mean to be insulting or derogatory with the "hackish" comment!
I just meant that I was trying to figure out what the most elegant method was, and I felt it was a tossup. I have no more and no less of a problem with having empty targets versus telling LightTPD to handle everything through the dispatch file, that's all I was trying to say. In fact, the more I think about it, the more I'm ok with Brad's/my method of having the empty targets do the mapping. I'd just assume have a 404 error than crash the dispatch.fcgi script.
The whole thing's pretty hackish as it is, but I don't really mind. After all, I sought this out as a hobby project to have an opportunity to hack things together. Eventually I'm sure that Six Apart will come up with a more elegant solution. In the mean time, I think that this method isn't as difficult as it looks, and as I said, I like being under the hood a bit.
As far as the comments... I haven't had a chance to try Brad's suggestion. As far as why it doesn't seem to be liking your comments? Well... I don't know what's up, but this time Akismet seemed to think your comment was spam (go figure). I probably need to adjust how the spam filters react to people who have authenticated because there's no reason it should have marked you as spam since you authenticated!
On December 7, 2006 10:34 AM,
Comment by Michael James Boyle:
Uh, oh... I got another out of memory error trying to comment just now... Again, MT needed to be restarted (all I had to do was kill the currently active dispatch.fcgi process). It's looking more and more like having MT restart periodically would be a good idea. I'll look into it when I have time and report back...
On December 7, 2006 12:48 PM,
Comment by John:
Check the source of my previous comment... I indicated I was kidding about hackish by putting a grin in there, but MT probably thought it was malicious XHTML.
<GRIN>
On December 7, 2006 1:00 PM,
Comment by Michael James Boyle:
No worries! (I went in and changed it so the grin should show up now...)
Of course, your comment wasn't published again, so I'm starting to worry about what's going on with the comments not showing up when Movable Type clearly thinks they are. This time all I had to do was go in and re-save the comment.
Are you getting an out of memory error when you try to post the comment?
On December 9, 2006 7:21 PM,
Comment by Michael James Boyle:
Nice!
Brad Choate's suggestion for solving the not seeing the right originating IP when proxying from Apache to LightTPD seems to work. I'll update the instructions above to reflect that as soon as I get a chance.
On December 10, 2006 5:56 PM,
Comment by Michael James Boyle:
I've updated the article to reflect Brad Choate's suggestion and am now having Apache proxy my mt directory to LightTPD.
I also added a comment about using a counter to kill the dispatch.fcgi script periodically, hopefully avoiding memory leak or bloat issues. It's working so far.
On December 23, 2006 12:05 AM,
Comment by Geoff:
Wow. That looks hard. I was thinking about trying Lighttpd on a site or two of mine at TxD, but you've cured me of that. Good luck!
On December 23, 2006 2:44 PM,
Comment by Michael James Boyle:
Geoff--
Don't say that! It really isn't as hard as it looks. I'm just still ironing out a few kinks. I'm very happy with the results except for commenting, and I don't know the deal with that.
I really need to rewrite this article in a simpler fashion. Unfortunately I started writing it before I'd completely figured it out for myself, so the confusion shows through a bit. I'll put up a striped down step by step instruction when I get the chance. (Hopefully in the next couple of weeks.)