5/5 - (1 vote)

This article is a tutorial that is intended for novice system administrators, so experienced professionals can safely skip it. The publication is a continuation of a series of educational articles here , here .

In this article, we will practice writing an Ansible role to automatically raise a web server.

So, as I mentioned in the first part , any ansible-role writing is accompanied by a plan. This plan should include everything that will need to be installed and configured.

If we consider all the points in one article, then the material turns out to be too voluminous – in fact, that is why it was decided to divide it into two parts. Here is our plan, in which the first three points that we completed in the first part are crossed out :

  1. Point 1. Initial server setup.
  2. Point 2. Installing LEMP.
  3. Item 3. Rights and user.
  4. Step 4. Setting up LEMP.
  5. Item 5. Transfer of site code and database.
  6. Item 6. Testing.
  7. Item 7. Bottom line.

In this article, we will complete the remaining four steps. So, at the moment in the /var/ansible directory is:

.

├── hosts.txt

├── LEMP

│ ├──defaults

│ │ └── main.yml

│ ├── files

│ ├── handlers

│ │ └── main.yml

│ ├── meta

│ │ └── main.yml

│ ├── README.md

│ ├── tasks

│ │ ├── apache2_install.yml

│ │ ├── default_settings.yml

│ │ ├── default_user_settings.yml

│ │ ├── exim4_install.yml

│ │ ├── main.yml

│ │ ├── mysql_install.yml

│ │ └── nginx_install.yml

│ ├── templates

│ ├── tests

│ │ ├── inventory

│ │ └── test.yml

│ └── vars

│ └── main.yml

└── playbook.yml

Item 4. Initial server setup.

At this stage, we will configure the following services:

  1. php
  2. nginx
  3. mysql
  4. apache2
  5. exim4

php setup.

The first step is to install php on the remote server. To do this, create an empty file /var/ansible/LEMP/taskscalled php.yml. We use the apt module .

cd /var/ansible/LEMP/tasks
touch php.yml

We add:

- name: install php
  apt: name={{ item }} update_cache=yes state=latest
  with_items:
  - git
  - php
  - php-curl
  - php-gd
  - php-mbstring
  - php-xml
  - php-xmlrpc
  - php-soap
  - php-intl
  - php-zip
  1. with_items : – create an array of variables from the packages we need.
  2. name={{ item }} – here we use the item array to install php modules. apt will run until it has installed all the packages in the item array.

Don’t forget to include the new yml file in main.yml.

Add to main.yml.

#####install php
  - include_tasks: php.yml

For php we will need to pass a php.ini file.

Create a php directory and a php-ansible.ini file in /var/ansible/LEMP/files/.

cd /var/ansible/LEMP/files/
mkdir php
touch php/php.ini

Insert into the php-ansible.ini file, after [PHP], any configuration you need:

[PHP]

Next, you need to transfer this file to /etc/php/7.3/apache2/. It will be automatically loaded and applied after restarting the php interpreter. In this case it is apache2.

Open the php.yml file in jobs (/var/ansible/LEMP/tasks/php.yml)and add file copy and apache2 reload.

We add:

- name: copy config
  copy:
    src: php/php-ansible.ini
    dest: /etc/php/7.3/apache2

- name: restart apache2
  service:
    name: apache2
    state: restarted

Setting up nginx.

  1. To configure, we need to transfer the nginx configuration files:
    1. nginx.conf
    2. site configuration. domain_name.conf.
  2. Enable site.
  3. Reload nginx.

Let’s go to the directory /var/ansible/LEMP/files.

Create an nginx directory.

mkdir nginx
cd nginx/

In the nginx directory, you need to create 2 files: nginx.conf and domain_name.conf .

touch nginx.conf domain_name.conf
  1. nginx.conf must be passed to the nginx root directory
  2. domain_name.conf needs to be passed to sites-available.

In nginx.conf we insert the configuration from our previous article .

The domain_name.conf is the configuration from our previous article .

After adding the configuration, go to ./tasks/nginx_install.yml.

We transfer the configuration to the remote server using the copy and file module .

