I was recently introduced to using Jenkins as a replacement for cron and I've enjoyed it so much, that I thought I'd give it a bit of a write up.
Motivation
This started out for me with a problem where I had a set of tasks that had to be
kicked off at a certain time of day. I have multiple servers that act as
workers, any of which could run the task, but I definitely only want one of them
to do so. Until recently, I would use a boolean host variable in my ansible
provsioning scripts called cron_master
, to signify that this particular host
is considered the cron_master
and should have the crontab set up with the
desired scheduled tasks. This has worked well and I can't really fault it.
However, I've recently been getting in to the whole servers as cattle, not
pets
thing and that makes this approach less feasible. What happens when we
shoot the cron_master
? What happens if we spin up a second cron_master
by
mistake? I'm sure we could go about this in some complicated way with service
discovery and leader election, but my friend @stephenmelrose
suggested try using jenkins and ansible to fire the jobs.
Scheduling tasks in Jenkins
Jenkins offers a simple way to start jobs on a schedule, which is set up in a very similar way to how you would schedule using crontab.
Under the build triggers section, check Build periodically
and enter a
schedule. The schedule is much like the cron schedule, but there are a few
differences - use jenkins' built in help to learn more.
Running one off commands with ansible
I already had jenkins provisioning servers with ansible scripts, so the inventory and key management was all available. Ansible's patterns are very flexible, allowing you to select a slice of hosts by group.
ansible "worker[0]" - inventory/staging.py -a "cowsay dave"
This pattern can be read as the first host that is in the worker group, taken from the staging inventory.
You could take this a step further, by creating playbooks for each task and using the run_once flag, but I think this way is clear enough for me.
We can then just set this up as an Execute Shell
build step in the Jenkins
job.
What are the benefits?
Jenkins keeps and makes visible a track history of the success of the tasks.
Jenkins keeps and makes available the console output for each task run.
Jenkins has a wide array of plugins and options available for triggering notifications when tasks start, fail, succeed, become unstable etc.
Jenkins makes running tasks manually available to less technical users. Each task has a build now button, allowing those who have access to run the tasks whenever they please.
Other solutions
While not as flexible as using jenkins to drive the tasks, there are systems that offer Dead Man's Switch style behaviour, where the task at hand or the cron command itself phones home to say the task was completed. There are tools specifically made for this, like Dead Man's Snitch or Taylor Otwell's recently released deployment tool Envoyer has a "heartbeat" feature.