< Shared Library Search Paths | Russ Allbery > Technical Notes | System Templating with Bundle > |
In the days before configuration management systems became widespread, I used to manage my personal systems using bundle and a Git repository. This an update using Puppet for the configuration management instead of bundle. It's what I currently use to manage all of my personal systems.
The goals of this system include all the regular configuration management goals: ability to reproduce a system after a failure, making the same changes across multiple systems and keeping them consistent, and making changes using a programming language that ensures any variations from my template are repaired. In addition, I want to write changes locally in a fast editor, keep all configuration in Git, and push changes to remote systems, but I don't want to maintain a configuration management infrastructure, a TLS public-key infrastructure, or worry about security patches for a daemon.
This is a small-scale master-less Puppet setup that's designed for a small number of systems (less than ten). It supports systems that aren't continuously on-line, but requires manual action to push a new version of the configuration.
The private Git repository that holds my system configuration looks like a
typical Puppet repository: top-level modules
and nodes
directories, with the normal Puppet module layout under the modules
directory. The Puppet configuration is applied with puppet apply
with nodes/
as its file argument. (See below for how this is
automated.)
For this type of small configuration, I don't bother with a node
classifier or any of the other complex machinery to map nodes to modules.
I just have one file for each system in nodes
, named after the
system with .pp
appended, and a defaults.pp
with global
defaults. Each file contains the node definition and is mostly
include
and class
statements pulling in modules. (In a few
cases, I'll put some very system-specific resources directly in the node,
but I try to avoid that and put everything into modules.)
The rest looks like typical Puppet. You can find numerous resources on-line for how to set up your Puppet modules, and there are lots of strong, conflicting opinions about the best way to do this. Since I'm the only user of my configuration (and since it has private pieces and therefore isn't public), I just organize things via any idiosyncratic way that makes sense to me.
The interesting part of this setup is how to push out changes.
For my laptops, I just keep a local Git checkout of the repository in my home directory, update it as needed, and then run:
puppet apply --modulepath "$(pwd)/modules:/usr/share/puppet/modules" \ --show_diff "$(pwd)/nodes"
from the top level of the checkout. (I use some of the Puppet modules
that are packaged for Debian, hence /usr/share/puppet/modules
.)
Add --noop
before the "$(pwd)/nodes"
argument to see changes
before applying them.
The more interesting case is remote systems where I don't want to log in and update a checkout. For those, I create a Git remote named after the system:
git remote add <system> ssh://root@<system>/root/puppet
and prior to the first push create the Git repository on the remote system as root:
mkdir /root/puppet cd /root/puppet git init --shared git config receive.denyCurrentBranch ignore
and lock off the permissions on /root/puppet
if desired. Then, I
use the following shell script (called apply-puppet
and kept in the
root of the Puppet Git repository) to push changes to the system:
#!/bin/sh set -e puppet-lint modules puppet-lint nodes case "$1" in host1|host2) hostname="$1" shift git push "$hostname" ssh -l root "$hostname" /root/bin/apply-puppet "$@" ;; *) puppet apply --modulepath "$(pwd)/modules:/usr/share/puppet/modules" \ --show_diff "$@" "$(pwd)/nodes" ;; esac
This includes the command from above for the normal case of applying the
template locally. The first case statement has all of the systems on
which I want to update Puppet remotely and have Git remotes set up. (One
could be a bit more creative here and dynamically read this list from the
Git remotes.) Note that --noop
will be passed along to Puppet, as
will any other flags.
I run puppet-lint
before every application of the template to make
sure it stays clean at all times. You may want to take a different
approach to linting.
Note that changes are made by ssh as root. This is a security decision
that I make for my personal systems: I use Kerberos and ssh with GSS-API
authentication, and a separate /root
Kerberos principal that has
direct access to root on my remote systems (via /root/.k5login
).
If you want to use sudo
instead, you will have to add a small bit
of additional complexity to store the checkout elsewhere and run the
command under sudo
. Similarly, on my local systems, I use
su
instead of sudo
; if you want to use sudo
, that's
an obvious change to the puppet apply
line.
/root/bin/apply-puppet
is just this shell script:
#!/bin/sh set -e PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH cd /root/puppet git reset --hard puppet apply --modulepath /root/puppet/modules:/usr/share/puppet/modules \ --show_diff "$@" /root/puppet/nodes
This is mostly the same as the non-remote case of the apply-puppet
script in the repository, except that it adds git reset --hard
to
handle the push to a non-bare repository (and sanitizes the path and
changes to the correct directory).
< Shared Library Search Paths | Russ Allbery > Technical Notes | System Templating with Bundle > |