We add:

  - name: Copy config.
    copy:
      src: nginx/nginx.conf
      dest: /etc/nginx/nginx.conf

  - name: Copy domain_name  config
    copy:
      src: nginx/domain_name.conf
      dest: /etc/nginx/sites-available/domain_name.conf

  - name: enable site domain_name
    file:
      src: /etc/nginx/sites-available/domain_name.conf
      dest: /etc/nginx/sites-enabled/domain_name.conf
      state: link

  - name: restart nginx
    service:
      name: nginx
      state: restarted
  1. src – file in ./files directory;
  2. dest – the address where the file is stored on the remote server.

Let’s run and check:

TASK [LEMP : include_tasks] *************************************************************************************************************************************************************************************
included: /var/ansible/LEMP/tasks/nginx_install.yml for ansible2
TASK [LEMP : Install nginx] *************************************************************************************************************************************************************************************
ok: [ansible2]
TASK [LEMP : Copy config.] **************************************************************************************************************************************************************************************
changed: [ansible2]
TASK [LEMP : Copy domain_name  config] **************************************************************************************************************************************************************************
changed: [ansible2]
TASK [LEMP : enable site domain_name] ***************************************************************************************************************************************************************************
changed: [ansible2]
PLAY RECAP ******************************************************************************************************************************************************************************************************
ansible2                   : ok=4    changed=3    unreachable=0    failed=0

nginx config added.

Outcome:

  1. The nginx configuration and domain_name are passed.
  2. A symbolic link is created in sites-enabled.
  3. The service is restarted.

mysql setup.

You need to create a mysql.cnf configuration file and then transfer it to a remote server.

Create mysql directory in /var/ansible/LEMP/files.

cd /var/ansible/LEMP/files
mkdir mysql
cd mysql/
touch mysql.cnf

Insert after [mysqld] the configuration you need.

[mysqld]

Open ansible mysql configuration file/var/ansible/LEMP/tasks/mysql_install.yml

We transfer the configuration file to the remote server and restart mysql.

We add:

  - name: Copy config
    copy:
      src: mysql/mysql.cnf
      dest: /etc/mysql/mysql.conf.d/mysql.cnf

  - name: restart mysql
    service:
      name: mysql
      state: restarted

Outcome:

  1. Restart the mysql service.
  2. mysql setup is complete.

apache2 setup.

We do similar actions as in nginx

Need to transfer

  1. apache2.conf
  2. domain_name.conf
  3. ports.conf

We create a directory and the above listed files in the directory /var/ansible/LEMP/files.

Configurations for apache2 and domain_name can be taken from the following articles:

  1. apache2.conf configuration
  2. configuration  domain_name.conf .

We configure apache2 to work on port 81. Add configuration for ports.conf:

Listen 81
<IfModule ssl_module>
         Listen 443
</IfModule>

<IfModule mod_gnutls.c>
         Listen 443
</IfModule>

Next we go to/var/ansible/LEMP/tasks/apache2_install.yml

We add:

 - name: copy config
    copy:
      src: apache2/apache2.conf
      dest: /etc/apache2/apache2.conf

  - name: copy domain_name config
    copy:
      src: apache2/domain_name.conf
      dest: /etc/apache2/sites-available/domain_name.conf

  - name: enable site domain_name
    file:
      src: /etc/apache2/sites-available/domain_name.conf
      dest: /etc/apache2/sites-enabled/domain_name.conf
      state: link

  - name: copy ports
    copy:
      src: apache2/ports.conf
      dest: /etc/apache2/ports.conf

  - name: restart apache2
    service:
      name: apache2
      state: restarted

Outcome:

  1. Transferring configuration files;
  2. Restart the apache2 service.

Setting up exim4.

Necessary:

  1. Upload configuration file /etc/exim4/update-exim4.conf.conf;
  2. Reload exim4.

Create an exim4 directory and update-exim4.conf.conf file in /var/ansible/LEMP/files.

Add to the exim4 configuration file:

