Vai al contenuto

Distribuzione Ansible con Ansistrano

In questo capitolo imparerai come distribuire applicazioni con il ruolo Ansible Ansistrano.


Obiettivi: In questo capitolo imparerai come:

✔ Implementare Ansistrano;
✔ Configurare Ansistrano;
✔ Usare cartelle e file condivisi tra le versioni distribuite;
✔ Distribuire diverse versioni di un sito da git;
✔ Reagire tra i passaggi di implementazione.

🏁 ansible, ansistrano, ruoli, distribuzioni

Conoscenza: ⭐ ⭐
Complessità: ⭐ ⭐ ⭐

Tempo di lettura: 40 minuti


Ansistrano è un ruolo Ansible per distribuire facilmente applicazioni PHP, Python, ecc. Si basa sulla funzionalità di Capistrano.

Introduzione

Ansistrano richiede quanto segue:

  • Ansible sulla macchina di distribuzione,
  • rsync o git sulla macchina client.

Può scaricare il codice sorgente da rsync, git, scp, http, S3, ...

Nota

Per il nostro esempio di distribuzione, utilizzeremo il protocollo git.

Ansistrano distribuisce applicazioni seguendo questi 5 passaggi:

  • Setup: crea la struttura delle directory per ospitare le release;
  • Update Code: scaricando la nuova release per gli obiettivi;
  • Symlink Shared e Symlink: dopo aver distribuito la nuova release, il link simbolico corrente è modificato per puntare a questa nuova versione;
  • Clean Up: per fare un po' di pulizia (rimuovi le vecchie versioni).

Fasi di una distribuzione

Lo scheletro di una distribuzione con Ansistrano assomiglia a questo:

/var/www/site/
├── current -> ./releases/20210718100000Z
├── releases
│   └── 20210718100000Z
│       ├── css -> ../../shared/css/
│       ├── img -> ../../shared/img/
│       └── REVISION
├── repo
└── shared
    ├── css/
    └── img/

Puoi trovare tutta la documentazione di Ansistrano sul suo repository Github.

Labs

Continuerai a lavorare sui tuoi 2 server:

Il server di gestione:

  • Ansible è già installato. Dovrai installare il ruolo ansistrano.deploy.

Il server gestito:

  • Sarà necessario installare Apache e distribuire il sito client.

Distribuzione del server Web

Per una maggiore efficienza, useremo il ruolo geerlingguy.apache per configurare il server:

$ ansible-galaxy role install geerlingguy.apache
Starting galaxy role install process
- downloading role 'apache', owned by geerlingguy
- downloading role from https://github.com/geerlingguy/ansible-role-apache/archive/3.1.4.tar.gz
- extracting geerlingguy.apache to /home/ansible/.ansible/roles/geerlingguy.apache
- geerlingguy.apache (3.1.4) was installed successfully

Probabilmente avremo bisogno di aprire alcune regole del firewall, quindi installeremo anche la collezione ansible.posix per lavorare con il suo modulo firewalld:

$ ansible-galaxy collection install ansible.posix
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/download/ansible-posix-1.2.0.tar.gz to /home/ansible/.ansible/tmp/ansible-local-519039bp65pwn/tmpsvuj1fw5/ansible-posix-1.2.0-bhjbfdpw
Installing 'ansible.posix:1.2.0' to '/home/ansible/.ansible/collections/ansible_collections/ansible/posix'
ansible.posix:1.2.0 was installed successfully

Una volta installato il ruolo e la collezione, possiamo creare la prima parte del nostro playbook, che sarà:

  • Installare Apache,
  • Creare una cartella di destinazione per il nostro vhost,
  • Creare un vhost di default,
  • Apri il firewall,
  • Avviare o riavviare Apache.

