Con questo articolo vediamo come semplificare la configurazione iniziale di un nuovo server sia fisico che virtuale con l’utilizzo di Ansible.
Installazione ambiente per Ansible
Per poter proseguire è necessario avere un computer linux dov’è presente Ansible.
Per installare Ansible si può utilizzare il proprio pacchetto di distribuzione oppure utilizzare il comando pip e creare un virtualenv dove installare tutto all’ultima versione o alla versione desiderata senza modificare le librerie di sistema.
installazione prerequisiti
Prima installiamo, se non presente, python3 e pip:
- su sistemi RedHat >=8: sudo dnf install -y python3 python3-pip
- mentre su sistemi RedHat <8: sudo yum install -y python3 python3-pip (necessita il reposity epel)
- infine su sistemi Debian/Ubuntu: sudo install -y python3 python3-pip python3-venv
Ambiente virtuale venv per python
Ora da utente creiamo il nostro ambiente virtuale python:
cd $HOME && mkdir venv && python3 -m venv venv/ansible
Attiviamo il nostro ambiente virtuale python con:
source venv/ansible/bin/activate
A questo punto con questa shell abbiamo un nuovo ambiente python dove poter installare tutto quello che vogliamo senza interferire con quello di sistema.
Da ora in poi consideriamo di essere nel virtualenv oppure su un ambiente python dove poter installare librerie con pip.
Installazione Ansible, lint e molecule
Aggiorniamo all’ultima versione pip con:
pip install setuptools && pip install --upgrade pip
Installiamo ansible con:
pip install ansible
Possiamo testare il funzionamento con:
ansible -v
e controllare gli ansible_facts in localhost con:
ansible localhost -m setup
Per controllare la sintassi yaml e quella specifica di Ansible installiamo i parser:
pip install yamllint ansible-lint
Per testare le regole di Ansible in maniera automatica tramite Docker installiamo molecule:
pip install molecule docker molecule[docker]
Ruolo Ansible: configurazione iniziale
Passiamo quindi al progetto principale, ovvero la creazione di un ruolo Ansible per la configurazione iniziale.
cd $HOME && mkdir roles
cd roles
ansible-galaxy role init eniocarboni.test
cd eniocarboni.test
molecule init scenario -r eniocarboni.test -d docker
La prima cosa da fare è modificare i metadati nel file meta/main.yml aggiungendo o modificando dentro al tag galaxy_info:
galaxy_info:
role_name: test
namespace: eniocarboni
author: Your Name
company: Yours
...
Inoltre va modificato almeno la licenza e la compatibilità del ruolo con le varie distribuzioni:
license: GPL-3.0
platforms:
- name: Ubuntu
versions:
- all
A questo punto possiamo già testare il nostro ruolo con molecule che automaticamente utilizzerà Docker con una sua immagine di default (quay.io/centos/centos:stream8):
molecule test
dato che abbiamo installato sia yamllint che ansible-lint configuriamoli per l’uso con molecule:
cat <<EOF >>molecule/default/molecule.yml
lint: |
set -e
yamllint .
ansible-lint
EOF
cat <<EOF >.ansible-lint
---
exclude_paths:
- molecule
EOF
Rilanciando il test vedremo che molecule usa anche yamllint ed ansible-lint:
molecule test
Naturalmente possiamo lanciare i comandi anche direttamente senza molecule:
yamllint .
ansible-lint
test su una vm reale
Per testare su un server fisico o virtuale abbiamo bisogno di avere l’accesso via ssh con utente che abbia accesso a root tramite sudo o a root direttamente.
Quindi quel che ci servirà sarà:
- il nome del server o il suo ip: testserver;
- utente per accedere: user;
- il file di playbook: playbook.yml;
- il file inventory: inventory_hosts
Quindi basta fare:
mkdir -p tests/mytest
echo "testserver" >tests/mytest/inventory_hosts
cat <<EOF >tests/mytest/playbook.yml
---
- hosts: all
become: true
roles:
- eniocarboni.test
EOF
Per testare direttamente nel server testserver basta lanciare:
ansible-playbook -u root -i tests/mytest/inventory_hosts tests/mytest/playbook.yml
Task da effettuare
A questo punto dobbiamo aggiungere i task da fare sul server e li mettiamo nel file tasks/main.yml come ad esempio:
- installazione nuovi pacchetti;
- modifica del nome host;
- modifica del fuso orario;
- settaggio della lingua principale;
- …
Vediamo brevemente come fare:
cat <<EOF >tasks/main.yml
---
- import_tasks: update_packages.yml
- import_tasks: hostname.yml
- import_tasks: update_timezone.yml
- import_tasks: update_locale.yml
EOF
for f in update_packages hostname update_timezone update_locale
do
echo "---" >tasks/$f.yml
done
Completiamo quindi il primo file update_packages.yml con:
cat <<EOF >>tasks/update_packages.yml
- name: update apt cache
ansible.builtin.apt:
update_cache: yes
cache_valid_time: 3600
when: ansible_pkg_mgr == "apt"
- name: update yum cache
ansible.builtin.yum:
update_cache: yes
when: ansible_pkg_mgr == "yum"
- name: update dnf cache
ansible.builtin.dnf:
update_cache: yes
when: ansible_pkg_mgr == "dnf"
- name: Install new packages
ansible.builtin.package: "name={{ item }} state=latest" # noqa package-latest
with_items: "{{ packages | default([]) }}"
EOF
Basta quindi mettere nel file defaults/main.yml le variabili di default come ad esempio packages:
cat <<EOF >>defaults/main.yml
packages:
- wget
- openssh-server
EOF
Finalmente possiamo testare i primi task con molecule via docker o sul nostro server di test e verificare che tutto funzioni correttamente:
molecule test
INFO default scenario test matrix: dependency, lint, cleanup, destroy, syntax, create, prepare, converge, idempotence, side_effect, verify, cleanup, destroy
INFO Performing prerun...
...
INFO Running default > dependency
WARNING Skipping, missing the requirements file.
WARNING Skipping, missing the requirements file.
INFO Running default > lint
WARNING Loading custom .yamllint config file, this extends our internal yamllint config.
WARNING Failed to discover lintable files using git: fatal: non è un repository Git (né lo è alcun genitore fino al punto di mount /)
Mi fermo al limite del filesystem (l'opzione GIT_DISCOVERY_ACROSS_FILESYSTEM non è impostata).
INFO Running default > cleanup
WARNING Skipping, cleanup playbook not configured.
INFO Running default > destroy
INFO Sanity checks: 'docker'
PLAY [Destroy] *****************************************************************
TASK [Destroy molecule instance(s)] ********************************************
changed: [localhost] => (item=instance)
TASK [Wait for instance(s) deletion to complete] *******************************
FAILED - RETRYING: [localhost]: Wait for instance(s) deletion to complete (300 retries left).
ok: [localhost] => (item=instance)
TASK [Delete docker networks(s)] ***********************************************
PLAY RECAP *********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
INFO Running default > syntax
playbook: /home/enio/Progetti/ansible/eniocarboni.test/molecule/default/converge.yml
INFO Running default > create
PLAY [Create] ******************************************************************
TASK [Log into a Docker registry] **********************************************
skipping: [localhost] => (item=None)
skipping: [localhost]
TASK [Check presence of custom Dockerfiles] ************************************
ok: [localhost] => (item={'image': 'quay.io/centos/centos:stream8', 'name': 'instance', 'pre_build_image': True})
...
...
TASK [eniocarboni.test : update apt cache] *************************************
skipping: [instance]
TASK [eniocarboni.test : update yum cache] *************************************
skipping: [instance]
TASK [eniocarboni.test : update dnf cache] *************************************
ok: [instance]
TASK [eniocarboni.test : Install new packages] *********************************
changed: [instance] => (item=wget)
changed: [instance] => (item=openssh-server)
PLAY RECAP *********************************************************************
instance : ok=3 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
INFO Running default > idempotence
...
...
PLAY RECAP *********************************************************************
instance : ok=3 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
INFO Idempotence completed successfully.
INFO Running default > side_effect
WARNING Skipping, side effect playbook not configured.
INFO Running default > verify
INFO Running Ansible Verifier
PLAY [Verify] ******************************************************************
TASK [Example assertion] *******************************************************
ok: [instance] => {
"changed": false,
"msg": "All assertions passed"
}
PLAY RECAP *********************************************************************
instance : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
INFO Verifier completed successfully.
INFO Running default > cleanup
WARNING Skipping, cleanup playbook not configured.
INFO Running default > destroy
PLAY [Destroy] *****************************************************************
TASK [Destroy molecule instance(s)] ********************************************
changed: [localhost] => (item=instance)
TASK [Wait for instance(s) deletion to complete] *******************************
FAILED - RETRYING: [localhost]: Wait for instance(s) deletion to complete (300 retries left).
changed: [localhost] => (item=instance)
TASK [Delete docker networks(s)] ***********************************************
PLAY RECAP *********************************************************************
localhost : ok=2 changed=2 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
INFO Pruning extra files from scenario ephemeral directory
ansible-playbook -u root -i tests/mytest/inventory_hosts tests/mytest/playbook.yml
A questo punto dovremmo proseguire con tutti gli altri task che abbiamo lasciato vuoti ed aggiungerne eventualmente altri, operazione che non farò in questo articolo per non dilungarmi troppo.
Ruolo Ansible: eniocarboni.server_initial_setup
Per chi fosse interessato può utilizzare il mio ruolo presente su Ansible Galaxy eniocarboni.server_initial_setup installandolo con:
ansible-galaxy install eniocarboni.server_initial_setup
e controllando o clonando il codice su GitHub dove si trova anche tutta la documentazione su come utilizzarlo.
Questo ruolo è testato nelle varie versioni di Ubuntu e sistemi RedHat o equivalenti.
I task che esegue in questo momento sono (e sono tutti configurabili):
- aggiornamento della cache dei pacchetti;
- aggiornamento dei pacchetti all’ultima versione;
- installazione nuovi pacchetti;
- pacchetto per aggiornamento automatico via cron in base alla distribuzione;
- installazione di screen o tmux;
- modifica dell’hostname;
- timezone o fuso orario;
- modifica dei locali e di quello principale (lingua del server);
- aggiunta o cancellazione delle chiavi pubbliche ssh;
- aggiunta parametri sysctl;
- configurazione firewall (ufw su Debian/Ubuntu, Firewalld su sistemi RedHat o eventualmente iptables)
Riferimenti
- Ansible: altro da quoll e dal sito ufficiale;
- Python;
- pip;
- repository epel;
- Docker;
- molecule;
- yaml;
- yamllint;
- ansible-lint;
- ssh;
- Ubuntu;
- Debian;
- RedHat;
- eniocarboni.server_initial_setup su Ansible Galaxy e su GitHub