Perl Application Bundling

I’ve been fairly obsessed with 12 Factor Applications lately. I’ve noticed lots of tools for bundling applications with Ruby (bundler is fairly ubiquitous), and a few other major languages, but nothing so explicit with Perl. Many developers that I have worked with or talked to haven’t done application bundling in Perl, but I’m interested to see better solutions than the one I’m about to offer should anyone want to respond.

Some things that I looked at but ultimately failed (or that I didn’t understand) included Module::Install auto_bundle and App::Bundle.

So, without getting into too many details, I’m going to fully bundle a perl application. In my experience, applications bundled this way have been able to run on default freshly created EC2 Ubuntu 12.04 and Virtual Box images created by vagrant (precise64).

Step one, create a Catalyst application. On your build box (where you might have installed the necessary Catalyst development packages via CPAN or apt), go ahead and execute:

$ catalyst.pl BundleTest

To create a new Catalyst application. Now, I’m not going to do any work to make it pretty, but I want it to follow 12 Factor as closely as possible, so I’m going to make it bundle Plack and Starman, so that it will open it’s own port. So, I open the Makefile.PL and I add two lines:

requires 'Plack';
requires 'Starman';

Because of our good fortune, this is already using Module::Install (because I don’t know Dist::Zilla or any of the other ones super well), so that is really all we need to do.

Next, I’m going to create the necessary Makefile by running:

$ perl Makefile.PL

A lot of times this step will fail because of the missing ‘inc/Module/Install.pm’ message. This can be corrected by installing Module::Install, but at this time you might also want to snag cpanminus, which is going to make the whole thing much easier (In fact, it’s highly unlikely that you’ll use cpan again after using it a few times):

$ sudo cpan App::cpanminus
$ sudo cpanm Module::Install

You should get output that looks like the following:

psalcido@enemy:~/Documents/devel/BundleTest$ perl Makefile.PL
include /home/psalcido/Documents/devel/BundleTest/inc/Module/Install.pm
include inc/Module/Install/Metadata.pm
include inc/Module/Install/Base.pm
include inc/Module/Install/Makefile.pm
Cannot determine perl version info from lib/BundleTest.pm
include inc/Module/Install/Catalyst.pm
include inc/Module/Install/Include.pm
include inc/File/Copy/Recursive.pm
*** Module::Install::Catalyst
Please run "make catalyst_par" to create the PAR package!
*** Module::Install::Catalyst finished.
include inc/Module/Install/Scripts.pm
include inc/Module/Install/AutoInstall.pm
include inc/Module/AutoInstall.pm
*** Module::AutoInstall version 1.06
*** Checking for Perl dependencies...
[Core Features]
- Test::More                       ...loaded. (0.98 >= 0.88)
- Catalyst::Runtime                ...loaded. (5.90015 >= 5.90015)
- Catalyst::Plugin::ConfigLoader   ...missing.
- Catalyst::Plugin::Static::Simple ...missing.
- Catalyst::Action::RenderView     ...missing.
- Moose                            ...loaded. (2.0604)
- namespace::autoclean             ...loaded. (0.13)
- Config::General                  ...loaded. (2.50)
- Plack                            ...loaded. (0.9989)
- Starman                          ...missing.
==> Auto-install the 4 mandatory module(s) from CPAN? [y] 
*** Dependencies will be installed the next time you type 'make'.
    (You may need to do that as the 'root' user.)
*** Module::AutoInstall configuration finished.
include inc/Module/Install/WriteAll.pm
include inc/Module/Install/Win32.pm
include inc/Module/Install/Can.pm
include inc/Module/Install/Fetch.pm
Warning: prerequisite Catalyst::Action::RenderView 0 not found.
Warning: prerequisite Catalyst::Plugin::ConfigLoader 0 not found.
Warning: prerequisite Catalyst::Plugin::Static::Simple 0 not found.
Warning: prerequisite Starman 0 not found.
Writing Makefile for BundleTest
Writing MYMETA.yml and MYMETA.json
Writing META.yml

Now that you have your META files, which include all of your requirements, you should be able to build your bundle using cpanminus, using the following commands:

$ cpanm -L extlib --installdeps .

This command demands that the dependencies for this application be installed to the directory extlib, regardless if they have already been installed in the system (-L does the full localization). If you are looking for speed, you can also include the –notest option.

After everything has been installed, we need to tar our installation:

$ make manifest
$ make tardist

The resulting tar file can now be deployed and exploded on a new box and executed with a simple command like the following (from the root directory of the application):

$ perl -I extlib/lib/perl5 -I lib extlib/bin/plackup -s Starman bundletest.psgi

I feel pretty comfortable expecting that the programming language be on the box we are trying to execute this code on, so I think that this is as far as we need to go with the bundle. The tar file can be pushed to s3, or some other global store, and pulled down and executed on a new box really quickly.

2 comments

  1. We’ve been pretty successful by compiling a catalyst app via B::C, which makes it faster (native app, zero startup time), but just for bundling (and longer startup-time) Par::Dist should also work.

  2. Since this, carton has become a big thing. It was important for an app that I was working on at the time, so I wrote this up.

Leave a comment