Considerazioni tecniche:

  • Distribuiremo il nostro sito nella cartella /var/www/site/.
  • Come vedremo più tardi, ansistrano creerà un collegamento simbolico corrente alla cartella di rilascio corrente.
  • Il codice sorgente da distribuire contiene una cartella html alla quale il vhost dovrebbe puntare. Il suo DirectoryIndex è index.htm.
  • La distribuzione è fatta da git, il pacchetto sarà installato.

Nota

Il target del nostro vhost sarà: /var/www/site/current/html.

Il nostro playbook per configurare il server: playbook-config-server.yml

---
- hosts: ansible_clients
  become: yes
  become_user: root
  vars:
    dest: "/var/www/site/"
    apache_global_vhost_settings: |
      DirectoryIndex index.php index.htm
    apache_vhosts:
      - servername: "website"
        documentroot: "{{ dest }}current/html"

  tasks:

    - name: create directory for website
      file:
        path: /var/www/site/
        state: directory
        mode: 0755

    - name: install git
      package:
        name: git
        state: latest

    - name: permit traffic in default zone for http service
      ansible.posix.firewalld:
        service: http
        permanent: yes
        state: enabled
        immediate: yes

  roles:
    - { role: geerlingguy.apache }

Il playbook può essere applicato al server:

$ ansible-playbook playbook-config-server.yml

Nota l'esecuzione dei seguenti compiti:

TASK [geerlingguy.apache : Ensure Apache is installed on RHEL.] ****************
TASK [geerlingguy.apache : Configure Apache.] **********************************
TASK [geerlingguy.apache : Add apache vhosts configuration.] *******************
TASK [geerlingguy.apache : Ensure Apache has selected state and enabled on boot.] ***
TASK [permit traffic in default zone for http service] *************************
RUNNING HANDLER [geerlingguy.apache : restart apache] **************************

Il ruolo geerlingguy.apache rende il nostro lavoro molto più facile prendendosi cura dell'installazione e della configurazione di Apache.

Puoi controllare che tutto funzioni usando curl:

$ curl -I http://192.168.1.11
HTTP/1.1 404 Not Found
Date: Mon, 05 Jul 2021 23:30:02 GMT
Server: Apache/2.4.37 (rocky) OpenSSL/1.1.1g
Content-Type: text/html; charset=iso-8859-1

Nota

Non abbiamo ancora distribuito alcun codice, quindi è normale che curl restituisca un codice HTTP 404. Ma possiamo già confermare che il servizio httpd sta funzionando e che il firewall è aperto.

Distribuzione del software

Ora che il nostro server è configurato, possiamo distribuire l'applicazione.

Per questo, useremo il ruolo ansistrano.deploy in un secondo playbook dedicato alla distribuzione delle applicazioni (per una maggiore leggibilità).

$ ansible-galaxy role install ansistrano.deploy
Starting galaxy role install process
- downloading role 'deploy', owned by ansistrano
- downloading role from https://github.com/ansistrano/deploy/archive/3.10.0.tar.gz
- extracting ansistrano.deploy to /home/ansible/.ansible/roles/ansistrano.deploy
- ansistrano.deploy (3.10.0) was installed successfully

Le fonti del software possono essere trovate nel repository github.

Creeremo un playbook playbook-deploy.yml per gestire la nostra distribuzione:

---
- hosts: ansible_clients
  become: yes
  become_user: root
  vars:
    dest: "/var/www/site/"
    ansistrano_deploy_via: "git"
    ansistrano_git_repo: https://github.com/alemorvan/demo-ansible.git
    ansistrano_deploy_to: "{{ dest }}"

  roles:
     - { role: ansistrano.deploy }
$ ansible-playbook playbook-deploy.yml

PLAY [ansible_clients] *********************************************************

