If you use Capistrano to deploy applications onto Unix systems you may find our new tool Capistrano-ext-puppetize useful.
Now instead of having to prepare your deployment host beforehand with all the third party libraries and tools it might need (Ruby interpreter, databases, development libraries, ImageMagick, redis, etc) you can keep a Puppet manifest alongside your codebase in a Git repository, and apply it automatically every time you deploy. The benefits are twofold:
To use capistrano-ext-puppetize in your project, add it to the Gemfile
or config/deploy.rb
if you're using that), and then create a manifest in config/puppet/manifests/site.pp
containing the things you need installed. More comprehensive instructions are available in the README on GitHub - if you only want to use it, stop reading this and read that instead. If you want to find out how it was put together, read on.
CEP grew out of some work we did to migrate our platform to a new "cloud" hosting provider. We wanted to automate building our servers from scratch starting with a generic base image. Having settled on "masterless Puppet" as a general approach here see some reasons that puppetmaster is not a good fit for our requirement, this left us with a couple of questions:
The simplest way to answer the second question is to keep the config alongside the app in the same git repository where it can be branched, merged, tagged, pulled, pushed, filed, stamped, indexed, briefed, debriefed and numbered This happily and conveniently solves a good chunk of the first question too, as all the Puppet files will be installed with the app by Capistrano's deploy:update_code
recipe. CEP is in essence, an after
hook for deploy:update_code
that shells into the deployed host and runs puppet apply
. Easy, right?
In practice it's a little more complicated than it is in theory (this maxim seems to be generally applicable to Devops, and possibly also to life), though not by much. The first consideration is that we want to use files and templates and modules and stuff, so it's not really puppet apply
we're running, it's something more like
puppet apply --modulepath=$PROJECT_DIR/config/puppet/modules:$PROJECT_DIR/config/puppet/vendor/modules --templatedir=$PROJECT_DIR/config/puppet/templates --fileserverconfig=$PROJECT_DIR/config/puppet/fileserver.conf $PROJECT_DIR/config/puppet/manifests/site.pp
Given that we quite likely want to apply the Puppet manifest at times other than deploy, such as when the box is booted or at scheduled intervals to make sure that unmanaged interactive changes are reported and reverted, CEP deals with this by creating a shell script /etc/puppet/apply
which invokes Puppet with all the options.
The second requirement is that we really would like our manifests to be able to do different things depending on the role (web, app, db, etc) and environment (staging, production, etc.) of the server they're running on. The traditional Puppet way of doing this is to make it dependent on the hostname, but obviously that's not going to fly when we're using Puppet to provision the host from a base OS install, as it's grabbed an IP address from DHCP and doesn't have a meaningful hostname. But we do have this data in Capistrano, so what if we export our Capistrano variables as Puppet facts?
Consulting the official Puppet documentation on how to create your own facts may leave you under the impression that it requires writing Ruby files, putting them in Puppet modules and indulging in various kinds of gymnastics to get those modules from the puppetmaster onto clients. Happily there is an easier way, as described in the Puppet Cookbook: you can simply define a shell environment variable FACTER_foo
to make the $foo
fact available to manifests. Coupling this with a bit of Capistrano internals poking and some random "why does it keep asking for a password?" head-scratching, we arrived at the following in our Capistrano recipe
# Export capistrano variables as Puppet facts so that the
# site.pp manifest can make decisions on what to install based
# on its role and environment. We only export string variables
# -- not class instances, procs, and other outlandish values
facts = variables.find_all { |k, v| v.is_a?(String) }.map {|k, v| "FACTER_cap_#{k}=#{v.inspect}" }.join(" ")
The password-prompt puzzlement was precipitated by procs. Capistrano allows variables whose values are procs, which are lazily evaluated on demand. Forcing the evaluation of all variables here, as we originally did, is not a good idea because some of them prompt for passwords and if they're not passwords you needed to know, you've just broken the ability to walk away and get a coffee while you're deploying. Hence the
is_a?(String)
criterion to filter these things out.
It is of course, a complete dog to test your manifests this way, as each change requires a git commit and a push to whatever upstream Capistrano is set to pull from. That's why we also have a puppet:install_vagrant
recipe. This is basically a hack we use for testing with Vagrant on VirtualBox. It writes /etc/puppet/vagrant-apply
which looks just like the real apply
script except that all the pathnames point to /vagrant
. In a Vagrant VM this is a shared folder that points to your project directory, so you can change your Puppet syntax and see the effect immediately.
CEP has entertaining interactions with rvm-capistrano, which are described in lurid detail in the README. It expects to find the various kinds of Puppet config in Config/puppet/{manifests/site.pp,modules,vendor/modules,templates,files}
config/puppet/vendor/modules
. Git submodules are of course the second worst system ever invented for doing this kind of thing, but all the others that we've seen are still tied for first place. It works for us.As always, YMMV.
Feel free to comment on how you find using it!
Want to know more about what it's like to work in tech at Simply Business? Read about our approach to tech, then check out our current vacancies.
Find out moreWe create this content for general information purposes and it should not be taken as advice. Always take professional advice. Read our full disclaimer
Keep up to date with Simply Business. Subscribe to our monthly newsletter and follow us on social media.
Subscribe to our newsletter6th Floor99 Gresham StreetLondonEC2V 7NG
Sol House29 St Katherine's StreetNorthamptonNN1 2QZ
© Copyright 2023 Simply Business. All Rights Reserved. Simply Business is a trading name of Xbridge Limited which is authorised and regulated by the Financial Conduct Authority (Financial Services Registration No: 313348). Xbridge Limited (No: 3967717) has its registered office at 6th Floor, 99 Gresham Street, London, EC2V 7NG.