Community Blog Deploying Drupal 8 using Ansible Playbook: Part 2

Deploying Drupal 8 using Ansible Playbook: Part 2

In this multi-part article series, we will be deploying Drupal 8 using Ansible Playbook on an Alibaba Cloud Elastic Compute Service (ECS) instance.

By Liptan Biswas, Alibaba Cloud Tech Share Author. Tech Share is Alibaba Cloud's incentive program to encourage the sharing of technical knowledge and best practices within the cloud community.

In the previous tutorial of the series, we have already created our playbook which we will run using the Ansible. The playbook itself doesn't do anything but will look for the tasks in roles. In this tutorial, we will start creating the roles for our playbook.

Create a new directory to add the roles in our Ansible playbook by running:

cd ~/drupal-ansible
mkdir roles

Let's start by creating the first role used in the playbook.

Installing MariaDB Role

Create a new directory for "mariadb" role and subdirectories for tasks, handlers, and templates.

mkdir -p roles/mariadb/{tasks,handlers,templates}

Now, create a new YAML file to write the tasks of "mariadb" role.

nano roles/mariadb/tasks/main.yaml

There are several tasks which we will write into the tasks file. We will go through each task to know more about what it does. We will also see the uses of different modules of Ansible.

Note: All the tasks provided below are part of the "main.yaml" file of "drupal" role. Copy and paste the tasks serially until you are instructed to close the file. Tasks are divided in such manner just to explain them. You can click here to check how the whole file should look.

The first task is to execute a shell command to add the repository key for MariaDB. Add the following YAML code into the file.

- name: install mariadb key
  shell: apt-key adv --recv-keys --keyserver hkp:// 0xF1656F24C74CD1D8

The above Ansible code will use the shell module of Ansible to execute the given shell command. Furthur, add the following lines.

The next task will add the MariaDB repository in the system using "apt_repository" module. In Ansible playbooks, state: present tells Ansible to add the object to the system. To remove some object, you can use state: absent. Ansible will update the apt repository cache when we pass update_cache=yes argument.

- name: install mariadb repository
  apt_repository: repo='deb [arch=amd64,i386,ppc64el] xenial main' state=present update_cache=yes

The following task will install packages such as MariaDB server and a few development packages that are required to install the python module of MySQL client. Notice the use of item variable in the package name and then providing the list within the with_items attribute. This method is used to iterate through the list of items. We have used Ansible package module to install the packages using apt.

- name: install latest mariadb
  package: name={{ item }} state=present
    - mariadb-server
    - libmysqlclient-dev
    - gcc
    - python3-dev

The next task executes a shell command which installs the latest version of Python pip into the system. We could have installed Python pip using the above method also, but that will install an older version of pip. Since we have used curl in a shell command, Ansible will give you a warning that there are alternatives to "curl" such as "get_uri". Setting warn=False disables the warnings.

- name: install the latest pip
  shell: curl | python3 warn=False

Next, use the pip module to install the required python packages. Python package "mysqlclient" is required to run queries on the database server.

- name: install python MySQL client
      - setuptools
      - mysqlclient
    state: latest

By default, MariaDB listens to the localhost addresses only. In our scenario, we are using one server to host the database server and the other one will be used for installing Drupal. We will need to modify the MariaDB configuration so that it can listen to all address. In our case, the ECS server running Drupal will make queries to the database server instance. The below task will find the line starting with bind-address in the MySQL configuration file /etc/mysql/my.cnf. Upon matching, it will take the backup of the file and will replace the whole line with bind-address = It will then notify the handler to run the job named with restart mariadb.

- name: configure mariadb to bind to all addresses
    path: /etc/mysql/my.cnf
    regexp: ^bind-address
    line: bind-address =
    state: present
    backup: yes
    - restart mariadb

For this handler to run, we will need to create a task with the name "restart mariadb" in handlers directory. We will do that later in the tutorial once we finish the tasks.

In a fresh MariaDB installation, the root password is not set. For the security of the instance, it is critical to set the root password. The mysql_user module in Ansible is used to manipulate the MySQL user accounts. The Ansible task below will update the password of the root account to the password specified in the variable mysql_root_pass. The value of the variables in Ansible can be assigned in many ways. Later in this tutorial, we will assign the variables globally.

- name: update mysql root password for all root accounts
  mysql_user: name=root host={{ item }} password={{ mysql_root_pass }}
   - ::1
   - localhost

Now that our root account is secured with some password, the password must be supplied to MySQL client so that it can automatically run queries on the database server without prompting for the username and password each time. For this purpose, we will use Ansible template module. Template module can populate the variable before copying the file to the remote server.

- name: copy .my.cnf file with root password credentials
    src: my.cnf
    dest: ~/.my.cnf
    mode: 0600

The above task will copy the "my.cnf" file available in "templates" directory of "mariadb" role and will put the file in the home directory of the root user on the remote machine after populating the variables. We will also need to create this file, which we will do later in the tutorial.

Now, the next task will start the MariaDB server and will enable it to start automatically at boot time and failures.

- name: service
  service: name=mariadb state=started enabled=yes

- meta: flush_handlers

The flush_handlers command will execute all the handlers which are pending. Further, add the following tasks to remove the anonymous users and test database.

- name: ensure anonymous users are not in the database
  mysql_user: name='' host={{ item }} state=absent
   - ::1
   - localhost

