Cooking Perl with Chef Solo

David Golden

@xdg

configuration management

(e.g. chef, puppet, cfengine, ...)

unknown state

target state

new machine

deployed app

infrastructure as code

automated!

repeatable!

testable!

(no manual steps, checklist, etc.)

one tool to deploy the whole stack

(DB, caching, messaging, etc.)

chef ❤ ruby

(written in ruby; various ruby app stacks)

chef ❤ python

(virtualenv; pip; django apps)

chef ?? perl

(system perl and CPAN)

do you need:

  • a different perl?
  • versioned dependency sets?

we have tools for that

  • perlbrew
  • local::lib
  • carton
  • (others?)

demo task: deploy simple Plack app

  • app.psgi
  • lib/ZZZ/Hello/World.pm
  • Makefile.PL
  • carton.lock

app.psgi

use ZZZ::Hello::World;
my $app = sub { ZZZ::Hello::World->run_psgi(@_) };

lib/ZZZ/Hello/World.pm

package ZZZ::Hello::World;
our $VERSION = "1.0";

use Plack::Request;

sub run_psgi {
  my $self = shift;
  my $req = Plack::Request->new(shift);
  my $res = $req->new_response(200);
  $res->content_type('text/html');
  $res->body("Hello World (@{[scalar localtime]})");
  return $res->finalize;
}

1;

Makefile.PL

use inc::Module::Install;
name 'ZZZ-Hello-World';
version '1.0';

requires 'Plack';
requires 'Starman';

WriteAll;

carton.lock has versioned prereqs

(carton will install and use them at runtime)

$ carton install
$ carton exec -Ilib -- starman -D -p 80 app.psgi

step 1

teach chef about perlbrew and friends

quick chef glossary (1/2)

  • cookbook
  • recipe
  • resource
  • provider

quick chef glossary (2/2)

  • node
  • attributes

solution: a perlbrew cookbook

  • bootstrap perlbrew
  • perlbrew_perl — install a perl
  • perlbrew_lib — configure a local::lib
  • perlbrew_run — shell commands with specific perl

next: create a recipe for hello-world

warning!

ruby DSL ahead

recipe for hello-world (1/3)

# get perl and carton ready

include_recipe 'perlbrew'

perl_ver = node['hello-world']['perl_version']
perl_carton = "#{perl_ver}@carton"

perlbrew_perl perl_ver
perlbrew_lib perl_carton

perlbrew_run "cpanm Carton" do
  perlbrew perl_carton
end

recipe for hello-world (2/3)

# deploy the app from git

package 'git'

git "/opt/hello-world" do
  repository "git://github.com/dagolden/zzz-hello-world.git"
  reference node['hello-world']['deploy_tag']
end

recipe for hello-world (3/3)

# install deps and fire it up

perlbrew_run "carton install" do
  cwd "/opt/hello-world"
  perlbrew perl_carton
end

perlbrew_run "carton exec -Ilib -- starman -D -p 80 app.psgi" do
  cwd "/opt/hello-world"
  perlbrew perl_carton
end

step 2

deploy to a (virtual) machine

chef vs chef solo

Chef vs chef solo

rsync/ssh is manual

(plus different data for each node)

(needs automation tools)

pocketknife (ruby)

littlechef (python)

I didn't like them

solution: pantry (perl)

  • pantry init
  • pantry create node
  • pantry edit node
  • pantry sync node

deploying hello-world (1/2)

$ mkdir my-pantry
$ cd my-pantry
$ git init
$ pantry init

(copy in perlbrew and hello-world cookbooks)

$ git add .
$ git commit -m "added cookbooks"
$ pantry create node xdgvm.dyndns.org
$ pantry edit node xdgvm.dyndns.org

xdgvm.dyndns.org.json (node config file)

{
   "name" : "xdgvm.dyndns.org",
   "run_list" : ["recipe[hello-world]"],
   "hello-world": {
      "perl_version": "perl-5.14.2",
      "deploy_tag":   "v1.0"
   }
}

xdgvm.dyndns.org.json (advanced)

{
   "name" : "xdgvm.dyndns.org",
   "run_list" : ["recipe[perlbrew]", "recipe[hello-world]"],
   "perlbrew" : {
      "perls" : [ "perl-5.14.2", "perl-5.12.3", "perl-5.10.1" ],
      "install_options" : "-D usethreads"
   },
   "hello-world": {
      "perl_version": "perl-5.14.2",
      "deploy_tag":   "v1.0"
   }
}

deploying hello-world (2/2)

$ git add .
$ git commit -m "configured xdgvm.dyndns.org"
$ git tag release-1.0 -m "deploy hello-world 1.0"

(note: versioned configuration!)

$ pantry sync node xdgvm.dyndns.org

(get a cup of coffee while you wait)

it works!

Hello World

chef <3 perl (now)

still missing some things

  • better plack automation (e.g. restart server on update)
  • better carton automation (e.g. reinstalling dependencies when carton.lock changes)
  • better app deployment (e.g. parameterized git checkout)
  • more pantry features (e.g. avoid editing raw JSON)
  • ...

chef ❤ perl (soon)