cooking perl with chef

126
Cooking Perl with Chef David Golden @xdg http://perlchef.com/ July 2012

Upload: david-golden

Post on 06-May-2015

3.789 views

Category:

Technology


2 download

DESCRIPTION

Reliable and scalable applications need repeatable, automated application deployment. Configuration management tools like Chef, Puppet and others make it easy to deploy an entire application stack, but support for Perl applications has lagged behind other popular, dynamic languages. The Perl community has responded to these challenges with tools like perlbrew, local::lib, carton and others to make it easier to manage an application and its dependencies in isolation. This presentation will show you how to make those tools work with Chef for complete automation of Perl application deployment.

TRANSCRIPT

Page 1: Cooking Perl with Chef

Cooking Perl with Chef

David Golden@xdg

http://perlchef.com/

July 2012

Page 2: Cooking Perl with Chef

ConfigurationManagement

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

Page 3: Cooking Perl with Chef

Unknown state

↓Target state

Page 4: Cooking Perl with Chef

New machine

↓Deployed app

Page 5: Cooking Perl with Chef

Infrastructure as code

automated!repeatable!

testable!

(no manual steps, checklist, etc.)

Page 6: Cooking Perl with Chef

One tool to deploythe whole stack

(DB, caching, messaging, ...)

Page 7: Cooking Perl with Chef

But wait!Isn't that hard to do for Perl apps?

Page 8: Cooking Perl with Chef

Perl applications are complex

Page 9: Cooking Perl with Chef

Dependency hell

Page 10: Cooking Perl with Chef

App = perl + CPAN + your code

Page 11: Cooking Perl with Chef

App = perl + CPAN + your code

CHIDateTimeDBIJSONMoosePlackPOETry::Tiny...

Page 12: Cooking Perl with Chef

App = perl + CPAN + your code

CHIDateTimeDBIJSONMoosePlackPOETry::Tiny...

your application is the versioned set of all its compontents

Page 13: Cooking Perl with Chef

App = perl + CPAN + your code

CHIDateTimeDBIJSONMoosePlackPOETry::Tiny...

v1.0.0

your application is the versioned set of all its compontents

Page 14: Cooking Perl with Chef

App = Perl + CPAN + your code

CHIDateTimeDBIJSONMoosePlackPOETry::Tiny...

v1.0.0 v5.14.2

your application is the versioned set of all its compontents

Page 15: Cooking Perl with Chef

App = Perl + CPAN + your code

0.550.761.6222.532.06030.99891.3540.11...

v1.0.0 v5.14.2

your application is the versioned set of all its compontents

Page 16: Cooking Perl with Chef

App = Perl + CPAN + your code

0.550.761.6222.532.06030.99891.3540.11...

v1.0.0 v5.14.2 v1.0

your application is the versioned set of all its compontents

Page 17: Cooking Perl with Chef

App = Perl + CPAN + your code

0.550.761.6222.532.06030.99891.3540.11...

v1.0.0 v5.14.2 v1.0

change one piece...

Page 18: Cooking Perl with Chef

App = Perl + CPAN + your code

0.550.761.6222.532.06030.99891.3540.11...

v1.0.0 v5.16.0 v1.0

change one piece...

Page 19: Cooking Perl with Chef

App = Perl + CPAN + your code

0.550.761.6222.532.06030.99891.3540.11...

v1.0.1 v5.16.0 v1.0

… and you have a new version of your application

Page 20: Cooking Perl with Chef

Repeatable deployment means...

Page 21: Cooking Perl with Chef

Repeatable deployment means...

... the same Perl

Page 22: Cooking Perl with Chef

Repeatable deployment means...

... the same Perl

... the same modules

Page 23: Cooking Perl with Chef

Repeatable deployment means...

... the same Perl

... the same modules

... the same code

Page 24: Cooking Perl with Chef

Repeatable deployment means...

... the same Perl

... the same modules

... the same code

... on demand

Page 25: Cooking Perl with Chef

Easy...

Page 26: Cooking Perl with Chef

If we have the right tools

Page 27: Cooking Perl with Chef

