Use Ansible to deploy and setup an instance on OpenStack as a LAMP server

This tutorial describes how to use Ansible to automatically create, deploy and configure an Ubuntu-based OpenStack instance with Apache, MySQL and PHP.


For this tutorial you'll need the following:

  1. An account on Fuga's OpenStack platform.
  2. A Linux machine (either an instance on OpenStack or your local workstation) capable of running ansible.
  3. A downloaded copy of your openrc.sh file.
  4. The CLI tools installed

In this tutorial we'll assume an Ubuntu / Debian derived Linux distribution. With small changes, the commands should be compatible with any Linux flavor.

What is Ansible

Quote from the ansible.com website:

Ansible is a radically simple IT automation engine that automates cloud provisioning, configuration management, application deployment, intra-service orchestration, and many other IT needs.

Ansible is comparable with Puppet, Chef and SaltStack (among others). Ansible is however the easiest to get started with as it simply runs through ssh instead of a custom agent.
This can however make it more difficult for larger setups and/or more complicated configurations.

An ansible playbook consists of some playbook information and a set of tasks. The format is yaml. A basic playbook looks like this:

- name: <Name of playbook>
  hosts: <A list of hosts to run the playbook on>
  gather_facts: true or false.
    - <task1>
    - <task2>
    - <task3>
    - etc.

And the format of a task is:

name: <Task name>
  <state information>

Please note that yaml is indentation sensitive. Meaning that wrong indentation can lead to non-runable playbooks or wrong usage of variables within a playbook.


First thing we'll need is ansible:

sudo apt-get install ansible python-shade

Next, we'll need to load the OpenStack variables into the environment.

. openrc.sh

(Make sure to provide the correct password when asked.)

Finally, we'll create a directory to put our ansible playbooks:

mkdir ~/ansible; cd ~/ansible

Our first playbook - Create an instance on Fuga

Now that the setup is complete, we want to create a playbook to create an instance on fuga.

We are going to assume the following for our example:

  Image: Ubuntu 16.04 64 Bit
  Hostname: lamp01
  ssh keyname as known within fuga: MySSHKey
  size: c1.small
  network: MyNetwork

You can find other images through the dashboard or using the openstack CLI tools using

glance image-list

First, we'll define our instance deployment playbook.

Create a file within the ~/ansible directory called deploy.yaml and enter the following playbook:

- name: Deploy on OpenStack
  hosts: localhost
  gather_facts: false
    - name: Deploy an instance
        state: present
        name: lamp01
        image: 7050c27f-14ba-4374-9703-e4d61938cde4
        key_name: MySSHKey
        wait: yes
        flavor: c1.small
        auto_floating_ip: yes
        network: MyNetwork
          hostname: lamp01.localdomain

Pay close attention to the indentation.
Save the file and quit the editor. Now run the following command from within the ~/ansible directory (Make sure your openrc.sh environment variables are set.)

ansible-playbook deploy.yaml

The output should look similar to this:

Ansible Output

The deployment can take a couple minutes to complete.
After the deployment, we need to make sure the server is reachable on its hostname through ssh.
You can set up DNS for the instance or we can add it to our hosts file.

A simple oneliner for adding the server to the hosts file:

nova list | grep lamp01 | awk {'print $12 " " $4'} | sed -e 's/[^ =]*=\(.*\),/\1/' | sudo tee -a /etc/hosts

Or, lookup the IP in the dashboard.
Lastly, we need to add the hostname to ansible's config file.
Open the file /etc/ansible/hosts and add the following:


Now we'll need to fix some dependencies for ansible to be able to fully manage the new instance.

Create an ansible playbook. We'll call it setup.yaml:

