Introduction

In this tutorial you learn how to configure an Ubuntu-based OpenStack instance with Apache, MySQL, PHP, and configure and install WordPress.

In another Ansible tutorial, we explained how you can deploy an instance and install a Lamp server using Ansible. Because WordPress needs a web server (for example Apache), a database (MySQL) and PHP to run on we are going to use those playbooks as our starting point.

Prerequisites

For this tutorial, you’ll need the following:

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

Step 1 – Creating an Instance on Fuga Cloud

First, we want to create a playbook to create an instance on Fuga Cloud. Make sure you replace MY_SSH_KEY with your key pair name.

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

- name: Deploy on OpenStack
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Deploy an instance
      os_server:
        state: present
        name: lamp01
        image: Ubuntu 16.04 LTS
        key_name: MY_SSH_KEY
        wait: yes
        flavor: c2.small
        network: public
        meta:
          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 of 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:

[webservers]
INSTANCE_IP ansible_ssh_private_key_file=/home/ubuntu/.ssh/MY_SSH_KEY

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
  tasks:
     - name: "Update repository cache and Install Python 2 needed for running Ansible."
       raw: sudo apt-get update; sudo apt-get -y install python-simplejson

Ansible needs Python 2 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

Step 2 – Making it a LAMP server

Now that we have our Ubuntu server up and running, let’s install WordPress. We’ll make some playbooks to install, and setup Apache, MySQL, and PHP and eventually install WordPress on it.

Setting up Apache 2 and PHP 5 is easy. But we also want to create a bit more structure. So let’s 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: This playbook is almost the same as in the other tutorial, but we added some extra PHP packages necessary for running WordPress.

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
  tasks:
    - 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
    - name: Install php-fpm
      package: name=php-fpm state=present
    - name: Install php-common
      package: name=php-common state=present
    - name: Install php-mbstring
      package: name=php-mbstring state=present
    - name: Install php-xmlrpc
      package: name=php-xmlrpc state=present
    - name: Install php-xml
      package: name=php-xml state=present
    - name: Install php-mysql
      package: name=php-mysql state=present
    - name: Install php zip
      package: name=php-zip state=present
    - name: Install php-curl
      package: name=php-curl state=present

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

This playbook is also almost the same as in the other tutorial, but we added some extra rules to create a database called ‘wordpress’ and create a new MySQL user called ‘wpuser’ for running WordPress

editor ~/ansible/roles/mysql.yaml
- name: Install MySQL for production ready server
  user: ubuntu
  hosts: all
  become: True
  become_user: root
  vars:
    MySQL_root_pass: ReplaceWithYourPassword
  tasks:
    - 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: test1
      apt:
        package:
            - mysql-server
            - mysql-client
            - python-mysqldb
        state: present
        force: yes
        update_cache: yes
        cache_valid_time: 3600
      when: ansible_os_family == "Debian"
    - 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
      with_items:
        - 127.0.0.1
        - localhost
        - ::1
        - "{{ ansible_fqdn }}"
    - name: Removes the MySQL test database
      mysql_db: db=test state=absent login_password="{{ MySQL_root_pass }}" login_user=root

    - name: Create a new database called wordpress
      mysql_db:
        login_user: root
        login_password: "{{ MySQL_root_pass }}"
        name: wordpress
        state: present

    - name: Configure new MySQL user called wpuser
      mysql_user:
        login_user: root
        login_password: "{{ MySQL_root_pass }}"
        name: wpuser
        password: wpuser_password_replace_with_your_own
        priv: 'wordpress.*:ALL'

Now, let’s create the wordpress role file. Open the file `~/ansible/roles/wordpress.yaml`:

editor ~/ansible/roles/wordpress.yaml

Paste in the following YAML config:

- name: Install WordPress
  remote_user: ubuntu
  hosts: all
  become: true
  become_user: root
  gather_facts: true
  tasks:
    - name: Download and Extract WorPress
      unarchive:
        src: https://wordpress.org/latest.tar.gz
        dest: /var/www/
        remote_src: yes

    - name: Update default Apache site
      lineinfile:
        path: /etc/apache2/sites-enabled/000-default.conf
        state: present
        regexp: '(.)+DocumentRoot /var/www/html'
        line: 'DocumentRoot /var/www/wordpress'

    - name: Restart Apache
      service:
        name: apache2
        state: restarted

    - name: Copy sample config file
      command: mv /var/www/wordpress/wp-config-sample.php /var/www/wordpress/wp-config.php

    - name: Update WordPress config file
      lineinfile:
        path: /var/www/wordpress/wp-config.php
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
      with_items:
        - {'regexp': "define\\( 'DB_NAME', '(.)+' \\);", 'line': "define( 'DB_NAME', 'wordpress' );"}
        - {'regexp': "define\\( 'DB_USER', '(.)+' \\);", 'line': "define( 'DB_USER', 'wpuser' );"}
        - {'regexp': "define\\( 'DB_PASSWORD', '(.)+' \\);", 'line': "define( 'DB_PASSWORD', 'wpuser_password_replace_with_your_own' );"}

    - name: Update ownership to Apache user
      file:
        path: /var/www/wordpress/
        state: directory
        recurse: yes
        owner: www-data

    - name: Set the correct permissions on WordPress directories
      command: find /var/www/wordpress/ -type d -exec chmod 750 {} \;

    - name: Set the correct permissions for WordPress files
      command: find /var/www/wordpress/ -type f -exec chmod 640 {} \;

Last, create the LAMP role file. This file will include our Apache, MySQL & WordPress roles, this way we don’t have to play all the files seperatly but we can play them in one run.

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

- name: Include Apache
  import_playbook: apache.yaml

- name: Include MySQL
  import_playbook: mysql.yaml

- name: Include MySQL
  import_playbook: wordpress.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, Apache 2, MySQL, PHP and WordPress ready to go.

You can check your newly installed WordPress by typing the public ip of the server or domain name in your browser. Please make sure that your security group allows port 80 for http and port 443 for https.

Conclusion

In this tutorial, you’ve used Ansible to automatically create, deploy and configure an Ubuntu-based OpenStack instance with Apache, MySQL, PHP and WordPress.

More Ansible tutorials can be found in the Fuga Academy.

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.