Run an Ansible playbook when starting a cloud instance

Blog Post created by agentle on Oct 18, 2016

Getting Started with Configuration Management Deployment

When you think like a systems engineer, you want to be able to repeat the deployment of your infrastructure over and over again throughout its lifecycle. You want to improve systems and fix systems when they break. Configuration management takes care of consistent configurations, versions, and updates across software, hardware, and networks. You can use configuration management tools to manage the ever growing list of servers, storage, and network configurations across your organization. When deploying applications, you want consistency and reliability as well as collaboration and trust across teams to enact the desired configuration, automatically.


Why or When Would You Use Configuration Management?

Use configuration management when your teams already know one of these technologies and you have a base set of configurations you know work well. For example, when you have a solid understanding of Puppet or Chef and an existing library of testing solutions. In a way, if you have already set up authentication for these config management systems, integration points to your cloud appear to be “free” or low-cost.


Or, maybe you want your teams to learn one of these configuration management tools and the techniques that go along with configuration management.


Would this approach give a better audit trail? Yes, absolutely. Any Orchestration templates and configuration management templates kept in version control provide an audit trail for changes, differences, and a history of configuration.


In our experience, a configuration management tool such as Ansible has a more modular approach than managing and maintaining large Orchestration templates. While there are ways to modularize Orchestration templates, Ansible already has this method built-in the playbook examples.


Also, observe that configuration management platforms are moving to platform-like experience. For example, see Chef Automate, Puppet MCollective, or Ansible Tower.


To start this learning lab, we'll go through an Ansible example.


An Overview of Ansible

To deploy to Metacloud using Ansible, you write configuration files in YAML to create playbooks, which contain plays much like a coach writes for a team. By combining units of organization called roles, such as for a group of hosts, you apply the configuration you want to the servers you manage. In this context, hosts are remote machines managed by Ansible. Roles can apply certain variable values, certain tasks, and certain handlers, which are regular tasks but only run when notified by say a configuration file change.


You describe the infrastructure for the application and connect computing, storage, networks, and databases if needed. You deploy the hosts and can even do rolling updates, or add and remove nodes from a cluster with a few Ansible commands.


Note: This process isn’t about installing Metacloud or OpenStack services with Ansible; this process describes using Ansible to deploy applications using cloud resources.


How Ansible Meets DevOps Needs

You can certainly tick off many checklist items for a 12-factor application with Ansible:


Codebase - YAML files describing the deployment can be tracked. Teams often use GitHub or similar version control systems to track changes in the codebase of playbooks.


Dependencies - You can use groups of with_items for dependencies and to both declare and isolate them.


Backing services - Configurations for groups of hosts give you the ability to set up and tear down web servers, database servers, load balancers, all separately from each other.


Concurrency - You can create asynchronous tasks so that longer-running tasks can keep going while Ansible looks for smaller tasks to complete at the same time. Many Ansible modules are written so that you specify the desired final state, allowing multiple executions without changing the initial application. Inspection is valued over change.


Development/Production parity - Ansible and Vagrant combined give developers a way to replicate the deployment locally so your development and production environments are at parity.


Our overall process for this deployment is to launch a VM with public network access, then install Ansible and run a playbook using cloud-init. The cloud-init startup script makes sure that VM downloads and launches an Ansible playbook by cloning a repository from GitHub.