Use one-size fits all distribution packagers like apt, yum, etc...?

(How much do you like your system perl?!)

Page 28: Cooking Perl with Chef

This problem is notunique to Perl

Page 29: Cooking Perl with Chef

Let's be inspired by Larry

Page 30: Cooking Perl with Chef

[larry hat pic]

Page 31: Cooking Perl with Chef

Or better yet, Paul

Page 32: Cooking Perl with Chef
Page 33: Cooking Perl with Chef

YARRR!

Page 34: Cooking Perl with Chef

Great hackers steal!

Page 35: Cooking Perl with Chef

Great hackers steal ideas

Page 36: Cooking Perl with Chef

Consider Chef...

Page 37: Cooking Perl with Chef

(written in ruby; various ruby app stacks)

Chef ❤ Ruby

Page 38: Cooking Perl with Chef

(virtualenv; pip; django apps)

Chef ❤ Python

Page 39: Cooking Perl with Chef

Common patterns emerge

Page 40: Cooking Perl with Chef

python → virtualenv + pipruby → rvm + Bundler

Page 41: Cooking Perl with Chef

We've built tools like these, too

Page 42: Cooking Perl with Chef

Kang-min Liu (gugod)

Matt S Trout (mst)

Tatsuhiko Miyagawa

(and a big community helping them)

Page 43: Cooking Perl with Chef

perlbrew – multiple perl manager

Matt S Trout (mst)

Tatsuhiko Miyagawa

(and a big community helping them)

Page 44: Cooking Perl with Chef

perlbrew – multiple perl manager

local::lib – custom @INC manager

Tatsuhiko Miyagawa

(and a big community helping them)

Page 45: Cooking Perl with Chef

perlbrew – multiple perl manager

local::lib – custom @INC manager

carton – versioned dependency installer

(and a big community helping them)

Page 46: Cooking Perl with Chef

So we have the pieces

Page 47: Cooking Perl with Chef

Repeatable deployment in five parts

Page 48: Cooking Perl with Chef

Repeatable deployment in five parts

application-specific Perl

application-specific @INC path

versioned application code

versioned module dependencies

automate the previous four

Page 49: Cooking Perl with Chef

Repeatable deployment in five parts

perlbrew

application-specific @INC path

versioned application code

versioned module dependencies

automate the previous four

Page 50: Cooking Perl with Chef

Repeatable deployment in five parts

perlbrew

local::lib

versioned application code

versioned module dependencies

automate the previous four

Page 51: Cooking Perl with Chef

Repeatable deployment in five parts

perlbrew

local::lib

git

versioned module dependencies

automate the previous four

Page 52: Cooking Perl with Chef

Repeatable deployment in five parts

perlbrew

local::lib

git

carton

automate the previous four

Page 53: Cooking Perl with Chef

Repeatable deployment in five parts

perlbrew

local::lib

git

carton

@&$%!

Page 54: Cooking Perl with Chef

[swedish chef FAIL pic]

Page 55: Cooking Perl with Chef

No support in Chef...

Page 56: Cooking Perl with Chef

So I implemented it

Page 57: Cooking Perl with Chef

(In Ruby)

Page 58: Cooking Perl with Chef

(After I learned some Ruby)

Page 59: Cooking Perl with Chef

(perlbrew; local::lib; carton)

Chef ❤ Perl

Now...

Page 60: Cooking Perl with Chef

Time for a quick Chef glossary...