- name: remove the test database
  mysql_db: name=test state=absent

Finally, create the database for Drupal and also create a new database user having all the privileges over the database we have created.

- name: create a new database for Drupal
  mysql_db: name={{ drupal_db_name }} state=present

- name: create a new database user for Drupal
  mysql_user: name={{ drupal_db_user }}  password={{ drupal_db_pass }}
                priv="{{ drupal_db_name }}.*:ALL" state=present host={{ hostvars['web-server']['ansible_default_ipv4']['address'] }}

Notice the variable hostvars'web-server'['address'] in the last task. The variable will result in the private IP address of instance named "web-server" which is the instance on which we will install Drupal. This will make sure that our database can be used only from that instance.

Save the file and exit from the editor.

Our tasks file for the "mariadb" role is now in place. We are still required to create a file to define the values of the variables. Also, we need to create two additional files for describing the handler to restart mariadb and the template for "my.cnf" file.

Let's start by creating the variable file.

Variables in Ansible can be defined in several ways. The first method is on the per-role basis, which will be available locally to the concerned role only. Or, we can define the variable globally, so that it is available to all the roles in the playbook. In this tutorial, we will require to define the variable globally because there are several places in playbook where a variable is accessed in multiple roles.

To make a variable accessible globally, you will need to put the variables in a YAML file called "all.yaml" under group_vars directory. Let's create the directory to store the variables.

cd ~/drupal-ansible
mkdir group_vars

Create the YAML file to store the variables.

nano group_vars/all.yaml

Populate the file with the variables we have used in "mariadb" role.

# mariadb role variables

mysql_root_pass: VeryStrongPassword
drupal_db_user: drupal-data
drupal_db_name: drupal-user
drupal_db_pass: StrongPass

Modify the above variables with some strong password and save the file. Our variables are now in place, let's proceed to create the handler which restarts the MariaDB instance when requested.

Create a new YAML file to store the Ansible code for the handler.

nano roles/mariadb/handlers/main.yaml

Populate the file with the following code.

- name: restart mariadb
  service: name=mariadb state=restarted

Save the file and exit from the editor. The last thing we need to do in the "mariadb" role is to create the template "my.cnf" in the template directory.

Create the new file named "my.cnf".

nano roles/mariadb/templates/my.cnf

Populate the file with the following text.

password={{ mysql_root_pass }}

Notice that we have put a variable mysql_root_pass in the above configuration file. Before copying this file to the remote host, Ansible will populate the value of the variable in this configuration file.

Let's proceed further to create our second role, which will be executed on the "web-server" instance.

PHP Role

Create a new directory for the "php" role and subdirectories for the tasks. Creating the template and handlers directory is not required as we will not be using any of them in this role.

mkdir -p roles/php/tasks

Now, create a new YAML file to write the tasks of "php" role.

nano roles/php/tasks/main.yaml

Populate the file with the following tasks.

- name: install ondrej/php repository
  apt_repository: repo='ppa:ondrej/php' state=present update_cache=yes

The above task will add the PPA "ondrej/php" into the system. The following task will install PHP 7.2 along with all the required modules.

- name: install php7.2 with all modules
  package: name={{ item }} state=present
    - php7.2
    - php7.2-curl
    - php7.2-gd
    - php7.2-cli
    - php7.2-mysql
    - php7.2-xml
    - php7.2-mbstring
    - php7.2-fpm
    - php7.2-json
    - php7.2-intl
    - php7.2-zip

The next task will start the php7.2-fpm service and will also enable it to automatically start at boot time.

- name: start and enable service
  service: name=php7.2-fpm state=started enabled=yes

Set the time zone and memory limit in the loaded PHP configuration file of PHP-FPM. The module "lineinfile" is used to find and replace a whole line in Ansible. It matches the line using the regular expressions. Since we have modified the PHP configuration file, flush_handlers will also run a handler which will restart the php7.2-fpm service.

- name: setting timezone in php.ini
    path: /etc/php/7.2/fpm/php.ini
    regexp: ^;date.timezone or ^date.timezone
    line: date.timezone = {{ date_timezone }}
    state: present

- name: setting timezone in php.ini
    path: /etc/php/7.2/fpm/php.ini
    regexp: ^memory_limit
    line: memory_limit = {{ memory_limit }}
    state: present

- name: restart php7.2-fpm
  service: name=php7.2-fpm state=restarted

The final task in the file simply restarts the php7.2-fpm service.

Save the file and exit from the editor. Our role to install PHP 7.2 is also ready, but we have used a couple of variable in the above tasks. We will need to define the variable in the common variables file we created earlier.

Edit the common global variables file.

nano group_vars/all.yaml

Append the following at the end of the line.

# PHP Variables
date_timezone: Asia/Kolkata
memory_limit: 512M  # Modify according to available memory on your instance


In this tutorial, we have learned to create the roles for the Ansible playbook we have written. The whole idea to use the roles instead of writing all the plays in one file is to make our playbook more user-friendly and modular. Each module or role in the playbook we have written can be used later in some other playbook also.

In the final tutorial of this series, we will be creating two more roles which are "drupal" and "nginx". We will also run the playbook and verify if Drupal is successfully installed on the desired instance.

0 0 0
Share on

Alibaba Clouder

553 posts | 51 followers

You may also like