TASK [ansistrano.deploy : ANSISTRANO | Ensure deployment base path exists] *****
TASK [ansistrano.deploy : ANSISTRANO | Ensure releases folder exists]
TASK [ansistrano.deploy : ANSISTRANO | Ensure shared elements folder exists]
TASK [ansistrano.deploy : ANSISTRANO | Ensure shared paths exists]
TASK [ansistrano.deploy : ANSISTRANO | Ensure basedir shared files exists]
TASK [ansistrano.deploy : ANSISTRANO | Get release version] ********************
TASK [ansistrano.deploy : ANSISTRANO | Get release path]
TASK [ansistrano.deploy : ANSISTRANO | GIT | Register ansistrano_git_result variable]
TASK [ansistrano.deploy : ANSISTRANO | GIT | Set git_real_repo_tree]
TASK [ansistrano.deploy : ANSISTRANO | GIT | Create release folder]
TASK [ansistrano.deploy : ANSISTRANO | GIT | Sync repo subtree[""] to release path]
TASK [ansistrano.deploy : ANSISTRANO | Copy git released version into REVISION file]
TASK [ansistrano.deploy : ANSISTRANO | Ensure shared paths targets are absent]
TASK [ansistrano.deploy : ANSISTRANO | Create softlinks for shared paths and files]
TASK [ansistrano.deploy : ANSISTRANO | Ensure .rsync-filter is absent]
TASK [ansistrano.deploy : ANSISTRANO | Setup .rsync-filter with shared-folders]
TASK [ansistrano.deploy : ANSISTRANO | Get current folder]
TASK [ansistrano.deploy : ANSISTRANO | Remove current folder if it's a directory]
TASK [ansistrano.deploy : ANSISTRANO | Change softlink to new release]
TASK [ansistrano.deploy : ANSISTRANO | Clean up releases]

PLAY RECAP ********************************************************************************************************************************************************************************************************
192.168.1.11               : ok=25   changed=8    unreachable=0    failed=0    skipped=14   rescued=0    ignored=0   

Tante cose fatte con sole 11 righe di codice!

$ curl http://192.168.1.11
<html>
<head>
<title>Demo Ansible</title>
</head>
<body>
<h1>Version Master</h1>
</body>
<html>

Controllo sul server

Ora puoi connetterti da ssh alla tua macchina client.

  • Crea un albero nella directory /var/www/site/:
$ tree /var/www/site/
/var/www/site
├── current -> ./releases/20210722155312Z
├── releases
│   └── 20210722155312Z
│       ├── REVISION
│       └── html
│           └── index.htm
├── repo
│   └── html
│       └── index.htm
└── shared

Nota che:

  • il current symlink alla release ./releases/20210722155312Z
  • la presenza di una directory shared
  • la presenza dei git repos in ./repo/

  • Dal server Ansible riavviare la distribuzione 3 volte, quindi controllare il client.

$ tree /var/www/site/
var/www/site
├── current -> ./releases/20210722160048Z
├── releases
│   ├── 20210722155312Z
│   │   ├── REVISION
│   │   └── html
│   │       └── index.htm
│   ├── 20210722160032Z
│   │   ├── REVISION
│   │   └── html
│   │       └── index.htm
│   ├── 20210722160040Z
│   │   ├── REVISION
│   │   └── html
│   │       └── index.htm
│   └── 20210722160048Z
│       ├── REVISION
│       └── html
│           └── index.htm
├── repo
│   └── html
│       └── index.htm
└── shared

Nota che:

  • ansistrano ha mantenuto le ultime 4 release,
  • il link current è collegato ora all'ultima release

Limita il numero di release

La variabile ansistrano_keep_releases è usata per specificare il numero di rilasci da mantenere.

  • Utilizzando la variabile ansistrano_keep_releases, mantieni solo 3 rilasci del progetto. Verifica.
---
- hosts: ansible_clients
  become: yes
  become_user: root
  vars:
    dest: "/var/www/site/"
    ansistrano_deploy_via: "git"
    ansistrano_git_repo: https://github.com/alemorvan/demo-ansible.git
    ansistrano_deploy_to: "{{ dest }}"
    ansistrano_keep_releases: 3

  roles:
     - { role: ansistrano.deploy }
---
$ ansible-playbook -i hosts playbook-deploy.yml

Sulla macchina client:

$ tree /var/www/site/
/var/www/site
├── current -> ./releases/20210722160318Z
├── releases
│   ├── 20210722160040Z
│   │   ├── REVISION
│   │   └── html
│   │       └── index.htm
│   ├── 20210722160048Z
│   │   ├── REVISION
│   │   └── html
│   │       └── index.htm
│   └── 20210722160318Z
│       ├── REVISION
│       └── html
│           └── index.htm
├── repo
│   └── html
│       └── index.htm
└── shared

Utilizzo di shared_path e shared_files

---
- hosts: ansible_clients
  become: yes
  become_user: root
  vars:
    dest: "/var/www/site/"
    ansistrano_deploy_via: "git"
    ansistrano_git_repo: https://github.com/alemorvan/demo-ansible.git
    ansistrano_deploy_to: "{{ dest }}"
    ansistrano_keep_releases: 3
    ansistrano_shared_paths:
      - "img"
      - "css"
    ansistrano_shared_files:
      - "logs"

  roles:
     - { role: ansistrano.deploy }

Sulla macchina client, crea il file log nella directory shared:

sudo touch /var/www/site/shared/logs

Quindi esegui il playbook:

TASK [ansistrano.deploy : ANSISTRANO | Ensure shared paths targets are absent] *******************************************************
ok: [192.168.10.11] => (item=img)
ok: [192.168.10.11] => (item=css)
ok: [192.168.10.11] => (item=logs/log)

TASK [ansistrano.deploy : ANSISTRANO | Create softlinks for shared paths and files] **************************************************
changed: [192.168.10.11] => (item=img)
changed: [192.168.10.11] => (item=css)
changed: [192.168.10.11] => (item=logs)

Sulla macchina client:

$  tree -F /var/www/site/
/var/www/site/
├── current -> ./releases/20210722160631Z/
├── releases/
│   ├── 20210722160048Z/
│   │   ├── REVISION
│   │   └── html/
│   │       └── index.htm
│   ├── 20210722160318Z/
│   │   ├── REVISION
│   │   └── html/
│   │       └── index.htm
│   └── 20210722160631Z/
│       ├── REVISION
│       ├── css -> ../../shared/css/
│       ├── html/
│       │   └── index.htm
│       ├── img -> ../../shared/img/
│       └── logs -> ../../shared/logs
├── repo/
│   └── html/
│       └── index.htm
└── shared/
    ├── css/
    ├── img/
    └── logs

Si prega di notare che l'ultima versione contiene 3 link: css, imge log

  • da /var/www/site/releases/css alla directory ../../shared/css/.
  • da /var/www/site/releases/img alla directory ../../shared/img/.
  • da /var/www/site/releases/logs al file ../../shared/logs.

Pertanto, i file contenuti in queste 2 cartelle e il file log sono sempre accessibili attraverso i seguenti percorsi:

  • /var/www/site/current/css/,
  • /var/www/site/current/img/,
  • /var/www/site/current/logs,

ma soprattutto saranno mantenuti da una release all'altra.

Usa una sottodirectory del repository per la distribuzione

Nel nostro caso, il repository contiene una cartella html, che contiene i file del sito.

  • Per evitare questo livello extra di directory, usa la variabile ansistrano_git_repo_tree specificando il percorso della sotto-directory da usare.

Non dimenticare di modificare la configurazione di Apache per tenere conto di questo cambiamento!

Modifica il playbook per la configurazione del server playbook-config-server.yml

---
- hosts: ansible_clients
  become: yes
  become_user: root
  vars:
    dest: "/var/www/site/"
    apache_global_vhost_settings: |
      DirectoryIndex index.php index.htm
    apache_vhosts:
      - servername: "website"
        documentroot: "{{ dest }}current/" # <1>

  tasks:

    - name: create directory for website
      file:
        path: /var/www/site/
        state: directory
        mode: 0755

    - name: install git
      package:
        name: git
        state: latest

  roles:
    - { role: geerlingguy.apache }

<1> Modifica questa riga

Cambia il playbook per la distribuzione playbook-deploy.yml

---
- hosts: ansible_clients
  become: yes
  become_user: root
  vars:
    dest: "/var/www/site/"
    ansistrano_deploy_via: "git"
    ansistrano_git_repo: https://github.com/alemorvan/demo-ansible.git
    ansistrano_deploy_to: "{{ dest }}"
    ansistrano_keep_releases: 3
    ansistrano_shared_paths:
      - "img"
      - "css"
    ansistrano_shared_files:
      - "log"
    ansistrano_git_repo_tree: 'html' # <1>

  roles:
     - { role: ansistrano.deploy }

<1> Modifica questa riga

  • Non dimenticare di eseguire entrambi i playbook

  • Controlla la macchina cliente:

$  tree -F /var/www/site/
/var/www/site/
├── current -> ./releases/20210722161542Z/
├── releases/
│   ├── 20210722160318Z/
│   │   ├── REVISION
│   │   └── html/
│   │       └── index.htm
│   ├── 20210722160631Z/
│   │   ├── REVISION
│   │   ├── css -> ../../shared/css/
│   │   ├── html/
│   │   │   └── index.htm
│   │   ├── img -> ../../shared/img/
│   │   └── logs -> ../../shared/logs
│   └── 20210722161542Z/
│       ├── REVISION
│       ├── css -> ../../shared/css/
│       ├── img -> ../../shared/img/
│       ├── index.htm
│       └── logs -> ../../shared/logs
├── repo/
│   └── html/
│       └── index.htm
└── shared/
    ├── css/
    ├── img/
    └── logs

<1> Notare l'assenza di html

Gestione del ramo o dei tag git

La variabile ansistrano_git_branch è usata per specificare un branch o un tag da distribuire.

  • Distribuisci il branch releases/v1.1.0:
---
- hosts: ansible_clients
  become: yes
  become_user: root
  vars:
    dest: "/var/www/site/"
    ansistrano_deploy_via: "git"
    ansistrano_git_repo: https://github.com/alemorvan/demo-ansible.git
    ansistrano_deploy_to: "{{ dest }}"
    ansistrano_keep_releases: 3
    ansistrano_shared_paths:
      - "img"
      - "css"
    ansistrano_shared_files:
      - "log"
    ansistrano_git_repo_tree: 'html'
    ansistrano_git_branch: 'releases/v1.1.0'

  roles:
     - { role: ansistrano.deploy }

Nota

Per divertirti, durante la distribuzione, puoi aggiornare il browser, per vedere in 'live' il cambiamento.

$ curl http://192.168.1.11
<html>
<head>
<title>Demo Ansible</title>
</head>
<body>
<h1>Version 1.0.1</h1>
</body>
<html>
  • Distribuisci il tag v2.0.0:
---
- hosts: ansible_clients
  become: yes
  become_user: root
  vars:
    dest: "/var/www/site/"
    ansistrano_deploy_via: "git"
    ansistrano_git_repo: https://github.com/alemorvan/demo-ansible.git
    ansistrano_deploy_to: "{{ dest }}"
    ansistrano_keep_releases: 3
    ansistrano_shared_paths:
      - "img"
      - "css"
    ansistrano_shared_files:
      - "log"
    ansistrano_git_repo_tree: 'html'
    ansistrano_git_branch: 'v2.0.0'

  roles:
     - { role: ansistrano.deploy }
$ curl http://192.168.1.11
<html>
<head>
<title>Demo Ansible</title>
</head>
<body>
<h1>Version 2.0.0</h1>
</body>
<html>

Azioni tra le fasi di implementazione

Una distribuzione con Ansistrano rispetta le seguenti fasi:

  • Setup
  • Update Code
  • Symlink Shared
  • Symlink
  • Clean Up

È possibile intervenire prima e dopo ciascuno di questi passi.

Fasi di una distribuzione

Un playbook può essere incluso attraverso le variabili fornite per questo scopo:

  • ansistrano_before_<task>_tasks_file
  • o ansistrano_after_<task>_tasks_file

  • Esempio semplice: invia un'email (o qualsiasi cosa desideri come la notifica di Slack) all'inizio della distribuzione:

---
- hosts: ansible_clients
  become: yes
  become_user: root
  vars:
    dest: "/var/www/site/"
    ansistrano_deploy_via: "git"
    ansistrano_git_repo: https://github.com/alemorvan/demo-ansible.git
    ansistrano_deploy_to: "{{ dest }}"
    ansistrano_keep_releases: 3
    ansistrano_shared_paths:
      - "img"
      - "css"
    ansistrano_shared_files:
      - "logs"
    ansistrano_git_repo_tree: 'html'
    ansistrano_git_branch: 'v2.0.0'
    ansistrano_before_setup_tasks_file: "{{ playbook_dir }}/deploy/before-setup-tasks.yml"

  roles:
     - { role: ansistrano.deploy }

Crea il file deploy/before-setup-tasks.yml:

---
- name: Send a mail
  mail:
    subject: Starting deployment on {{ ansible_hostname }}.
  delegate_to: localhost
TASK [ansistrano.deploy : include] *************************************************************************************
included: /home/ansible/deploy/before-setup-tasks.yml for 192.168.10.11

TASK [ansistrano.deploy : Send a mail] *************************************************************************************
ok: [192.168.10.11 -> localhost]
[root] # mailx
Heirloom Mail version 12.5 7/5/10.  Type ? for help.
"/var/spool/mail/root": 1 message 1 new
>N  1 root@localhost.local  Tue Aug 21 14:41  28/946   "Starting deployment on localhost."
  • Probabilmente dovrai riavviare alcuni servizi alla fine della distribuzione, per esempio per pulire la cache. Riavviamo Apache alla fine della distribuzione:
---
- hosts: ansible_clients
  become: yes
  become_user: root
  vars:
    dest: "/var/www/site/"
    ansistrano_deploy_via: "git"
    ansistrano_git_repo: https://github.com/alemorvan/demo-ansible.git
    ansistrano_deploy_to: "{{ dest }}"
    ansistrano_keep_releases: 3
    ansistrano_shared_paths:
      - "img"
      - "css"
    ansistrano_shared_files:
      - "logs"
    ansistrano_git_repo_tree: 'html'
    ansistrano_git_branch: 'v2.0.0'
    ansistrano_before_setup_tasks_file: "{{ playbook_dir }}/deploy/before-setup-tasks.yml"
    ansistrano_after_symlink_tasks_file: "{{ playbook_dir }}/deploy/after-symlink-tasks.yml"

  roles:
     - { role: ansistrano.deploy }

Crea il file deploy/after-symlink-tasks.yml:

---
- name: restart apache
  systemd:
    name: httpd
    state: restarted
TASK [ansistrano.deploy : include] *************************************************************************************
included: /home/ansible/deploy/after-symlink-tasks.yml for 192.168.10.11

TASK [ansistrano.deploy : restart apache] **************************************************************************************
changed: [192.168.10.11]

Come avete visto durante questo capitolo, Ansible può migliorare notevolmente la vita dell'amministratore di sistema. Ruoli molto intelligenti come Ansistrano sono dei "must haves" che diventano rapidamente indispensabili.

L'utilizzo di Ansistrano garantisce il rispetto delle buone pratiche di diffusione, riduce i tempi necessari per mettere in produzione un sistema ed evita il rischio di potenziali errori umani. La macchina funziona velocemente, bene, e raramente commette errori!