Skip to main content

Raymii.org Logo (IEC resistor symbol)logo

Quis custodiet ipsos custodes?
Home | About | All pages | RSS Feed | Gopher

Ansible - Create OpenStack servers with Ansible 2.0 and the os_server module and a dynamic inventory

Published: 10-09-2016 | Author: Remy van Elst | Text only version of this article


Table of Contents


I regularly deploy clusters and single servers on OpenStack with Ansible.However, Ansible 2.0 comes with new OpenStack modules my playbooks still usedthe old ones. I reserved some time to convert these playbooks to the new modulesand ansible 2. This article shows a very simple example, it creates threeservers in OpenStack and adds them to different hostgroups based on variables.For example, to create one loadbalancer and two appservers and run specificplaybooks on those hosts based on their role.

Since Ansible 2 the OpenStack modules are renewed. The old nova_* modules arereplaced by the os_server modules. Ansible 2 also changed some deperecatedstuff regarding yaml parsing and variable concatination. Therefore some of myold playbooks were not working anymore and I had to figure out how to get itworking with the new versions.

I've written about Ansible before, click that link to get all the articlesabout Ansible.

ansible

If you like this article, consider sponsoring me by trying out a Digital OceanVPS. With this link you'll get $100 credit for 60 days). (referral link)

I found documentation on the new modules but that just created a singleinstance. I need to spawn multiple instances and add those to specifichostgroups based on variables, so this guide wasn't complete enough for me.

This is my folder structure:

$ tree -I "*.git".|-- ansible.cfg|-- group_vars|   `-- all.yml|-- roles|   |-- haproxy|   |   |-- handlers|   |   |   `-- main.yml|   |   |-- tasks|   |   |   `-- main.yml|   |   |-- templates|   |   |   `-- haproxy.cfg.j2|   |   `-- vars|   |       `-- main.yml|   `-- create_instances|       |-- tasks|       |   `-- main.yml|       `-- vars|           `-- main.yml`-- site.yml

site.yml is the main playbook and create-instances and appservers are thespecific roles. Create the folder structure if you're starting from zero.

On the local host you need the OpenStack tools and some python modulesinstalled. The os_server page lists all the requirements. You also need anopenstackrc file with credentials in your environment:

$ cat openstackrcexport OS_AUTH_URL="https://identity.stack.cloudvps.com/v2.0"export OS_USERNAME="username"export OS_PASSWORD="password"export OS_TENANT_ID="UUID"export OS_TENANT_NAME="UUID"

Source it before running the playbooks:

source openstackrc

Main playbook

The site.yml playbook first runs on locahost and creates the OpenStackinstances. It also adds them to the specific hostgroups. Those hostgroups areonly available when you run this playbook.

Then it starts a new play (if that is how you call multiple runs) to the firstnewly created hostgroup (loadbalancers) and you can add a new play to run onthe appservers as well.

---- name: create instances  hosts: localhost  roles:    - { role: create_instances }- name: deploy haproxy  hosts: loadbalancers  user: root  roles:    - { role:  haproxy }    - { role:  keepalived }

Instance creation playbook

The first playbook, roles/create-instances/tasks/main.yml runs on localhostand creates the OpenStack instances:

    ---    - name: launch instances      os_server:        name: "{{ prefix }}-{{ item.name }}"        state: present        key_name: "{{ item.key }}"        availability_zone: "{{ item.availability_zone }}"        nics: "{{ item.nics }}"        image: "{{ item.image }}"        flavor: "{{ item.flavor }}"      with_items: "{{ servers }}"      register: "os_hosts"    - name: add hosts to inventory      add_host:        name: "{{ item['openstack']['human_id'] }}"        groups: "{{ item['item']['meta']['group'] }}"        ansible_host: "{{ item.openstack.accessIPv4 }}"      with_items: "{{ os_hosts.results }}"

Based on the name we defined and the group we gave it also is adds the hosts toa new hostgroup. That hostgroup is only active within this playbook run.

The parameters we used to create the instances are also available in theresult of that action. It's a dict, so you can access all three the serverswe created. That is were we get the group value from.

If you're using a jumphost and other machines with private IP's you need touse a different ansible_ssh_host, but you can achieve that by adding extradata to the variables. The debug module is your friend here.

If the instances are already created, it will not create them again but it willadd then to the hostgroup again.

The variables required for this playbook are the following (roles/create-instances/vars/main.yml):

---prefix: demoservers:  - name: lb1    image: CloudVPS Ubuntu 16.04    flavor: Standard 2    key: SSH-Key    nics: "net-id=00000000-0000-0000-0000-000000000000"    availability_zone: NL1    meta:       group: loadbalancers  - name: app1    image: CloudVPS Ubuntu 16.04    flavor: Standard 2    key: SSH-Key    nics: "net-id=00000000-0000-0000-0000-000000000000"    availability_zone: NL1    meta:       group: appservers  - name: app2    image: CloudVPS Ubuntu 16.04    flavor: Standard 2    key: SSH-Key    nics: "net-id=00000000-0000-0000-0000-000000000000"    availability_zone: NL2    meta:       group: appservers

The prefix is used in the servername and can be used to create differentpseudo-groups in the same tenant/project. I do recommend to create differentprojects/tenants per environment (accept/staging etc) instead of prefixes, sinceyou can then manage the rights more fine grained.

Role specific playbooks

The role specific playbooks are just regular playbook roles you would run. Forthe guide you can use an example play which just does a ping(roles/ping/tasks/main.yml):

---- name: ping instances  ping:

The full play then results in:

    $ ansible-playbook site.yml      [WARNING]: provided hosts list is empty, only localhost is available    PLAY [create instances] ********************************************************    TASK [setup] *******************************************************************    ok: [localhost]    TASK [create_instances : launch instances]    ************************************* changed: [localhost] =>    (item={u'name': u'lb1', u'availability_zone': u'NL1', u'nics':    u'net-id=00000000-0000-0000-0000-000000000000', u'image': u'CloudVPS    Ubuntu 16.04', u'meta': {u'group': u'loadbalancers'}, u'key':    u'SSH-Key', u'flavor': u'Standard 2'}) changed: [localhost] =>    (item={u'name': u'app1', u'availability_zone': u'NL1', u'nics':    u'net-id=00000000-0000-0000-0000-000000000000', u'image': u'CloudVPS    Ubuntu 16.04', u'meta': {u'group': u'appservers'}, u'key': u'SSH-Key',    u'flavor': u'Standard 2'}) changed: [localhost] => (item={u'name':    u'app2', u'availability_zone': u'NL1', u'nics':    u'net-id=00000000-0000-0000-0000-000000000000', u'image': u'CloudVPS    Ubuntu 16.04', u'meta': {u'group': u'appservers'}, u'key': u'SSH-Key',    u'flavor': u'Standard 2'})    TASK [create_instances : add hosts to inventory]    ******************************* changed: [localhost] =>    (item={u'changed': True, '_ansible_no_log': False,    '_ansible_item_result': True, u'server':  [...] # a lot of json    u'volumes': [], u'metadata': {}, u'human_id': u'demo-app2'}, u'id':    u'eff00345-977f-4c72-4684-4aa22d1dfc9f'})    PLAY [ping instances] **********************************************************    TASK [setup] *******************************************************************    ok: [demo-app1]    ok: [demo-app2]    TASK [common : ping instances] *************************************************    ok: [demo-app1]    ok: [demo-app2]    PLAY RECAP *********************************************************************    demo-app1            : ok=2    changed=0    unreachable=0    failed=0       demo-app2            : ok=2    changed=0    unreachable=0    failed=0       localhost            : ok=3    changed=2    unreachable=0    failed=0   
Tags: ansible, cloudvps, nova, openstack, python, tutorials