# /etc/exim4/update-exim4.conf.conf
#
# Edit this file and /etc/mailname by hand and execute update-exim4.conf
# yourself or use 'dpkg-reconfigure exim4-config'
#
# Please note that this is _not_ a dpkg-conffile and that automatic changes
# to this file might happen. The code handling this will honor your local
# changes, so this is usually fine, but will break local schemes that mess
# around with multiple versions of the file.
#
# update-exim4.conf uses this file to determine variable values to generate
# exim configuration macros for the configuration file.
#
# Most settings found in here do have corresponding questions in the
# Debconf configuration, but not all of them.
#
# This is a Debian specific file
dc_eximconfig_configtype='local'
dc_other_hostnames='domain_name'
dc_local_interfaces='127.0.0.1 ; ::1'
dc_readhost=''
dc_relay_domains=''
dc_minimaldns='false'
dc_relay_nets=''
dc_smarthost=''
CFILEMODE='644'
dc_use_split_config='false'
dc_hide_mailname=''
dc_mailname_in_oh='true'
dc_localdelivery='mail_spool'

Add the desired configuration to this file.

(/var/ansible/LEMP/tasks/exim4_install.yml)Add “copy file” and “reload exim4” to the configuration file.

  - name: copy config
    copy:
      src: exim4/update-exim4.conf.conf
      dest: /etc/exim4/update-exim4.conf.conf

  - name: restart exim4
    service:
      name: exim4
      state: restarted

The configuration for exim4 is ready.

Structure of files directory with configuration:

├── files

│ ├── apache2

│ │ ├── apache2.conf

│ │ ├── domain_name.conf

│ │ └── ports.conf

│ ├── exim4

│ │ └── update-exim4.conf.conf

│ ├── mysql

│ │ └── mysql.cnf

│ ├── nginx

│ │ ├── domain_name.conf

│ │ └── nginx.conf

│ └── PHP

│ └── php-ansible.ini

At the moment, the configuration of services is completed. If you need to upload or change additional files, you can also add and change them using the above method.

Item 5. Transfer of site code and database.

At this point, you need to move the site dump to load it into the database and transfer the site code.

We will need 2 directories in the files directory.

It:

  1. mysql_dump – site dump directory;
  2. data – directory with code.

First, add the platform code to the /data.

In our case, the site code will be the wordpress installer .

Next, transfer the dump of your database to the directory /mysql_dump.

Create a new yml file called file.yml at /var/ansible/LEMP/tasks/.

Include it in main.yml

  ####add_site_file
  - include_tasks: file.yml

Add a task to it:

  - name: copy file domain_name
    copy:
      src: data/
      dest: /var/www/domain_name/data/
      owner: domain_name
      group: domain_name

  - name: copy dump
    copy:
      src: mysql_dump/dump.sql
      dest: /tmp/dump.sql

  - name: mysql_dump
    mysql_db:
      name: domain_name_db
      state: import
      target: /tmp/dump.sql
      login_user: root
      login_password: "{{ mysql_root_password }}"

In order to load the dump into the database, the mysql_db module is used.

Site files and dump were moved to the server.

Item 6. Testing.

At the moment the directory structure looks like this:
.
├── hosts.txt
├── LEMP
│ ├── defaults
│ │ └── main.yml
│ ├── files
│ │ ├── apache2
│ │ │ ├── apache2.conf
│ │ │ ├── mysql
_
_
_
_
_
_ cnf
│ │ ├── mysql_dump
│ │ │ └── dump.sql │ │
├── nginx
│ │ │ ├── ─ php-ansible.ini │ ├── handlers

│ │ └── main.yml
│ ├── meta
│ │ └── main.yml
│ ├── README.md
│ ├── tasks
│ │ ├── apache2_install.yml
│ │ ├── default_settings.yml
│ │ ├── default_user_settings.yml
│ │ ├── exim4_install.yml
│ │ ├── file.yml
│ │ ├── main.yml
│ │ ├── mysql_install.yml
│ │ ├── nginx_install.yml
│ ── php.yml
│ ├── templates
│ ├── tests
│ │ ├── inventory
│ │ └── test.yml
│ └── vars
│ └── main.yml
├── php.ini
├── playbook.retry
└── playbook.yml

Task configuration in .tasks directory:

apache2_install.yml :

  - name: Install apache2
    apt:
      name: apache2
      state: latest

  - name: copy config
    copy:
      src: apache2/apache2.conf
      dest: /etc/apache2/apache2.conf

  - name: copy domain_name config
    copy:
      src: apache2/domain_name.conf
      dest: /etc/apache2/sites-available/domain_name.conf

  - name: copy ports
    copy:
      src: apache2/ports.conf
      dest: /etc/apache2/ports.conf

  - name: restart apache2
    service:
      name: apache2
      state: restarted