(see http://wiki.opscode.com/)

Page 61: Cooking Perl with Chef

“Cookbook”

A collection of components to configurea particular application

Typically includes recipes, providers, templates, etc.

(CPAN analogy → “distribution”)

Page 62: Cooking Perl with Chef

“Recipe”

Component applied that deploys an application or service

Typically declarative, specifying desired resources and associated configuration

Page 63: Cooking Perl with Chef

“Resource”

An abstraction of something to be deployed

Page 64: Cooking Perl with Chef

“Provider”

Platform-specific implementation to deliver a resource

Page 65: Cooking Perl with Chef

“Node”

A host computer managed with Chef

Often means the configuration file that defines recipes, attributes and roles that define the target state of a host

Page 66: Cooking Perl with Chef

“Attribute”

A variable used in a recipe and/or provider that customizes the configuration of a resource

Attributes have defaults, but can be customized for nodes or roles

Page 67: Cooking Perl with Chef

“Role”

A collection of recipes and attributes used to apply common configuration across multiple nodes

Page 68: Cooking Perl with Chef

Summary...

cookbooks include recipes and providers

roles, recipes and attributes get applied to nodes

recipes specify desired resources and customize them with attributes

providers do the work of deploying resources

Page 69: Cooking Perl with Chef

I wrote two Perl Chef cookbooksfor the Chef community repository

(which is like CPAN circa 1996 or so)

http://community.opscode.com/

Page 70: Cooking Perl with Chef

1. perlbrew – for managing perls

2. carton – for deploying apps

Also available here: https://github.com/dagolden/perl-chef

Page 71: Cooking Perl with Chef

perlbrew cookbook resources:

perlbrew_perl – install a perl

perlbrew_lib – create a local::lib

perlbrew_cpanm – install modules to perl or lib

perlbrew_run – run shell commands under aparticular perlbrew and/or lib

Page 72: Cooking Perl with Chef

carton cookbook resource:

carton_app – deploy an app with carton

– start in directory with the app source– configure for a specific perlbrew perl– install versioned dependencies with carton– create a runit service for the app– start the app

Page 73: Cooking Perl with Chef

Time for an example:

Deploying a “Hello World” Plack app

https://github.com/dagolden/zzz-hello-world

Page 74: Cooking Perl with Chef

1. Write the application

2. Use carton to create a carton.lock file with versioned dependency info

3. Write a simple cookbook for the application

4. Check it all into git

5. Deploy the application with Chef

Steps for creating Hello World

Page 75: Cooking Perl with Chef

$ tree.├── Changes├── Makefile.PL├── app.psgi├── carton.lock├── cookbook│ └── hello-world│ ├── README.md│ ├── attributes│ │ └── default.rb│ ├── metadata.rb│ └── recipes│ └── default.rb└── lib └── ZZZ └── Hello └── World.pm

Page 76: Cooking Perl with Chef

$ tree.├── Changes├── Makefile.PL├── app.psgi├── carton.lock├── cookbook│ └── hello-world│ ├── README.md│ ├── attributes│ │ └── default.rb│ ├── metadata.rb│ └── recipes│ └── default.rb└── lib └── ZZZ └── Hello └── World.pm

Page 77: Cooking Perl with Chef

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

(this Plack app just invokes a simple module)

Page 78: Cooking Perl with Chef

$ tree.├── Changes├── Makefile.PL├── app.psgi├── carton.lock├── cookbook│ └── hello-world│ ├── README.md│ ├── attributes│ │ └── default.rb│ ├── metadata.rb│ └── recipes│ └── default.rb└── lib └── ZZZ └── Hello └── World.pm

Page 79: Cooking Perl with Chef

use 5.008001;use strict;use warnings;

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(<<"HERE");<html><head><title>Hello World</title></head><body><p>Hello World. It is @{[scalar localtime]}</p>...</body></html>HERE return $res->finalize;}

1;(the module just returns some dynamic HTML)

Page 80: Cooking Perl with Chef

$ tree.├── Changes├── Makefile.PL├── app.psgi├── carton.lock├── cookbook│ └── hello-world│ ├── README.md│ ├── attributes│ │ └── default.rb│ ├── metadata.rb│ └── recipes│ └── default.rb└── lib └── ZZZ └── Hello └── World.pm

Page 81: Cooking Perl with Chef

use inc::Module::Install;name 'ZZZ-Hello-World';version '1.0'; requires 'Plack';requires 'Starman'; WriteAll;

(the Makefile.PL also includes deployment dependencies like Starman)

Page 82: Cooking Perl with Chef

During development, carton installs dependencies locally and creates a versioned dependency file called carton.lock

$ carton install# installs dependencies into a local directory# creates carton.lock if it doesn't exist# carton.lock is a JSON file of dependency info