- name: Setup ansible needed things.
  hosts: all
  gather_facts: no
     - name: "Install Python2 needed for running ansible."
       raw: sudo apt-get -y install python-simplejson`

Ansible needs python2 with simplejson before it can do its things. The above playbook will install it.

Now run it:

ansible-playbook setup.yaml

This can again take about 15 to 30 seconds.

You should get asked to approve the SSH Key Fingerprint of the server.
If, for some reason, it gives an error, the first thing to check is if you can ssh into the new instance on the hostname you specified in /etc/ansible/hosts

Your output should look similar to this:

Ansible Output

Make it a lamp server

Now we'll make some playbooks to setup a lamp stack.
Setting up apache2 and php5 is easy. But we also want to create a bit more structure. So lets start with creating an ansible/roles directory where we can put tasks file specific for certain roles.

mkdir ~/ansible/roles

Next, we'll create the apache role file. Open the file ~/ansible/roles/apache.yaml:

editor ~/ansible/roles/apache.yaml

Paste in the following yaml config:

- name: install apache & php
  remote_user: ubuntu
  hosts: all
  become: true
  become_user: root
  gather_facts: true
    - name: "Install apache2"
      package: name=apache2 state=present
    - name: "Install apache2-php5"
      package: name=libapache2-mod-php state=present
    - name: "Install php-cli"
      package: name=php-cli state=present
    - name: "Install php-mcrypt"
      package: name=php-mcrypt state=present
    - name: "Install php-gd"
      package: name=php-gd state=present

Next, we'll do the same for MySQL. Don't forget to replace the MySQL root password!!

editor ~/ansible/roles/mysql.yaml
- name: Install MySQL for production ready server
  user: ubuntu
  hosts: all
  become: True
  become_user: root
    MySQL_root_pass: ReplaceWithYourPassword

    - name: Set MySQL root password before installing
      debconf: name='mysql-server' question='mysql-server/root_password' value='{{MySQL_root_pass | quote}}' vtype='password'

    - name: Confirm MySQL root password before installing
  debconf: name='mysql-server' question='mysql-server/root_password_again' value='{{MySQL_root_pass | quote}}' vtype='password'

- name: Install MySQL
  apt: package={{ item }} state=installed force=yes update_cache=yes cache_valid_time=3600
  when: ansible_os_family == 'Debian'
    - mysql-server
    - mysql-client
    - python-mysqldb

- name: Deletes anonymous MySQL server user for localhost
  mysql_user: user="" state="absent" login_password='{{ MySQL_root_pass }}'  login_user=root
- name: Secures the MySQL root user
  mysql_user: user="root" password="{{ MySQL_root_pass }}" host="{{ item }}" login_password="{{MySQL_root_pass}}" login_user=root
    - localhost
    - ::1
    - "{{ ansible_fqdn }}"
- name: Removes the MySQL test database
  mysql_db: db=test state=absent login_password="{{ MySQL_root_pass }}" login_user=root

Last, create the lamp role file. This file will include our apache & mysql roles:

editor ~/ansible/roles/lamp.yaml
- name: install LAMP Stack
  hosts: all
  remote_user: ubuntu
  become: true
  become_user: root
  gather_facts: true

- include: apache.yaml
- include: mysql.yaml

Finally, we'll run the playbook:

ansible-playbook roles/lamp.yaml

You should have output similar to:

Ansible Output

That's it :) You now have a lamp server with Ubuntu, Apache2, MySQL and PHP ready to go.
Good luck!

Don't forget to set the right security groups for your server (only the default policy is added during creation). Otherwise the webserver might not be reachable.

Was this article helpful?

Go to the next tutorial

Using Letsencrypt’s certificates and securing nginx’s SSL configuration

This tutorial will guide you through setting up an nginx webserver secured by a free certificate from letsencrypt. Prerequisites This tutorial assumes you have an instance ready with Ubuntu 16.04 as OS. If you don't have one ready yet, you can follow the instructions on creating a cloud instance. You'll also need to assign a floating IP to your instance. Lastly, you'll need to make sure your desired domain name points to this floating IP.
Fuga OpenStack


Terms of use