Raymii.org
Quis custodiet ipsos custodes?Home | About | All pages | Cluster Status | RSS Feed
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
❗ This post is over eight years old. It may no longer be up to date. Opinions may have changed.
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 used the old ones. I reserved some time to convert these playbooks to the new modules and ansible 2. This article shows a very simple example, it creates three servers in OpenStack and adds them to different hostgroups based on variables. For example, to create one loadbalancer and two appservers and run specific playbooks on those hosts based on their role.
Since Ansible 2 the OpenStack modules are renewed. The old nova_*
modules are
replaced by the os_server
modules. Ansible 2 also changed some deperecated
stuff regarding yaml
parsing and variable concatination. Therefore some of my
old playbooks were not working anymore and I had to figure out how to get it
working with the new versions.
I've written about Ansible before, click that link to get all the articles about Ansible.
Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:
I'm developing an open source monitoring app called Leaf Node Monitoring, for windows, linux & android. Go check it out!
Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.
You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $200 credit for 60 days. Spend $25 after your credit expires and I'll get $25!
I found documentation on the new modules but that just created a single instance. I need to spawn multiple instances and add those to specific hostgroups 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 the
specific roles. Create the folder structure if you're starting from zero.
On the local host you need the OpenStack tools and some python modules
installed. The os_server page lists all the requirements. You also need an
openstackrc
file with credentials in your environment:
$ cat openstackrc
export 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 OpenStack
instances. It also adds them to the specific hostgroups. Those hostgroups are
only available when you run this playbook.
Then it starts a new play
(if that is how you call multiple runs) to the first
newly created hostgroup (loadbalancers) and you can add a new play
to run on
the 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 localhost
and 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 to a new hostgroup. That hostgroup is only active within this playbook run.
The parameters we used to create the instances are also available in the
result
of that action. It's a dict
, so you can access all three the servers
we 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 to
use a different ansible_ssh_host
, but you can achieve that by adding extra
data to the variables. The debug
module is your friend here.
If the instances are already created, it will not create them again but it will add then to the hostgroup again.
The variables required for this playbook are the following (roles/create-
instances/vars/main.yml
):
---
prefix: demo
servers:
- 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 different
pseudo-groups in the same tenant/project. I do recommend to create different
projects/tenants per environment (accept/staging etc) instead of prefixes, since
you can then manage the rights more fine grained.
Role specific playbooks
The role specific playbooks are just regular playbook roles you would run. For
the 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