default_settings.yml :

  - name: update repo.
    shell: apt update

  - name: install default app.
    shell:
      cmd: "apt install -y dirmngr mc iotop htop telnet tcpdump nmap curl hexedit sudo zip unzip patch pwgen vim less parted subversion ntp bzip2 lsof strace mutt s-nail ncdu smartmontools tree dnsutils logrotate rsyslog"

  - name: time
    shell:
      cmd: "timedatectl set-timezone {{time_zone}}"

  - name: locale settings
    shell:
      cmd: 'locale-gen {{locale1}} && update-locale LANG={{locale2}} LC_TIME="{{locale1}}"'

  - name: hostname
    shell:
      cmd: "hostnamectl set-hostname {{DOMAIN_NAME}}"

default_user_settings.yml :

  - name: add group
    group:
      name: "{{ DOMAIN_NAME }}"
      state: present
      gid: "{{ Group_GID }}"

  - name: add user
    user:
      name: "{{ DOMAIN_NAME }}"
      password: "{{ user_password | password_hash('sha512') }}"
      uid: "{{ User_uid }}"
      group: "{{ DOMAIN_NAME }}"
      state: present
      update_password: on_create
      home: "/var/www/{{ DOMAIN_NAME }}"
      shell: /bin/bash

  - name: create home directory
    file:
      path: "/var/www/{{ DOMAIN_NAME }}"
      owner: "{{ DOMAIN_NAME }}"
      group: "{{ DOMAIN_NAME }}"
      mode: 0751
      state: directory

  - name: create other directory
    file:
      path: "/var/www/{{ DOMAIN_NAME }}/data"
      owner: "{{ DOMAIN_NAME }}"
      group: "{{ DOMAIN_NAME }}"
      mode: 0755
      state: directory

  - name: create other directory
    file:
      path: "/var/www/{{ DOMAIN_NAME }}/log"
      owner: "{{ DOMAIN_NAME }}"
      group: "{{ DOMAIN_NAME }}"
      mode: 0755
      state: directory

  - name: create other directory
    file:
      path: "/var/www/{{ DOMAIN_NAME }}/sess"
      owner: "{{ DOMAIN_NAME }}"
      group: "{{ DOMAIN_NAME }}"
      mode: 0755
      state: directory

  - name: create other directory
    file:
      path: "/var/www/{{ DOMAIN_NAME }}/tmp"
      owner: "{{ DOMAIN_NAME }}"
      group: "{{ DOMAIN_NAME }}"
      mode: 0755
      state: directory

  - name: create other directory
    file:
      path: "/var/www/{{ DOMAIN_NAME }}/upload"
      owner: "{{ DOMAIN_NAME }}"
      group: "{{ DOMAIN_NAME }}"
      mode: 0755
      state: directory

  - name: create other directory
    file:
      path: "/var/www/{{ DOMAIN_NAME }}/log/apache2"
      owner: "{{ DOMAIN_NAME }}"
      group: "{{ DOMAIN_NAME }}"
      mode: 0755
      state: directory

  - name: create other directory
    file:
      path: "/var/www/{{ DOMAIN_NAME }}/log/nginx"
      owner: "{{ DOMAIN_NAME }}"
      group: "{{ DOMAIN_NAME }}"
      mode: 0755
      state: directory

exim4_install.yml :

  - name: Install exim4
    apt:
      name: exim4
      state: latest

  - name: copy config
    copy:
      src: exim4/update-exim4.conf.conf
      dest: /etc/exim4/update-exim4.conf.conf

  - name: restart exim4
    service:
      name: exim4
      state: restarted

main.yml :

#  - include_tasks: default_settings.yml
  #####install mysql
#  - include_tasks: mysql_install.yml
  #####install nginx
#  - include_tasks: nginx_install.yml
  #####install apache2
#  - include_tasks: apache2_install.yml
  #####install exim4
#  - include_tasks: exim4_install.yml
  #####default user settings
#  - include_tasks: default_user_settings.yml
  #####install php
#  - include_tasks: php.yml
  #####copy file domain_name
  - include_tasks: file.yml

