Search code examples
cronansibleoperation

Deploying and scheduling changes with Ansible OSS


Please note: I am not interested in any enterprise/for-pay (Tower?) solutions here, only solutions available via Ansible's OSS offering.


OK so I've got my Ansible project configured and working perfectly, woo hoo! Looks something like this:

myansible01.example.com:/opt/ansible/
    site.yml
    fizz.yml
    buzz.yml
    group_vars/
    roles/
        common/
            tasks/
                main.yml
            handlers
                main.yml
        foos/
            tasks/
                main.yml
            handlers/
                main.yml

There's several things I need to accomplish to get this working in a production environment:

  • I need to be able to automate the deployment of changes to this project
  • I need to schedule playbooks to be ran, say, every 30 seconds (to ensure all managed nodes are always in compliance)

So my concerns:

  • How are changes usually deployed to live Ansible projects? Say the project is located at myansible01.example.com:/opt/ansible (my Ansible server). Is it sufficient to simply delete the Ansible project root (rm -rf /opt/ansible) and then copy the latest (containing changes) Ansible project back to the same location? What happens if Ansible is currently running any plays while I perform this "drop-n-swap"?
  • It looks like the commercial offering (Ansible Tower) has a scheduling feature built into it, but not the OSS offering. How can I schedule Ansible OSS to run plays at certain times? For instance, I might want certain plays to be ran every 30 seconds, so as to ensure nodes are always within compliance. Is cron sufficient to do this, or is there a more standard approach?

Solution

  • For this kind of task you typically want an orchestration engine such as Jenkins to do all your, well, orchestration.

    You can set Jenkins to run playbooks on timers or other events such as a push to an SCM such as git.

    Typically a job starts by checking out a tag/branch of our Ansible code base and then applying it to all of our specified servers so you always know what is being run. If you want, this can simply be the head on master (in git terms) so it's always applying the most recent changes. If you were also to have this to hook into your SCM repo then a simple push will force those changes to be applied to all of your servers.

    Because of that immediacy you might want to consider only doing this on some test servers that then have some form of testing done against them (such as Serverspec) to verify that your changes are good before rolling them out to a production environment.

    Jenkins, by default, will not run a job while the same job is running (or if you are maxed out on executor slots) so you can always be sure that it will only pull the repo (including any changes) after your Ansible run is complete. If you have multiple jobs running you can use blocking to prevent jobs running at the same time (both trying to apply potentially different configurations to the servers) but you don't have to worry about a new job starting and pulling the repo into the already running job as Jenkins separates these into separate work spaces.

    We use Jenkins for manual runs of Ansible against our environment but we also have a "self healing" Jenkins job that simply runs a tagged commit of our Ansible code base against our environment, forcing it to an idempotent state to prevent natural drift of configurations. When we need to do something different to the environment or are running a slightly further ahead commit of our code base in to it we can easily disable the self healing job until we're happy with things and then either just re-enable the job to put things back or advance the tag that Jenkins is using to now use the more recent commit.