Page 83: Cooking Perl with Chef

During deployment, carton installs dependencies from carton.lock and runs the app with them

$ carton install# installs dependencies into a local directory

$ carton exec ­Ilib ­­ starman ­p 8080 app.psgi# runs the app using carton installed deps

Page 84: Cooking Perl with Chef

$ tree.├── Changes├── Makefile.PL├── app.psgi├── carton.lock├── cookbook│ └── hello-world│ ├── README.md│ ├── attributes│ │ └── default.rb│ ├── metadata.rb│ └── recipes│ └── default.rb└── lib └── ZZZ └── Hello └── World.pm

Page 85: Cooking Perl with Chef

# carton.lock JSON{ "modules" : { "Class::Inspector" : { "dist" : "Class-Inspector-1.27", "module" : "Class::Inspector", "pathname" : "A/AD/ADAMK/Class-Inspector-1.27.tar.gz", ... }. "Data::Dump" : { ... }, "Devel::StackTrace" : { ... }, "Encode::Locale" : { ... }, ...}

(carton.lock associates module names to specific versions of those module)

Page 86: Cooking Perl with Chef

$ tree.├── Changes├── Makefile.PL├── app.psgi├── carton.lock├── cookbook│ └── hello-world│ ├── README.md│ ├── attributes│ │ └── default.rb│ ├── metadata.rb│ └── recipes│ └── default.rb└── lib └── ZZZ └── Hello └── World.pm

Page 87: Cooking Perl with Chef

# perlbrew to execute withdefault['hello-world']['perl_version'] = 'perl-5.16.0'

# Install directory, repo and tagdefault['hello-world']['deploy_dir'] = '/opt/hello-world'default['hello-world']['deploy_repo'] =

'https://github.com/dagolden/zzz-hello-world.git'default['hello-world']['deploy_tag'] = 'master'

# Service user/group/portdefault['hello-world']['user'] = "nobody"default['hello-world']['group'] = "nogroup"default['hello-world']['port'] = 8080

(attributes are variables used in the recipe; can be customized per-node during deployment)

Page 88: Cooking Perl with Chef

$ tree.├── Changes├── Makefile.PL├── app.psgi├── carton.lock├── cookbook│ └── hello-world│ ├── README.md│ ├── attributes│ │ └── default.rb│ ├── metadata.rb│ └── recipes│ └── default.rb└── lib └── ZZZ └── Hello └── World.pm

Page 89: Cooking Perl with Chef

include_recipe 'carton'

package 'git-core'

git node['hello-world']['deploy_dir'] do repository node['hello-world']['deploy_repo'] reference node['hello-world']['deploy_tag'] notifies :restart, "carton_app[hello-world]"end

carton_app "hello-world" do perlbrew node['hello-world']['perl_version'] command "starman -p #{node['hello-world']['port']} app.psgi" cwd node['hello-world']['deploy_dir'] user node['hello-world']['user'] group node['hello-world']['group']end

carton_app "hello-world" do action :startend

(recipe ensures carton and git are available...)

Page 90: Cooking Perl with Chef

include_recipe 'carton'

package 'git-core'

git node['hello-world']['deploy_dir'] do repository node['hello-world']['deploy_repo'] reference node['hello-world']['deploy_tag'] notifies :restart, "carton_app[hello-world]"end

carton_app "hello-world" do perlbrew node['hello-world']['perl_version'] command "starman -p #{node['hello-world']['port']} app.psgi" cwd node['hello-world']['deploy_dir'] user node['hello-world']['user'] group node['hello-world']['group']end

carton_app "hello-world" do action :startend

(git resource specifies where application code goes...)

Page 91: Cooking Perl with Chef

include_recipe 'carton'

package 'git-core'

git node['hello-world']['deploy_dir'] do repository node['hello-world']['deploy_repo'] reference node['hello-world']['deploy_tag'] notifies :restart, "carton_app[hello-world]"end

carton_app "hello-world" do perlbrew node['hello-world']['perl_version'] command "starman -p #{node['hello-world']['port']} app.psgi" cwd node['hello-world']['deploy_dir'] user node['hello-world']['user'] group node['hello-world']['group']end