mysql_install.yml:

  - name: add mysql repo
    get_url:
      url: https://dev.mysql.com/get/mysql-apt-config_0.8.6-1_all.deb
      dest: "/tmp"
      mode: 0440

  - name: install mysql repo
    apt: "deb=/tmp/mysql-apt-config_0.8.6-1_all.deb"
    become: true

  - name: add key mysql and update repo
    shell: "apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29 && apt update"

  - name: install python-mysqldb
    apt:
      name: python-mysqldb
      state: present
      update_cache: yes

  - name: check latest version of mysql 5.7
    command: bash -c "apt-cache showpkg mysql-server|grep 5.7|head -1|cut -d' ' -f1"
    register: latestmysql57
  - debug: msg="{{ latestmysql57.stdout }}"

  - name: install mysql 57
    apt:
      name: mysql-server={{ latestmysql57.stdout }}
      state: present
      update_cache: yes

  - name: update mysql root password for all root accounts
    become: true
    mysql_user:
      name: root
      host: "{{ item }}"
      password: "{{ mysql_root_password }}"
      login_user: root
      login_password: 12345
      check_implicit_admin: yes
      priv: "*.*:ALL,GRANT"
      state: present
    with_items:
      - 127.0.0.1
      - ::1
      - localhost

  - name: Create a new database with name 'DOMAIN_NAME_DB'
    mysql_db:
      login_user: root
      login_password: "{{ mysql_root_password }}"
      name: "{{name_db}}"
      state: present

  - name: add user DOMAIN_NAME_USR
    mysql_user:
      login_user: root
      login_password: "{{ mysql_root_password }}"
      host: localhost
      name: "{{user_db}}"
      password: "{{password_user_db}}"
      priv: '{{name_db}}.*:ALL,GRANT'
      state: present

  - name: Copy config
    copy:
      src: mysql/mysql.cnf
      dest: /etc/mysql/mysql.conf.d/mysql.cnf

  - name: restart mysql
    service:
      name: mysql
      state: restarted

nginx_install.yml :

  - name: Install nginx
    apt:
      name: nginx
      state: latest

  - name: Copy config.
    copy:
      src: nginx/nginx.conf
      dest: /etc/nginx/nginx.conf

  - name: Copy domain_name  config
    copy:
      src: nginx/domain_name.conf
      dest: /etc/nginx/sites-available/domain_name.conf

  - name: enable site domain_name
    file:
      src: /etc/nginx/sites-available/domain_name.conf
      dest: /etc/nginx/sites-enabled/domain_name.conf
      state: link

  - name: restart nginx
    service:
      name: nginx
      state: restarted

php.yml :

- name: install php
  apt: name={{ item }} update_cache=yes state=latest
  with_items:
  - git
  - php
  - php-curl
  - php-gd
  - php-mbstring
  - php-xml
  - php-xmlrpc
  - php-soap
  - php-intl
  - php-zip

- name: copy config
  copy:
    src: php/php-ansible.ini
    dest: /etc/php/7.3/apache2

- name: restart apache2
  service:
    name: apache2
    state: restarted

Variable file /var/ansible/LEMP/vars/main.yml:

DOMAIN_NAME: domain_name
locale1: ru_RU.UTF-8
locale2: en_US.UTF-8
time_zone: Europe/Moscow
User_uid: 10000
Group_GID: 10000
user_password: password
mysql_root_password: password
name_db: domain_name_db
user_db: domain_name_usr
password_user_db: password

For testing, we go to a remote server. Add to /etc/hostsrecord like:

127.0.0.1 domain_name.com

Checking the status of services:

systemctl status nginx apache2 mysql exim4

If all services are working, we make a request to the site using curl.

# curl -LI domain_name.com
HTTP/1.1 302 Found
Server: nginx
Date: Wed, 29 Jun 2022 11:32:10 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Keep-Alive: timeout=15
Location: http://domain_name.com/wp-admin/setup-config.php

HTTP/1.1 200
Server: nginx
Date: Wed, 29 Jun 2022 11:32:10 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Keep-Alive: timeout=15
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0

Everything is working! Next, you need to configure the site in the web interface.

Item 7. Bottom line.

Well, here we are and got acquainted with Ansible and the basic modules that are most often used in its work. Of course, this ansible role is not ideal, and was created only for learning. This mini-course will be enough to understand how ansible works at a basic level.