1041 lines
22 KiB
ReStructuredText
1041 lines
22 KiB
ReStructuredText
.. footer:: @opdavies
|
|
|
|
Deploying PHP with Ansible, Ansible Vault, and Ansistrano
|
|
#########################################################
|
|
|
|
|
|
|
|
|
.. class:: titleslideinfo
|
|
|
|
Oliver Davies, Inviqa
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "Full stack Developer and Systems Administrator"
|
|
TextAnnotation "Organiser of PHP South Wales"
|
|
|
|
.. page:: imagePage
|
|
|
|
.. image:: images/techs.png
|
|
:width: 14cm
|
|
|
|
.. page:: standardPage
|
|
|
|
Things we'll be looking at
|
|
==========================
|
|
|
|
- **Ansible** crash course
|
|
- Keeping secrets with **Ansible Vault**
|
|
- Deployments with **Ansistrano**
|
|
|
|
.. page:: imagePage
|
|
|
|
.. image:: images/logo-acquia.png
|
|
:width: 12cm
|
|
|
|
|
|
|
|
|
.. image:: images/logo-platformsh.png
|
|
:width: 12cm
|
|
|
|
|
|
|
|
|
.. image:: images/logo-pantheon.png
|
|
:width: 12cm
|
|
|
|
.. page::
|
|
|
|
.. image:: images/logo-digital-ocean.png
|
|
:width: 6cm
|
|
|
|
|
|
|
|
|
.. image:: images/logo-linode.png
|
|
:width: 6cm
|
|
|
|
|
|
|
|
|
.. image:: images/logo-vultr.png
|
|
:width: 8cm
|
|
|
|
.. page:: standardPage
|
|
|
|
What is Ansible?
|
|
================
|
|
|
|
.. class:: text-lg
|
|
|
|
Ansible is an open-source **software provisioning**, **configuration management**, and **application-deployment** tool.
|
|
|
|
|
|
|
|
|
https://en.wikipedia.org/wiki/Ansible_(software)
|
|
|
|
.. page::
|
|
|
|
What is Ansible?
|
|
================
|
|
|
|
- CLI tool
|
|
- Configured with YAML
|
|
- Agentless, connects via SSH
|
|
- Jinja2 for templating
|
|
- Executes ad-hoc remote commands
|
|
- Installs software packages
|
|
- Performs deployment steps
|
|
- Batteries included
|
|
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "- Written in Python but configured with Yaml."
|
|
TextAnnotation "Drupal, Symfony and a lot of other projects use YAML."
|
|
TextAnnotation "Nothing needed on the server, other than Python."
|
|
TextAnnotation "First-party modules (SSH keys, file and directory management, package repositories, stopping/starting/restarting services, DO/Linode/AWS integration)."
|
|
|
|
.. page::
|
|
|
|
Why Ansible?
|
|
============
|
|
|
|
- Familiar syntax (Drupal 8, Symfony, Sculpin)
|
|
- Easily readable
|
|
- No server dependencies
|
|
- Easy to add to an existing project
|
|
- Includes relevant modules (Git, Composer)
|
|
- Idempotency, resulting in cleaner scripts
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
Hosts / Inventories
|
|
|
|
.. page:: standardPage
|
|
|
|
hosts.ini
|
|
=========
|
|
|
|
.. code:: ini
|
|
|
|
[webservers]
|
|
192.168.33.10
|
|
|
|
[webservers:vars]
|
|
ansible_ssh_port=22
|
|
ansible_ssh_user=opdavies
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "Vagrant IP address."
|
|
TextAnnotation "Supports wildcards and ranges"
|
|
|
|
hosts.yml
|
|
=========
|
|
|
|
.. code-block:: yaml
|
|
|
|
---
|
|
all:
|
|
children:
|
|
webservers:
|
|
hosts:
|
|
192.168.33.10:
|
|
vars:
|
|
ansible_ssh_port: 22
|
|
ansible_ssh_user: opdavies
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "My prefered format."
|
|
TextAnnotation "More consistency across the project, easier to copy variables from other places such as playbooks."
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
Ad-hoc Commands
|
|
|
|
.. page::
|
|
|
|
.. class:: centredtitle
|
|
|
|
``ansible all -i hosts.yml
|
|
-m ping``
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "Single ad-hoc command."
|
|
TextAnnotation "-i = inventory"
|
|
TextAnnotation "-m = module"
|
|
|
|
.. page:: standardPage
|
|
|
|
.. code:: json
|
|
|
|
webservers | SUCCESS => {
|
|
"ansible_facts": {
|
|
"discovered_interpreter_python": "/usr/bin/python"
|
|
},
|
|
"changed": false,
|
|
"ping": "pong"
|
|
}
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
``ansible all -i hosts.yml
|
|
-m command
|
|
-a "git pull --chdir=/app"``
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "Update a codebase using "git pull""
|
|
TextAnnotation "-a = (additional) arguments"
|
|
TextAnnotation "--chdir = change directory"
|
|
|
|
.. page::
|
|
|
|
.. class:: centredtitle
|
|
|
|
``ansible all -i hosts.yml
|
|
-m git
|
|
-a "repo=https://github.com
|
|
/opdavies/dransible
|
|
--chdir=/app"``
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "Same example, but using the core "Git" module"
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
Playbooks
|
|
|
|
.. page:: standardPage
|
|
|
|
.. code-block:: yaml
|
|
|
|
---
|
|
- hosts: webservers
|
|
|
|
vars:
|
|
git_repo: https://github.com/opdavies/dransible
|
|
project_root_dir: /app
|
|
|
|
tasks:
|
|
- name: Update the code
|
|
git:
|
|
repo: '{{ git_repo }}'
|
|
dest: '{{ project_root_dir }}'
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "YAML file"
|
|
TextAnnotation "Collection of multiple tasks"
|
|
TextAnnotation "Can add and use variables"
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
``ansible-playbook main.yml
|
|
-i hosts.yml``
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "How do we run a playbook?"
|
|
TextAnnotation "Use the ansible-playbook command and specify the name of the playbook."
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
Roles: configuring a LAMP stack
|
|
|
|
.. page:: standardPage
|
|
|
|
requirements.yml
|
|
================
|
|
|
|
.. code-block:: yaml
|
|
|
|
---
|
|
- src: geerlingguy.apache
|
|
- src: geerlingguy.composer
|
|
- src: geerlingguy.mysql
|
|
- src: geerlingguy.php
|
|
- src: geerlingguy.php-mysql
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "Requirements file for Ansible roles"
|
|
TextAnnotation "Typically requirements.yml"
|
|
TextAnnotation "Pulled from Ansible Galaxy"
|
|
TextAnnotation "Equivilent to composer.json/Packagist in PHP"
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
``ansible-galaxy install
|
|
-r requirements.yml``
|
|
|
|
.. page:: standardPage
|
|
|
|
.. code-block:: yaml
|
|
|
|
# playbook.yml
|
|
|
|
---
|
|
- hosts: webservers
|
|
|
|
roles:
|
|
- geerlingguy.apache
|
|
- geerlingguy.mysql
|
|
- geerlingguy.php
|
|
- geerlingguy.php-mysql
|
|
- geerlingguy.composer
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "How do we use them?"
|
|
TextAnnotation "Add them to the playbook under 'roles'."
|
|
TextAnnotation "Ordering matters here!"
|
|
TextAnnotation "If these were ordered alphabetically then Composer install would fail because it would run before PHP is installed."
|
|
|
|
.. page::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# playbook.yml
|
|
|
|
---
|
|
vars:
|
|
apache_vhosts:
|
|
- servername: dransible
|
|
documentroot: /app/web
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "configuring the Apache role to install virtual hosts."
|
|
|
|
.. page::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# playbook.yml
|
|
|
|
---
|
|
vars:
|
|
php_version: 7.4
|
|
php_packages_extra:
|
|
- libapache2-mod-php{{ php_version }}
|
|
- libpcre3-dev
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "configuring PHP."
|
|
|
|
.. page::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# playbook.yml
|
|
|
|
---
|
|
vars:
|
|
mysql_databases:
|
|
- name: main
|
|
|
|
mysql_users:
|
|
- name: user
|
|
password: secret
|
|
priv: main.*:ALL
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "configuring MySQL databases and users."
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
``ansible-playbook provision.yml
|
|
-i hosts.yml``
|
|
|
|
.. page:: standardPage
|
|
|
|
.. code-block::
|
|
|
|
PLAY [Provision the webserver machines] ********************************************************************************
|
|
|
|
TASK [Gathering Facts] *************************************************************************************************
|
|
ok: [webservers]
|
|
|
|
TASK [geerlingguy.apache : Include OS-specific variables.] *************************************************************
|
|
ok: [webservers]
|
|
|
|
TASK [geerlingguy.apache : Include variables for Amazon Linux.]
|
|
skipping: [webservers]
|
|
|
|
TASK [geerlingguy.apache : Define apache_packages.] ********************************************************************
|
|
ok: [webservers]
|
|
|
|
TASK [geerlingguy.apache : include_tasks] ******************************************************************************
|
|
included: /Users/opdavies/.ansible/roles/geerlingguy.apache/tasks/setup-Debian.yml for webservers
|
|
|
|
TASK [geerlingguy.apache : Update apt cache.] **************************************************************************
|
|
changed: [webservers]
|
|
|
|
.. page::
|
|
|
|
.. code-block::
|
|
|
|
TASK [geerlingguy.composer : Ensure composer directory exists.] ********************************************************
|
|
ok: [webservers]
|
|
|
|
TASK [geerlingguy.composer : include_tasks] ****************************************************************************
|
|
skipping: [webservers]
|
|
|
|
TASK [geerlingguy.composer : include_tasks] ****************************************************************************
|
|
skipping: [webservers]
|
|
|
|
RUNNING HANDLER [geerlingguy.apache : restart apache] ******************************************************************
|
|
changed: [webservers]
|
|
|
|
RUNNING HANDLER [geerlingguy.mysql : restart mysql] ********************************************************************
|
|
changed: [webservers]
|
|
|
|
RUNNING HANDLER [geerlingguy.php : restart webserver] ******************************************************************
|
|
changed: [webservers]
|
|
|
|
RUNNING HANDLER [geerlingguy.php : restart php-fpm] ********************************************************************
|
|
skipping: [webservers]
|
|
|
|
PLAY RECAP *************************************************************************************************************
|
|
webservers : ok=111 changed=32 unreachable=0 failed=0 skipped=78 rescued=0 ignored=0
|
|
|
|
.. page::
|
|
|
|
.. image:: images/after-provision-1.png
|
|
:width: 24cm
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "IP address of server, Apache is installed and running."
|
|
|
|
.. page::
|
|
|
|
.. image:: images/after-provision-2.png
|
|
:width: 24cm
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "No application code on the server yet."
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
Basic deployment
|
|
|
|
.. page:: standardPage
|
|
|
|
.. class:: small
|
|
|
|
.. code-block:: yaml
|
|
|
|
# deploy.yml
|
|
|
|
---
|
|
tasks:
|
|
- name: Creating project directory
|
|
file:
|
|
path: /app
|
|
state: directory
|
|
|
|
- name: Uploading application
|
|
synchronize:
|
|
src: '{{ playbook_dir }}/../'
|
|
dest: /app
|
|
|
|
|
|
|
|
|
|
- name: Installing Composer dependencies
|
|
composer:
|
|
command: install
|
|
working_dir: /app
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "Using file module to create the directory"
|
|
TextAnnotation "Using synchronize module/rsync to upload the files"
|
|
TextAnnotation "Using Composer module to install dependencies. There are other possible values."
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
``ansible-playbook deploy.yml
|
|
-i hosts.yml``
|
|
|
|
.. page:: standardPage
|
|
|
|
.. image:: images/after-deploy-1.png
|
|
:width: 24cm
|
|
|
|
.. page:: standardPage
|
|
|
|
Disadvantages
|
|
=============
|
|
|
|
- Sensitive data stored in plain text
|
|
- Single point of failure
|
|
- No ability to roll back
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
Keeping secrets with Ansible Vault
|
|
|
|
.. page:: standardPage
|
|
|
|
.. code-block:: yaml
|
|
|
|
---
|
|
vars:
|
|
mysql_databases:
|
|
- name: main
|
|
|
|
mysql_users:
|
|
- name: user
|
|
password: secret
|
|
priv: main.*:ALL
|
|
|
|
.. page::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# provision_vault.yml
|
|
|
|
---
|
|
vault_database_name: main
|
|
vault_database_user: user
|
|
vault_database_password: secret
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
``ansible-vault encrypt
|
|
provision_vault.yml``
|
|
|
|
.. class:: centredtitle
|
|
|
|
``New Vault password:
|
|
Confirm New Vault password:
|
|
Encryption successful``
|
|
|
|
.. page:: standardPage
|
|
|
|
.. code-block::
|
|
|
|
$ANSIBLE_VAULT;1.1;AES256
|
|
63656632326165643137646334343537396533656565313032363262623962393861666438393539
|
|
6366336638316133373061306332303761383565343035330a373637373830356430353630356161
|
|
32313831663039343733343539636365386333303862363635323138346137666166356639323338
|
|
3264636538356634390a343766353661386666376362376439386630363664616166643364366335
|
|
62373530393933373830306338386539626565313364643133666131613138383431353638636334
|
|
39376437633462373934313236363662633832643138386433646230313465383337373031373137
|
|
61353963623364393134386335373731356337366464633531656435383161656435313530363234
|
|
37373865393839616534353165656463313961333532363537383263343364646534333032336337
|
|
3235
|
|
|
|
.. page::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# provision_vars.yml
|
|
|
|
---
|
|
database_name: '{{ vault_database_name }}'
|
|
database_user: '{{ vault_database_user }}'
|
|
database_password: '{{ vault_database_password }}'
|
|
|
|
.. page::
|
|
|
|
|
|
.. code-block:: yaml
|
|
|
|
# provision.yml
|
|
|
|
---
|
|
vars_files:
|
|
- vars/provision_vault.yml
|
|
- vars/provision_vars.yml
|
|
|
|
vars:
|
|
mysql_databases:
|
|
- '{{ database_name }}'
|
|
|
|
mysql_users:
|
|
- name: '{{ database_user }}'
|
|
password: '{{ database_password }}'
|
|
priv: '{{ database_name }}.*:ALL'
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
``ansible-playbook deploy.yml
|
|
-i hosts.yml
|
|
--ask-vault-pass``
|
|
|
|
.. page::
|
|
.. class:: centredtitle
|
|
|
|
``ansible-playbook deploy.yml
|
|
-i hosts.yml
|
|
--vault-password-file secret.txt``
|
|
|
|
.. page::
|
|
.. class:: centredtitle
|
|
|
|
Better deployments with Ansistrano
|
|
|
|
.. page:: standardPage
|
|
|
|
.. image:: images/ansistrano.png
|
|
:width: 24cm
|
|
|
|
.. page::
|
|
|
|
Features
|
|
========
|
|
|
|
- Multiple release directories
|
|
- Shared paths and files
|
|
- Customisable
|
|
- Multiple deployment strategies
|
|
- Multi-stage environments
|
|
- Prune old releases
|
|
- Rollbacks
|
|
|
|
.. page::
|
|
|
|
|
|
.. code-block:: yaml
|
|
|
|
# requirements.yml
|
|
|
|
---
|
|
- src: ansistrano.deploy
|
|
- src: ansistrano.rollback
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "to install Ansistrano, add the additional roles to the requirements.yml file"
|
|
|
|
.. page::
|
|
|
|
|
|
.. code-block:: yaml
|
|
|
|
# deploy.yml
|
|
|
|
---
|
|
- hosts: all
|
|
|
|
roles:
|
|
- ansistrano.deploy
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "add to roles within the playbook"
|
|
|
|
.. page::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# deploy.yml
|
|
|
|
---
|
|
vars:
|
|
project_deploy_dir: /app
|
|
|
|
ansistrano_deploy_to: '{{ project_deploy_dir }}'
|
|
ansistrano_deploy_via: git
|
|
ansistrano_git_branch: master
|
|
ansistrano_git_repo: 'git@github.com:opdavies/dransible'
|
|
|
|
.. page::
|
|
.. code-block::
|
|
|
|
PLAY [webservers] ******************************************************************************************************
|
|
|
|
TASK [Gathering Facts] *************************************************************************************************
|
|
ok: [webservers]
|
|
|
|
TASK [ansistrano.deploy : include_tasks] *******************************************************************************
|
|
|
|
TASK [ansistrano.deploy : include_tasks] *******************************************************************************
|
|
included: /Users/opdavies/.ansible/roles/ansistrano.deploy/tasks/setup.yml for webservers
|
|
|
|
TASK [ansistrano.deploy : ANSISTRANO | Ensure deployment base path exists] *********************************************
|
|
ok: [webservers]
|
|
|
|
TASK [ansistrano.deploy : ANSISTRANO | Ensure releases folder exists] **************************************************
|
|
ok: [webservers]
|
|
|
|
TASK [ansistrano.deploy : ANSISTRANO | Ensure shared elements folder exists] *******************************************
|
|
ok: [webservers]
|
|
|
|
TASK [ansistrano.deploy : ANSISTRANO | Ensure shared paths exists] *****************************************************
|
|
ok: [webservers] => (item=web/sites/default/files)
|
|
|
|
.. page::
|
|
|
|
.. code-block::
|
|
|
|
TASK [ansistrano.deploy : Update file permissions] *********************************************************************
|
|
changed: [webservers]
|
|
|
|
TASK [ansistrano.deploy : include_tasks] *******************************************************************************
|
|
|
|
TASK [ansistrano.deploy : include_tasks] *******************************************************************************
|
|
included: /Users/opdavies/.ansible/roles/ansistrano.deploy/tasks/cleanup.yml for webservers
|
|
|
|
TASK [ansistrano.deploy : ANSISTRANO | Clean up releases] **************************************************************
|
|
changed: [webservers]
|
|
|
|
TASK [ansistrano.deploy : include_tasks] *******************************************************************************
|
|
|
|
TASK [ansistrano.deploy : include_tasks] *******************************************************************************
|
|
included: /Users/opdavies/.ansible/roles/ansistrano.deploy/tasks/anon-stats.yml for webservers
|
|
|
|
TASK [ansistrano.deploy : ANSISTRANO | Send anonymous stats] ***********************************************************
|
|
skipping: [webservers]
|
|
|
|
PLAY RECAP *************************************************************************************************************
|
|
webservers : ok=33 changed=14 unreachable=0 failed=0 skipped=7 rescued=0 ignored=0
|
|
|
|
.. page::
|
|
|
|
.. code-block::
|
|
|
|
vagrant@dransible:/app$ ls -l
|
|
total 8
|
|
|
|
lrwxrwxrwx 1 26 Jul 19 00:15 current -> ./releases/20190719001241Z
|
|
drwxr-xr-x 5 4096 Jul 22 20:30 releases
|
|
drwxr-xr-x 4 4096 Jul 19 00:00 shared
|
|
|
|
.. page::
|
|
|
|
.. code-block::
|
|
|
|
vagrant@dransible:/app/releases$ ls -l
|
|
total 20
|
|
|
|
drwxr-xr-x 5 4096 Jul 22 20:30 .
|
|
drwxr-xr-x 4 4096 Jul 19 00:15 ..
|
|
drwxr-xr-x 10 4096 Jul 19 00:02 20190719000013Z
|
|
drwxr-xr-x 10 4096 Jul 19 00:14 20190719001241Z
|
|
drwxr-xr-x 9 4096 Jul 22 20:30 20190722203038Z
|
|
|
|
.. page::
|
|
|
|
|
|
.. code-block:: yaml
|
|
|
|
# rollback.yml
|
|
|
|
---
|
|
- hosts: all
|
|
|
|
roles:
|
|
- ansistrano.rollback
|
|
|
|
vars:
|
|
ansistrano_deploy_to: '{{ project_deploy_dir }}'
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
``ansible-playbook rollback.yml
|
|
-i hosts.yml``
|
|
|
|
.. page::
|
|
|
|
.. class:: centredtitle
|
|
|
|
Customising Ansistrano:
|
|
Build Hooks
|
|
|
|
.. page:: imagePage
|
|
|
|
.. image:: images/ansistrano-flow.png
|
|
:width: 18cm
|
|
|
|
.. raw:: pdf
|
|
|
|
TextAnnotation "Each step has a 'before' and 'after' step Ansistrano allows us to add more things by providing a path to a playbook and adding additional steps."
|
|
|
|
.. page:: standardPage
|
|
|
|
.. code-block:: yaml
|
|
|
|
# deploy.yml
|
|
|
|
---
|
|
vars:
|
|
ansistrano_after_symlink_shared_tasks_file: >
|
|
'{{ playbook_dir }}/deploy/after-symlink-shared.yml'
|
|
ansistrano_after_symlink_tasks_file: >
|
|
'{{ playbook_dir }}/deploy/after-symlink.yml'
|
|
ansistrano_after_update_code_tasks_file: >
|
|
'{{ playbook_dir }}/deploy/after-update-code.yml'
|
|
|
|
release_web_path: '{{ ansistrano_release_path.stdout }}/web'
|
|
release_drush_path: '{{ ansistrano_release_path.stdout }}/bin/drush'
|
|
|
|
.. page::
|
|
|
|
|
|
.. code-block:: yaml
|
|
|
|
# deploy/after-update-code.yml
|
|
|
|
---
|
|
- name: Install Composer dependencies
|
|
composer:
|
|
command: install
|
|
working_dir: '{{ ansistrano_release_path.stdout }}'
|
|
|
|
.. page::
|
|
|
|
|
|
.. code-block:: yaml
|
|
|
|
# deploy/after-symlink-shared.yml
|
|
|
|
---
|
|
- name: Run database updates
|
|
command: >
|
|
{{ release_drush_path }}
|
|
--root {{ release_web_path }}
|
|
updatedb
|
|
|
|
.. page::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# deploy/after-symlink.yml
|
|
|
|
---
|
|
- name: Rebuild Drupal cache
|
|
command: >
|
|
{{ release_drush_path }}
|
|
--root {{ release_web_path }}
|
|
cache-rebuild
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
Demo
|
|
|
|
.. page::
|
|
|
|
.. class:: centredtitle
|
|
|
|
Generating settings files per deployment
|
|
|
|
.. page:: standardPage
|
|
|
|
.. code-block:: yaml
|
|
|
|
# deploy_vars.yml
|
|
---
|
|
drupal_settings:
|
|
- drupal_root: /app/web
|
|
sites:
|
|
- name: default
|
|
settings:
|
|
databases:
|
|
default:
|
|
default:
|
|
driver: mysql
|
|
host: localhost
|
|
database: '{{ database_name }}'
|
|
username: '{{ database_user }}'
|
|
password: '{{ database_password }}'
|
|
hash_salt: '{{ hash_salt }}'
|
|
config_directories:
|
|
sync: ../config/sync
|
|
|
|
.. page::
|
|
|
|
.. code-block:: jinja
|
|
|
|
{# templates/settings.php.j2 #}
|
|
|
|
{% for key, values in item.1.settings.databases.items() %}
|
|
{% for target, values in values.items() %}
|
|
$databases['{{ key }}']['{{ target }}'] = array(
|
|
'driver' => '{{ values.driver|default('mysql') }}',
|
|
'host' => '{{ values.host|default('localhost') }}',
|
|
'database' => '{{ values.database }}',
|
|
'username' => '{{ values.username }}',
|
|
'password' => '{{ values.password }}',
|
|
);
|
|
|
|
{% endfor %}
|
|
{% endfor %}
|
|
|
|
{% if item.1.settings.base_url is defined %}
|
|
$base_url = '{{ item.1.settings.base_url }}';
|
|
{% endif %}
|
|
|
|
.. page::
|
|
|
|
|
|
.. code-block:: yaml
|
|
|
|
# tasks/main.yml
|
|
|
|
---
|
|
- name: Ensure directory exists
|
|
file:
|
|
state: directory
|
|
path: '{{ item.0.drupal_root }}/sites/{{ item.1.name|default("default") }}'
|
|
with_subelements:
|
|
- '{{ drupal_settings }}'
|
|
- sites
|
|
no_log: true
|
|
|
|
- name: Create settings files
|
|
template:
|
|
src: settings.php.j2
|
|
dest:
|
|
'{{ item.0.drupal_root }}/sites/{{ item.1.name|default("default") }}/{{
|
|
item.1.filename|default("settings.php") }}'
|
|
with_subelements:
|
|
- '{{ drupal_settings }}'
|
|
- sites
|
|
no_log: true
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
Multiple environments development, test, production
|
|
|
|
.. page:: standardPage
|
|
|
|
.. code-block:: yaml
|
|
|
|
# vars.yml
|
|
|
|
---
|
|
vars:
|
|
mysql_databases:
|
|
- name: production
|
|
- name: staging
|
|
|
|
mysql_users:
|
|
- name: production
|
|
password: '{{ live_db_password }}'
|
|
priv: '{{ live_db_name }}.*:ALL'
|
|
|
|
- name: staging
|
|
password: '{{ staging_db_password }}'
|
|
priv: staging.*:ALL
|
|
|
|
.. page::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# hosts.yml
|
|
|
|
---
|
|
production:
|
|
children:
|
|
hosts:
|
|
webservers:
|
|
ansible_ssh_host: 192.168.33.10
|
|
ansible_ssh_port: 22
|
|
|
|
project_deploy_path: /app
|
|
git_branch: production
|
|
|
|
drupal_hash_salt: '{{ vault_drupal_hash_salt }}'
|
|
drupal_install: false
|
|
|
|
drupal_settings:
|
|
# ...
|
|
|
|
.. page::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# hosts.yml
|
|
|
|
---
|
|
staging:
|
|
children:
|
|
hosts:
|
|
webservers:
|
|
ansible_ssh_host: 192.168.33.10
|
|
ansible_ssh_port: 22
|
|
|
|
project_deploy_path: /app-staging
|
|
git_branch: staging
|
|
|
|
drupal_hash_salt: '{{ vault_drupal_hash_salt }}'
|
|
drupal_install: true
|
|
|
|
drupal_settings:
|
|
# ...
|
|
|
|
.. page:: titlePage
|
|
|
|
.. class:: centredtitle
|
|
|
|
``ansible-playbook deploy.yml
|
|
-i hosts.yml
|
|
--limit staging``
|
|
|
|
.. page::
|
|
|
|
.. class:: centredtitle
|
|
|
|
``ansible-playbook deploy.yml
|
|
-i hosts.yml
|
|
--limit production``
|
|
|
|
.. page:: standardPage
|
|
|
|
Thanks!
|
|
=======
|
|
|
|
References:
|
|
|
|
- https://oliverdavies.link/ansible-repos
|
|
- https://docs.ansible.com
|
|
- https://www.ansistrano.com
|
|
- https://symfonycasts.com/screencast/ansistrano
|
|
|
|
|
|
|
|
|
Me:
|
|
|
|
* https://www.oliverdavies.uk
|