carton_app "hello-world" do action :startend

(attributes parameterize the resource statement...)

Page 92: Cooking Perl with Chef

include_recipe 'carton'

package 'git-core'

git node['hello-world']['deploy_dir'] do repository node['hello-world']['deploy_repo'] reference node['hello-world']['deploy_tag'] notifies :restart, "carton_app[hello-world]"end

carton_app "hello-world" do perlbrew node['hello-world']['perl_version'] command "starman -p #{node['hello-world']['port']} app.psgi" cwd node['hello-world']['deploy_dir'] user node['hello-world']['user'] group node['hello-world']['group']end

carton_app "hello-world" do action :startend

(carton_app resources installs deps and sets up runit service...)

Page 93: Cooking Perl with Chef

include_recipe 'carton'

package 'git-core'

git node['hello-world']['deploy_dir'] do repository node['hello-world']['deploy_repo'] reference node['hello-world']['deploy_tag'] notifies :restart, "carton_app[hello-world]"end

carton_app "hello-world" do perlbrew node['hello-world']['perl_version'] command "starman -p #{node['hello-world']['port']} app.psgi" cwd node['hello-world']['deploy_dir'] user node['hello-world']['user'] group node['hello-world']['group']end

carton_app "hello-world" do action :startend

(again, attributes parameterize the resource...)

Page 94: Cooking Perl with Chef

include_recipe 'carton'

package 'git-core'

git node['hello-world']['deploy_dir'] do repository node['hello-world']['deploy_repo'] reference node['hello-world']['deploy_tag'] notifies :restart, "carton_app[hello-world]"end

carton_app "hello-world" do perlbrew node['hello-world']['perl_version'] command "starman -p #{node['hello-world']['port']} app.psgi" cwd node['hello-world']['deploy_dir'] user node['hello-world']['user'] group node['hello-world']['group']end

carton_app "hello-world" do action :startend

(finally, the resource is idempotently started...)

Page 95: Cooking Perl with Chef

These files – and the Perl Chefcookbooks – are all you need

Page 96: Cooking Perl with Chef

Enough code... let's see how to deploy it

Page 97: Cooking Perl with Chef

1. Set up a Vagrant virtual machine

2. Prepare Pantry to manage Chef Solo

3. Get Hello World cookbook and dependencies

4. Configure virtual machine for Hello World

5. Deploy

Steps for deployment of Hello World

Page 98: Cooking Perl with Chef

1. Set up a Vagrant virtual machine

2. Prepare Pantry to manage Chef Solo

3. Get Hello World cookbook and dependencies

4. Configure virtual machine for Hello World

5. Deploy

Steps for deployment of Hello World

Page 100: Cooking Perl with Chef

Vagrant is a tool for managing virtual machines

“Can I have a VirtualBox now, please?”

Page 101: Cooking Perl with Chef

Vagrant is a tool for managing virtual machines

$ vagrant box add base \ http://files.vagrantup.com/lucid32.box

$ vagrant init

$ vagrant up

Page 102: Cooking Perl with Chef

Vagrant is great for testing Chef deployment

(and other things, besides)

Page 103: Cooking Perl with Chef

1. Set up a Vagrant virtual machine

2. Prepare Pantry to manage Chef Solo

3. Get Hello World cookbook and dependencies

4. Configure virtual machine for Hello World

5. Deploy

Steps for deployment of Hello World

Page 104: Cooking Perl with Chef

Chef Solo is Chef without a centralconfiguration server

(good for demos and smaller deployments)

Page 105: Cooking Perl with Chef

Chef – you push config data to Chef Server – nodes run Chef Client to pull config

from Chef Server and execute it

Chef Solo – you push config data to nodes– you run Chef Solo remotely

Page 106: Cooking Perl with Chef
Page 107: Cooking Perl with Chef

One advantage of Chef Solo...

Your config repo is canonical

(i.e. you don't have to track what you've pushed to the central server)

Page 108: Cooking Perl with Chef

One dis-advantage of Chef Solo...

