Converting puppetd to run using cron
This page contains two possible solutions for using Puppet via cron
Goals
Our goals were to:
- Run puppet as cron
- conserve memory usage
- not use --splay (since puppetd still hangs in memory)
- spread out the puppetd times they run and manually load balance (if needed)
Puppet Recipe
$timeoffset1 = generate('/usr/bin/env', '/etc/puppet/modules/puppet/bin/randomnum.pl', "$fqdn", "30")
$timeoffset2 = generate('/usr/bin/env', '/etc/puppet/modules/puppet/bin/math.pl', "$timeoffset1", "+", "30")
## for 0.24.6+, uncomment this line and comment previous line.
# $timeoffset2 = $timeoffset1 + 30
cron { "puppet":
ensure => present,
command => "/usr/sbin/puppetd --onetime --no-daemonize --logdest syslog > /dev/null 2>&1",
user => 'root',
minute => [ $timeoffset1, $timeoffset2 ],
# Probably want to move these into their own resource.
package { "perl": ensure => "installed", },
file { "/etc/puppet/modules/puppet/etc/": ensure => "directory", },
}
See below for the randomnum and addnum scripts.
You can also create a tag to have some nodes run as daemon and others run as cron.
Random Number Script
Generates a random number and stores the entry so it can be recalled. If you want to load balance your puppetd, just edit the files in /etc/puppet/modules/puppet/etc/ folder.
You must create the folder and have the proper perms on /etc/puppet/modules/puppet/etc/ in order for it to work. Adjust according to your specific setup.
#!/usr/bin/perl
#### /etc/puppet/modules/puppet/bin/randomnum.pl
use strict;
umask 066;
die "Usage: <file basename> <run interval>" unless $ARGV[1];
my $puppetclientetcdir="/etc/puppet/modules/puppet/etc/";
my $keyfile=$puppetclientetcdir.$ARGV[0].".key";
if (!-e $keyfile) {
my $range=$ARGV[1];
my $random_number = int rand $range;
open my $OUT,'>', $keyfile or die "Couldn't write to $keyfile: !$";
print $OUT $random_number;
close $OUT;
}
open my $IN,"$keyfile" or die "Couldn't open $keyfile: !$";
foreach my $line (<$IN>) {
if ($line =~ /^\d+(:?\n|)$/) {
chomp $line;
print $line;
close $IN;
exit;
}
}
close $IN;
# If we get to here something is wrong, just print the XKCD random number
# and exit with an error.
print "4";
exit 1;
#### /etc/puppet/modules/puppet/bin/randomnum.pl END OF FILE
Additional Number Script
Creates the second time within an hour to run cron (add 30 min)
#!/usr/bin/perl
# $Id$
#### /etc/puppet/modules/puppet/bin/math.pl
#### No operator sanity checking, potential security consideration. :)
use strict;
die "Usage: $0 <operand1> <operator> <operand2>\n Example: $0 1 + 2\n Example: $0 2 \\* 8" unless $ARGV[2];
my $result = eval("return $ARGV[0] $ARGV[1] $ARGV[2];"); warn $@ if $@;
print int $result;
#### /etc/puppet/modules/puppet/bin/math.pl END OF FILE
Setting Cron using a Puppet custom function
An alternative cleaner implementation to the above is to use a "random" hash value for cron based on the IP address of the machine.
Here is a Puppet custom function to do this:
Usage Example
cron { "puppet":
ensure => present,
command => "/usr/sbin/puppetd --onetime --no-daemonize --logdest syslog > /dev/null 2>&1",
user => 'root',
minute => ip_to_cron(2)
}
Cron random custom function
The content of the following file needs to be added as a function, usually to your
modulename/lib/puppet/parser/functions/ip_to_cron.rb
cat ip_to_cron.rb
# provides a "random" value to cron based on the last byte of the machine IP address.
# used to avoid starting a certain cron job at the same time on all servers.
# if used with no parameters, it will return a single value between 0-59
# first argument is the occurrence within a timeframe, for example if you want it to run 2 times per hour
# the second argument is the timeframe, by default its 60 minutes, but it could also be 24 hours etc
# ohadlevy@gmail.com
#
# example usage
# ip_to_cron() - returns one value between 0..59
# ip_to_cron(2) - returns an array of two values between 0..59
# ip_to_cron(2,24) - returns an array of two values between 0..23
module Puppet::Parser::Functions
newfunction(:ip_to_cron, :type => :rvalue) do |args|
occours = (args[0] || 1).to_i
scope = (args[1] || 60).to_i
ip = IPAddr.new(lookupvar('ipaddress')).to_s.split('.')[3].to_i
base = ip % scope
if occours == 1
base
else
cron = Array.new
(1..occours).each do |i|
cron << ((base - (scope / occours * i)) % scope)
end
return cron.sort
end
end
end
