18/03/2018 - ANSIBLE, VAGRANT, NGINX
In this example we are going to use Vagrant and Ansible to build a LEMP (Linux, Nginx, MySQL, PHP-FPM) server on a single server. We will make sure that certain versions of Nginx (1.10.*), MySQL (5.7.*) and PHP (7.1.*) installed.
I am using Ansible 2.4.3.0
and Vagrant 1.9.5
on MacOS.
$ tree
.
├── Vagrantfile
├── lemp
├── provisioning
│ ├── host_vars
│ │ └── lemp
│ ├── hosts.yml
│ ├── roles
│ │ └── common
│ │ ├── handlers
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ ├── begin.yml
│ │ │ ├── end.yml
│ │ │ ├── main.yml
│ │ │ ├── mysql.yml
│ │ │ ├── nginx.yml
│ │ │ ├── php.yml
│ │ │ └── utility.yml
│ │ └── templates
│ │ ├── mysql.conf.j2
│ │ ├── mysql.php.j2
│ │ ├── ntp.conf.j2
│ │ ├── php.conf.j2
│ │ ├── site1.php.j2
│ │ ├── site1.vhost.j2
│ │ ├── site2.php.j2
│ │ └── site2.vhost.j2
│ └── site.yml
└── vagrant_hosts.yml
# Reflect any "hostname" and "ip" changes in "provisioning/hosts.yml" file
- name: This is for LEMP box
label: LEMP - 192.168.99.31
hostname: lemp
ip: 192.168.99.31
# -*- mode: ruby -*-
# vi: set ft=ruby :
require "yaml"
boxes = YAML.load_file("vagrant_hosts.yml")
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/xenial64"
ANSIBLE_RAW_SSH_ARGS = []
i = 0
boxes.each do |box|
ANSIBLE_RAW_SSH_ARGS << "-o IdentityFile=.vagrant/machines/#{box["hostname"]}/virtualbox/private_key"
config.vm.define box["hostname"] do |machine|
machine.vm.hostname = box["hostname"]
machine.vm.network "private_network", ip: box["ip"]
machine.vm.synced_folder box["hostname"], "/srv/www", create: true, nfs: true, mount_options: ["actimeo=2"]
machine.vm.provider :virtualbox do |virtualbox|
virtualbox.name = box["label"]
end
i += 1
if i == boxes.count
machine.vm.provision :ansible do |ansible|
ansible.raw_ssh_args = ANSIBLE_RAW_SSH_ARGS
ansible.verbose = "-vvv"
ansible.limit = "all"
ansible.inventory_path = "provisioning/hosts.yml"
ansible.playbook = "provisioning/site.yml"
end
end
end
end
end
# Reflect any "hostname" and "ip" changes in "../vagrant_hosts.yml" file
all:
hosts:
lemp:
ansible_host: 192.168.99.31
---
# This playbook sets up whole stack.
- name: Apply common configuration to "lemp" host
hosts: lemp
remote_user: root
become: yes
roles:
- common
---
# Variables listed here are applicable to "common" roles
localhost: 127.0.0.1
ansible_python_interpreter: /usr/bin/python3
locale: en_GB.UTF-8
language: en_GB:en
web_root: /srv/www
nginx_root: /etc/nginx
mysql_database: lemp
mysql_port: 3306
mysql_user: root
mysql_password: root
mysql_encoding: utf8mb4
mysql_collation: utf8mb4_unicode_ci
---
# This playbook contains common handlers that can be called in tasks.
# sudo service ntp restart (whether running or not)
- name: Restart ntp
service:
name: ntp
state: restarted
enabled: yes
# sudo update-locale
- name: Update locale
shell: update-locale
# sudo service nginx restart (whether running or not)
- name: Restart nginx
service:
name: nginx
state: restarted
enabled: yes
# sudo service mysql restart (whether running or not)
- name: Restart mysql
service:
name: mysql
state: restarted
enabled: yes
# sudo service php7.1-fpm restart (whether running or not)
- name: Restart php
service:
name: php7.1-fpm
state: restarted
enabled: yes
---
# This playbook contains common actions that will be run on "lemp" hosts.
# sudo apt-get update
- name: Update apt packages
apt:
update_cache: yes
tags:
- system
# sudo locale-gen en_GB.UTF-8
- name: Install GB locale
locale_gen:
name: "{{ locale }}"
state: present
tags:
- locale
# sudo update-locale LANG=en_GB.UTF-8
# sudo update-locale LC_ALL=en_GB.UTF-8
# sudo update-locale LANGUAGE=en_GB:en
- name: Set locale
command: update-locale "{{ item }}"
with_items:
- LANG="{{ locale }}"
- LC_ALL="{{ locale }}"
- LANGUAGE="{{ language }}"
notify: Update locale
tags:
- locale
# sudo timedatectl set-timezone Europe/London
- name: Set time zone to Europe/London
timezone:
name: Europe/London
tags:
- time
# sudo apt-get install ntp
- name: Install ntp
apt:
name: ntp
state: present
update_cache: yes
tags:
- ntp
# sudo cp provisioning/common/templates/ntp.conf.j2 /etc/ntp.conf
- name: Configure ntp file and restart
template:
src: ntp.conf.j2
dest: /etc/ntp.conf
notify: Restart ntp
tags:
- ntp
---
# This playbook contains common actions that will be run on "lemp" hosts.
# sudo apt-get autoclean
- name: Remove useless apt packages from the cache
apt:
autoclean: yes
tags:
- system
# sudo apt-get autoremove
- name: Remove dependencies that are no longer required
apt:
autoremove: yes
tags:
- system
---
# This playbook contains common actions that will be run on "lemp" hosts.
# Import begin tasks
- name: Import begin tasks
include_tasks: begin.yml
# Import utility tasks
- name: Import utility tasks
include_tasks: utility.yml
# Import nginx tasks
- name: Import nginx tasks
include_tasks: nginx.yml
# Import mysql tasks
- name: Import mysql tasks
include_tasks: mysql.yml
# Import php tasks
- name: Import php tasks
include_tasks: php.yml
# Restart services
- name: Restart services
command: /bin/true
notify:
- Restart nginx
- Restart mysql
- Restart php
tags:
- nginx
- mysql
- php
# Import end tasks
- name: Import end tasks
include_tasks: end.yml
---
# This playbook contains mysql actions that will be run on "lemp" hosts.
# sudo apt-get install *
- name: Install mysql and packages
apt:
name: "{{ item }}"
state: present
update_cache: yes
with_items:
- mysql-server=5.7.*
- python3-mysqldb
tags:
- mysql
# CREATE DATABASE lemp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
- name: Create a new database
mysql_db:
name: "{{ mysql_database }}"
encoding: "{{ mysql_encoding }}"
collation: "{{ mysql_collation }}"
state: present
tags:
- mysql
# Remove all anonymous user accounts
- name: Remove all anonymous user accounts
mysql_user:
name: ''
host_all: yes
state: absent
tags:
- mysql
# Create "root" with all privileges
- name: Create "root" user with all privileges
mysql_user:
name: "{{ mysql_user }}"
password: "{{ mysql_password }}"
priv: '*.*:ALL'
state: present
tags:
- mysql
# sudo cp provisioning/common/templates/mysql.conf.j2 /etc/mysql/mysql.conf.d/mysqld.cnf
- name: Create mysql configuration file
template:
src: mysql.conf.j2
dest: /etc/mysql/mysql.conf.d/mysqld.cnf
tags:
- mysql
# sudo cp provisioning/common/templates/mysql.php.j2 /srv/www/site*/mysql.php
- name: Create site index.php files
template:
src: mysql.php.j2
dest: "{{ web_root }}/{{ item }}/mysql.php"
with_items:
- site1
- site2
tags:
- mysql
---
# This playbook contains nginx actions that will be run on "lemp" hosts.
# sudo apt-get install nginx (v.1.10.*)
- name: Install nginx
apt:
name: nginx=1.10.*
state: present
update_cache: yes
tags:
- nginx
# sudo mkdir /srv/www/lemp
- name: Create web root
file:
path: "{{ web_root }}/{{ item }}"
state: directory
with_items:
- site1
- site2
tags:
- nginx
# sudo cp provisioning/common/templates/site*.vhost.j2 /etc/nginx/sites-available/site*
- name: Create site configuration files
template:
src: "{{ item }}.vhost.j2"
dest: "{{ nginx_root }}/sites-available/{{ item }}"
with_items:
- site1
- site2
tags:
- nginx
# sudo ln -s /etc/nginx/sites-available/site* /etc/nginx/sites-enabled/site*
- name: Create site symbolic link
file:
src: "{{ nginx_root }}/sites-available/{{ item }}"
dest: "{{ nginx_root }}/sites-enabled/{{ item }}"
state: link
with_items:
- site1
- site2
tags:
- nginx
# sudo nano /etc/hosts
- name: Configuring hosts file
lineinfile:
path: /etc/hosts
line: "{{ localhost }} {{ item }}.com"
with_items:
- site1
- site2
tags:
- nginx
---
# This playbook contains php-fpm actions that will be run on "lemp" hosts.
# sudo cp provisioning/common/templates/site*.php.j2 /srv/www/site*/index.php
- name: Create site index.php files
template:
src: "{{ item }}.php.j2"
dest: "{{ web_root }}/{{ item }}/index.php"
with_items:
- site1
- site2
tags:
- php
# sudo apt-get install software-properties-common
- name: Install software-properties-common
apt:
name: software-properties-common
state: present
tags:
- php
# sudo add-apt-repository ppa:ondrej/php
- name: Add repository into sources list
apt_repository:
repo: ppa:ondrej/php
state: present
update_cache: yes
tags:
- php
# sudo apt-get install *
- name: Install PHP and common modules
apt:
name: "{{ item }}"
state: present
with_items:
- php7.1
- php7.1-cli
- php7.1-common
- php7.1-json
- php7.1-opcache
- php7.1-mysql
- php7.1-mbstring
- php7.1-mcrypt
- php7.1-zip
- php7.1-fpm
tags:
- php
# sudo cp provisioning/common/templates/php.conf.j2 /etc/php/7.1/fpm/pool.d/www.conf
- name: Create php configuration file
template:
src: php.conf.j2
dest: /etc/php/7.1/fpm/pool.d/www.conf
tags:
- php
---
# This playbook contains utility actions that will be run on "lemp" hosts.
# sudo apt-get install nano
- name: Install nano
apt:
name: nano
state: present
update_cache: yes
tags:
- nano
[mysqld_safe]
socket = /var/run/mysqld/mysqld.sock
nice = 0
[mysqld]
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
port = {{ mysql_port }}
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
skip-external-locking
bind-address = {{ localhost }}
key_buffer_size = 16M
max_allowed_packet = 16M
thread_stack = 192K
thread_cache_size = 8
myisam-recover-options = BACKUP
query_cache_limit = 1M
query_cache_size = 16M
log_error = /var/log/mysql/error.log
expire_logs_days = 10
max_binlog_size = 100M
<?php
if (!$conn = mysqli_connect("{{ localhost }}", "{{ mysql_user }}", "{{ mysql_password }}")) {
exit('Connection failed: '.mysqli_connect_error().PHP_EOL);
}
echo 'Successful database connection!'.PHP_EOL;
driftfile /var/lib/ntp/drift
# Specify UK NTP servers.
server 0.uk.pool.ntp.org
server 1.uk.pool.ntp.org
server 2.uk.pool.ntp.org
server 3.uk.pool.ntp.org
# Use Ubuntu's NTP server as a fallback.
server ntp.ubuntu.com
# Local users may obtain data from NTP servers.
restrict {{ localhost }}
restrict ::1
[www]
user = www-data
group = www-data
listen = /run/php/php7.1-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
<?php
echo 'Site 1'.PHP_EOL;
server {
listen 80;
listen [::]:80;
server_name site1.com;
root /srv/www/site1;
index index.php index.html;
# Pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Deny access to .htaccess files
location ~ /\.ht {
deny all;
}
# Return 404 for all other files not matching the .php files
location / {
try_files $uri $uri/ =404;
}
error_log /var/log/nginx/site1_error.log;
access_log /var/log/nginx/site1_access.log;
}
<?php
echo 'Site 2'.PHP_EOL;
server {
listen 80;
listen [::]:80;
server_name site2.com;
root /srv/www/site2;
index index.php index.html;
# Pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# Deny access to .htaccess files
location ~ /\.ht {
deny all;
}
# Return 404 for all other files not matching the .php files
location / {
try_files $uri $uri/ =404;
}
error_log /var/log/nginx/site2_error.log;
access_log /var/log/nginx/site2_access.log;
}
$ vagrant up
...
PLAY [Apply common configuration to "lemp" host] *******************************
TASK [Gathering Facts] *********************************************************
TASK [common : Import begin tasks] *********************************************
TASK [common : Update apt packages] ********************************************
TASK [common : Install GB locale] **********************************************
TASK [common : Set locale] *****************************************************
TASK [common : Set time zone to Europe/London] *********************************
TASK [common : Install ntp] ****************************************************
TASK [common : Configure ntp file and restart] *********************************
TASK [common : Import utility tasks] *******************************************
TASK [common : Install nano] ***************************************************
TASK [common : Import nginx tasks] *********************************************
TASK [common : Install nginx] **************************************************
TASK [common : Create web root] ************************************************
TASK [common : Create site configuration files] ********************************
TASK [common : Create site symbolic link] **************************************
TASK [common : Configuring hosts file] *****************************************
TASK [common : Import mysql tasks] *********************************************
TASK [common : Install mysql and packages] *************************************
TASK [common : Create a new database] ******************************************
TASK [common : Remove all anonymous user accounts] *****************************
TASK [common : Create "root" user with all privileges] *************************
TASK [common : Create mysql configuration file] ********************************
TASK [common : Create site index.php files] ************************************
TASK [common : Import php tasks] ***********************************************
TASK [common : Create site index.php files] ************************************
TASK [common : Install software-properties-common] *****************************
TASK [common : Add repository into sources list] *******************************
TASK [common : Install PHP and common modules] *********************************
TASK [common : Create php configuration file] **********************************
TASK [common : Restart services] ***********************************************
TASK [common : Import end tasks] ***********************************************
TASK [common : Remove useless apt packages from the cache] *********************
TASK [common : Remove dependencies that are no longer required] ****************
RUNNING HANDLER [common : Restart ntp] *****************************************
RUNNING HANDLER [common : Update locale] ***************************************
RUNNING HANDLER [common : Restart nginx] ***************************************
RUNNING HANDLER [common : Restart mysql] ***************************************
RUNNING HANDLER [common : Restart php] *****************************************
PLAY RECAP *********************************************************************
lemp : ok=38 changed=24 unreachable=0 failed=0
$ timedatectl
Local time: Sun 2018-03-14 10:51:48 GMT
Universal time: Sun 2018-03-14 10:51:48 UTC
RTC time: Sun 2018-03-14 10:51:46
Time zone: Europe/London (GMT, +0000) # This was "Etc/UTC (UTC, +0000)" before
Network time on: yes
NTP synchronized: yes # This was "no" before
RTC in local TZ: no
$ locale
LANG=en_GB.UTF-8 # This was "en_US.UTF-8" before
LANGUAGE=en_GB:en # This was empty before
LC_CTYPE="en_GB.UTF-8"
LC_NUMERIC="en_GB.UTF-8"
LC_TIME="en_GB.UTF-8"
LC_COLLATE="en_GB.UTF-8"
LC_MONETARY="en_GB.UTF-8"
LC_MESSAGES="en_GB.UTF-8"
LC_PAPER="en_GB.UTF-8"
LC_NAME="en_GB.UTF-8"
LC_ADDRESS="en_GB.UTF-8"
LC_TELEPHONE="en_GB.UTF-8"
LC_MEASUREMENT="en_GB.UTF-8"
LC_IDENTIFICATION="en_GB.UTF-8"
LC_ALL=en_GB.UTF-8 # This was empty before
ubuntu@lemp:~$ php -v
PHP 7.1.15-1+ubuntu16.04.1+deb.sury.org+2 (cli) (built: Mar 6 2018 11:10:13) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.1.15-1+ubuntu16.04.1+deb.sury.org+2, Copyright (c) 1999-2018, by Zend Technologies
ubuntu@lemp:~$ nginx -v
nginx version: nginx/1.10.3 (Ubuntu)
ubuntu@lemp:~$ mysql -uroot -p
Enter password:
Server version: 5.7.21-0ubuntu0.16.04.1 (Ubuntu)
Results within the Vagrant is listed below. Any non existent URL access will produce default "404 Not Found" fallback page.
$ curl site1.com # Serves site1.com from /srv/www/site2
$ curl site1.com/mysql.php # Serves site1.com from /srv/www/site2
$ curl site2.com # Serves site2.com from /srv/www/site1
$ curl site2.com/mysql.php # Serves site2.com from /srv/www/site1
$ curl 127.0.0.1 # Serves localhost from /var/www/html (fallback)
$ curl lemp # Serves localhost from /var/www/html (fallback)
$ curl localhost # Serves localhost from /var/www/html (fallback)
If you go to http://192.168.99.31
within the host OS, you will see default Nginx page which is served from /var/www/html
. Any non existent URL access will produce default "404 Not Found" fallback page.