Manual rsync/ssh required (yuck!)

Page 109: Cooking Perl with Chef

1. Set up a Vagrant virtual machine

2. Prepare Pantry to manage Chef Solo

3. Get Hello World cookbook and dependencies

4. Configure virtual machine for Hello World

5. Deploy

Steps for deployment of Hello World

Page 110: Cooking Perl with Chef

Pantry is a tool for automating Chef Solo

Page 111: Cooking Perl with Chef

Pantry is a tool for automating Chef Solo

$ pantry create node server.example.com

$ pantry apply node server.example.com \ --role web --recipe myapp

$ pantry sync node server.example.com

Page 112: Cooking Perl with Chef

Pantry is written in Perl and available on CPAN

(Similar to pocketknife [Ruby] and littlechef [Python])

Page 113: Cooking Perl with Chef

Finally, a demonstration...

Screencast available athttp://youtu.be/H93rt-KtwBE

Page 114: Cooking Perl with Chef

1. Set up a Vagrant virtual machine

2. Prepare Pantry to manage Chef Solo

3. Get Hello World cookbook and dependencies

4. Configure virtual machine for Hello World

5. Deploy

Steps for deployment of Hello World

Page 115: Cooking Perl with Chef

$ vagrant init# create config file

$ vim Vagrantfile# edit to forward local port 8080 to# virtual machine port 8080

$ vagrant up# launch it

$ vagrant ssh# check that it's up, then exit

Page 116: Cooking Perl with Chef

1. Set up a Vagrant virtual machine

2. Prepare Pantry to manage Chef Solo

3. Get Hello World cookbook and dependencies

4. Configure virtual machine for Hello World

5. Deploy

Steps for deployment of Hello World

Page 117: Cooking Perl with Chef

$ pantry init# create directories to hold Chef Solo config

$ pantry create node vagrant \    ­­host localhost \    ­­port 8080      \    ­­user vagrant# create a node config file

$ ssh­add ~/.vagrant.d/insecure_private_key# allow pantry to ssh to vagrant machine 

(Important tip: remove the insecure_private_key after you no longer need it because Github chokes on it.)

Page 118: Cooking Perl with Chef

1. Set up a Vagrant virtual machine

2. Prepare Pantry to manage Chef Solo

3. Get Hello World cookbook and dependencies

4. Configure virtual machine for Hello World

5. Deploy

Steps for deployment of Hello World

Page 119: Cooking Perl with Chef

Four cookbooks must be downloadedand copied to the 'cookbooks' directory

– hello-world– carton– perlbrew– runit

Page 120: Cooking Perl with Chef

1. Set up a Vagrant virtual machine

2. Prepare Pantry to manage Chef Solo

3. Get Hello World cookbook and dependencies

4. Configure virtual machine for Hello World

5. Deploy

Steps for deployment of Hello World

Page 121: Cooking Perl with Chef

$ pantry apply node vagrant ­­recipe hello­world# apply recipe to node configuration

$ pantry apply node vagrant ­­default \    hello­world.perl_version=perl­5.14.2# override a default attribute

$ pantry show node vagrant# see the resulting JSON config file{   "hello­world" : {      "perl_version" : "perl­5.14.2"   },   "run_list" : [      "recipe[hello­world]"   ],   "name" : "vagrant",   "pantry_user" : "vagrant",   "pantry_port" : "2222",   "pantry_host" : "localhost"}

Page 122: Cooking Perl with Chef

1. Set up a Vagrant virtual machine

2. Prepare Pantry to manage Chef Solo

3. Get Hello World cookbook and dependencies

4. Configure virtual machine for Hello World

5. Deploy

Steps for deployment of Hello World

Page 123: Cooking Perl with Chef

$ pantry sync node vagrant# ... wait for everything to deploy ...# then load browser and test it!

Page 124: Cooking Perl with Chef

It works

Page 125: Cooking Perl with Chef

You can do this, too

Page 126: Cooking Perl with Chef

Don't be afraid. Try it out. Get involved.

tutorial and screencast → http://perlchef.com

mailing list → [email protected]