These commands require you to install the python-openstackclient and have access to Metacloud credentials in your local environment. Refer to Installing OpenStack CLI Tools on Mac OSX, Installing OpenStack CLI Tools on Windows, and Providing Metacloud Credentials to CLI Tools.


  1. Get the name of the image you want to use for launching the VM. This example uses an Ubuntu 14.04 image.
    $ openstack image list
  2. Get the name of the flavor you want to use. This example uses the m1.smallflavor.
    $ openstack flavor list
  3. Discover the security groups available. To make sure that web traffic works for the Jekyll server, ensure that port 80 is available. Refer toConfiguring Access and Security for Instances.
    openstack security list
  4. Discover the networks available for launching upon. If you need to create networks, refer to
    $ openstack network list
  5. Create a cloud-init-ansible.txtfile that transfers information and commands to the VM as well as install Ansible and get a copy of the playbook to run on the cloud instance.
       #cloud-config    apt_sources:     - source: "ppa:ansible/ansible"    apt_update: true    packages:     - software-properties-common     - git     - wget     - unzip     - python2.7     - ansible    runcmd:     - mkdir -p /etc/ansible/roles     - echo "[jekyll]\n127.0.0.1 ansible_connection=local" >> /etc/ansible/hosts     - git clone -b cloud /etc/ansible/roles/ansible-jekyll     - ansible --version     - ansible-galaxy install rvm_io.rvm1-ruby     - ansible-playbook /etc/ansible/roles/ansible-jekyll/site.yml    output : { all : '| tee -a /var/log/cloud-init-output.log' } 
  6. Launch the server, using the security group and the keypair you want inserted:
       $ openstack server create \
        --image "Ubuntu-14.04" \
        --flavor m1.small \
        --security-group jekyll \
        --key-name pub-key \
        --nic net-id=private-network \
        --user-data cloud-init-ansible.txt \
  7. If you need to add a floating IP address from your quota, use these commands to create a new floating IP in the pool:
       $ openstack ip floating pool list
       | Name                |
       | public-floating-60  |
       $ openstack ip floating create public-floating-601
       | Field       | Value                                |
       | fixed_ip    | None                                 |
       | id          | 93d429ae-e213-4f37-a609-da0ca48da8ef |
       | instance_id | None                                 |
       | ip          |                       |
       | pool        | public-floating-60                   |
  8. Next, list and associate an available floating IP address to this server so that it has external network access:
       $ openstack ip floating list
       | ID                                   | Floating IP Address | Fixed IP Address | Port                                 |
       | 82853365-1030-4c33-9f28-60ee4c52b46d |       |      | 339c2026-91e5-4945-8bf9-00c1601b46f1 |
       | 89d4a20c-3132-430c-8035-8d6c7ca45361 |        | None             | None                                 |
  9. Choose a floating IP address that does not have a fixed IP address already assigned to it (marked None in the example), and add it to your server.
    $ openstack ip floating add setup-ansible 
  10. Now you can connect to the launched server and take a look around to see how to continue with Ansible playbooks.
    $ ssh -vv cloud-user@
  11. Once you're connected to the VM through SSH, find out the Ansible version that was installed through the cloud-init file. The console log should also have this in it, since the cloud-init file has ansible --versionas a command.
       $ ansible -v
       ansible 2.1.0


Note: The username you use, such as cloud-user in the example, depends on the way the images are set up. Sometimes images use cloudas the operating system user. You can adjust your playbook accordingly and checkout different branches of the ansible-playbook to change to a different OS user name in the group_vars/jekyll.yml file.


Set up a git remote to push to the Ruby and Jekyll environment

To deploy a Jekyll site to your new server, run these commands in the git clone of a repo with a Jekyll site. An example is at

$ git push staging master 

For example:

$ git push jekyll-staging master 

Now, when you point your browser to, you see the deployed master branch of the summit-example site

Deployed Jekyll site in browser

Figure 1: Deployed Jekyll site in browser



If you get a scheduler error, it’s possible you’re using the wrong network ID for --nic net-id=<value>. It’s also possible you have chosen a flavor that can’t be launched on the availability zone for your credentials.

From within the VM, you can get log information about the cloud-init configuration from /var/log/cloud-init.log and /var/log/cloud-init-output.log.


To troubleshoot the SSH connection further, use -vvv for the highest verbosity information back when making the ssh call. If you can’t SSH to the instance, make sure the security group you chose when launching allows SSH ingress traffic. Also ensure you have outward access to the SSH port selected (typically 22).


Since you’re using a public/private keypair to access the instance, make sure the paired public key did get copied to the virtual machine at launch time, otherwise the mismatch prevents you from connecting with the key.