Skip to main content

WORKSHOP 3: Playbooks

In de vorige workshop leerden we ad-hoc commands gebruiken. Handig voor eenvoudige opdrachten maar ontoereikend voor complexe configuratie- of management taken. In zo'n gevallen zijn playbooks veel geschikter.

Met deze workshop zullen we playbooks gebruiken om Apache web servers te installeren met Ansible.

Doelstelling

In deze workshop leer je:

  • De volgende Ansible-modules begrijpen en gebruiken a.d.h.v. Playbooks:
    • apt module
    • service module
    • copy module

Workshop

Playbooks zijn bestanden die de gewenste configuratie en stappen beschrijven (desired state). Playbooks kunnen lange, complexe en manuele administratieve taken veranderen in eenvoudige, herhaalbare en voorspelbare configuraties.

Bij een playbook gaan we enkele van die verschillende ad-hoc-taken nemen, die je daarnet gebruikt hebt en deze in een herhaalbare set van plays en tasks plaatsen.

Een playbook kan meerdere plays hebben en een play kan meerdere tasks hebben. In zo'n task wordt dan een module aangesproken zie ook documentatie Playbooks.

Stap 1 - Playbook Basics

Playbooks zijn tekst-bestanden in YAML-stijl. Daarbij is het volgende van belang:

  • Een YAML-file (playbook) start steeds met 3 koppeltekens => ( --- ).
  • consequente indentatie (inspringen) m.b.v. spaties en géén tabs.

Verder zijn volgende keywords in de playbook belangrijk:

  • hosts: de host of groep uit de inventory waarop de playbook moet toegepast worden.
  • tasks: de bewerkingen die moeten worden uitgevoerd door Ansible-modules bijhorende opties
  • become:: privilege escalation => identiek als -b (become) in ad-hoc commands => sudo-rechten
caution

de volgorde binnen een playbook is belangrijk aangezien Ansible de plays en tasks sequentieel zoals in de playbook uitvoert.

Een playbook is ook (meestal) idempotent. Dit betekent dat het veilig is om eenzelfde playbook meerdere keren uit te voeren. Dit zou dus aanpassingen ook maar 1 keer uitvoeren. Er wordt m.a.w. op voorhand gekeken of de aanpassing nodig is of niet (zie ook).

note

de meeste Ansible-modules zijn effectief idempotent. Enkele uitzonderingen zijn dit door hun aard van werking niet. In dat geval moet je daar wel rekening mee houden.

Stap 2 - Een mappen- en bestandsstructuur voor je Playbook

Tijd voor je eerste playbook! In dit onderdeel stellen we een playbook samen die een Apache-webserver opzet in 3 stappen:

  1. apache2 package installeren
  2. de apache2 service activeren en starten
  3. een web.html bestand kopiëren naar elke host.

Er is een best practice wat betreft de mappen-structuur voor playbooks. Je kan dit best eens bekijken voor volgende ansible-projecten. Voor deze oefening houden we het nog even simpel.

  • Maak een nieuwe map in je home-folder aan met de naam ansible-files.
[student@ControlHost ~]$ cd ~
[student@ControlHost ~]$ mkdir ansible-files
[student@ControlHost ~]$ cd ansible-files
[student@ControlHost ansible-files]$
  • Voorzie een gelijkaardige ìnventory (yaml-stijl) met de naam "hosts.yml" in deze map zoals in de vorige workshop en laat de dummy-host achterwege.
hosts.yml
--- 
all:
hosts:
children:
web:
hosts:
node1:
ansible_host: <X.X.X.X>
node2:
ansible_host: <Y.Y.Y.Y>
node3:
ansible_host: <Z.Z.Z.Z>
vars:
ansible_user: ubuntu
ansible_password: PASSWORD
ansible_connection: ssh
ansible_become_pass: Azerty123
control:
hosts:
ansible-1:
ansible_host: <A.A.A.A>
vars:
ansible_user: student
ansible_password: PASSWORD
ansible_connection: local
vars:
ansible_port: 22
  • Maak ook een ansible.cfg bestand aan waarin opgegeven wordt dat je met deze inventory gaat werken.
  • Pas deze ansible.cfg ook hier zo aan dat ssh-fingerprints vertrouwd worden
ansible.cfg
[defaults]

inventory= ./hosts
host_key_checking=False


  • Maak een nieuw bestand aan met de naam apache.yml en start er je eerste play. Gebruik hiervoor vi, vim of Visual Studio Code om het bestand te bewerken.
apache.yml
---
- name: Apache server installed
hosts: node1
become: true

Bovenstaand fragment is makkelijk te begrijpen. In deze playbook werd:

  • een naam gegeven aan de play via het keyword name:.
  • bepaald op welke hosts deze playbook moet uitgevoerd worden via hosts.
  • aangegeven dat er met sudo-rechten moet gewerkt worden op de remote hosts via become.
note

het is in dit geval duidelijk dat er privilege escalation nodig is aangezien er voor het installeren van pakketten op een systeem steeds root-permissies (sudo-rechten) noodzakelijk zijn.

Nu we onze eerste play gestart hebben zullen we die verder aanvullen met een eerste task. Met de eerste taak gaan we verifiëren of de laatste versie van Apache aanwezig is en indien nodig deze te installeren.

  • Pas je playbook verder aan met de eerste taak binnen de play:
---
- name: Apache server installed
hosts: node1
become: true
tasks:
- name: Latest Apache version installed
ansible.builtin.apt:
name: apache2
state: latest

caution

Zoals eerder gezegd is de aliniëring van regels en keywords cruciaal. Zorg zeker dat de t in tasks verticaal gelijk staat met de b van become.
In principe zorgt een IDE (zoals vscode) daar automatisch voor.

tip

Ansible doet zelf een boolean-conversie, zodat je true ook als 'yes' of 'True' kan meegeven.
Om zeker geen problemen te hebben met automatische linting blijf je best bij de officiële manier om booleans te gebruiken in yaml: 'true' en 'false'

  • Het kan zeker handig zijn om je wat in te werken in de YAML-syntax (zie documentatie).
  • Een linter kan je helpen om de systax van je Ansible- of YAML-files te controleren. In Visual Studio Code kan je een Ansible extension installeren die je helpt via syntax-highlighting en linting. :::

In bovenstaand fragment voegden we volgende items toe:

  • Het keyword tasks: waaronder alle tasks zullen opgelijst worden binnen deze play

  • Een concrete task die we een eigen gekozen naam kunnen geven met name:. We refereren naar de gebruikte ansible-module via de modulenaam, hier apt. Elke nieuwe task onder tasks wordt gestart met een "-".

  • Binnen deze specifieke task geven we parameters mee door opnieuw in te springen:

    • name: om het pakket te definiëren dat we willen installeren met apt.
    • state: waarmee we willen meegeven in welke "status" de package zich moet bevinden
  • Sla je playbook op en verlaat je editor.

info

In deze (en volgende voorbeelden) gebruiken we steeds latest bij de state parameter. Meestal is dat geen goed idee aangezien je dan geen vat hebt op welke versie precies zal geïnstalleerd worden. Men kan beter aan version pinningdoen waarbij de exacte versie vastgelegd wordt.
Een linter, zoals ansible-lint zal je dat ook duidelijk maken (zie documentatie)

Stap 3 - Playbook starten

Een Ansible Playbook kan je starten door op de Control Host het commando ansible-playbook te gebruiken.

Maar vooraleer een playbook effectief te laten lopen is het een goed idee om de syntax van je playbook eens te controleren op fouten (ook als je een linter gebruikt kan dit een extra controle zijn).

  • Voer volgende controle uit:
[student@ControlHost ansible-files]$ ansible-playbook --syntax-check apache.yml
  • Als er geen fouten in vorige test zaten, kan je de playbook nu effectief uitvoeren:
[student@ControlHost ansible-files]$ ansible-playbook apache.yml

PLAY [Apache server installed] **********************************************************

TASK [Gathering Facts] *******************************************************************
ok: [node1]

// highlight-start
TASK [latest Apache version installed] ***************************************************
changed: [node1]
// highlight-end

PLAY RECAP **********************************************************
// highlight-next-line
node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Je output zou, zoals hierboven, geen fouten mogen aangeven maar wel een overzicht geven van de taken die uitgevoerd werden aangevuld met een recap (samenvatting). Er is ook een "built-in" taak met de naam Gathering Facts die automatisch uitgevoerd wordt bij het begin van élke play. Deze Gathering Facts verzamelt informatie van elke host die beheerd wordt in deze play. Deze info kunnen we later ook gebruiken (zie verdere workshops).

  • Connecteer even rechtstreeks met node1 via SSH en controleer of Apache geïnstaleerd werd.
[student@ControlHost ansible-files]$ ssh ubuntu@x.x.x.x
ubuntu@10.129.36.30 password:
ubuntu@IaC-Managed-Host-1:~$ dpkg -l apache2
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-==============-=================-============-=================================
// highlight-next-line
ii apache2 2.4.52-1ubuntu4.4 amd64 Apache HTTP Server

ubuntu@IaC-Managed-Host-1:~$ exit
[student@ControlHost ansible-files]$
note

Let op de ii, wat aangeeft dat het packet het pakket succesvol geïnstalleerd werd.

  • Controleer ook even het resultaat via een ad-hoc command:
[student@ControlHost ansible-files]$ ansible node1 -m command -a "dpkg -l apache2"
  • Voer je playbook van daarnet nogmaal uit en vergelijk de output met de eerste keer. De output zou van changed moeten veranderd zijn naar ok, en de kleur van geel naar groen. Daarnaast zou de PLAY RECAP nu ook aangepast moeten zijn. Vergelijk!
[student@ControlHost ansible-files]$ ansible-playbook apache.yml

Stap 4 - Playbook uitbreiden: Apache starten en activeren

In de volgende stap breiden we de playbook uit zodat de Apache service zal starten én dat deze ook bij elke reboot zal opgestart zijn. Voor dat laatste moeten we de service "enablen" (module documentatie "service")

  • Pas je playbook aan:
---
- name: Apache server installed
hosts: node1
become: true
tasks:
- name: Latest Apache version installed
ansible.builtin.apt:
name: apache2
state: latest

- name: Apache enable and running
ansible.builtin.service:
name: apache2
enabled: true
state: started

  • Zoals bij de vorige taak werden er nieuwe lijnen toegevoegd :

    • een 2de taak met eigen gekozen naam (nieuwe taak is een nieuw item in de lijst => "-".
    • de module werd gespecifieerd (service)
    • parameters voor de module werden meegegeven:
  • Deze aangepaste playbook kunnen we nogmaals uitvoeren:

[student@ControlHost ansible-files]$ ansible-playbook apache.yml

Bekijk de output. Daar zou je nu de nieuwe taak TASK [Apache enable and running] moeten toegevoegd zien.

  • Gebruik een ad-hoc command om te controleren of de service effectief online en enabled is met systemctl status apache2.

Stap 5 - Playbook uitbreiden: Een webpagina toevoegen

Als in de vorige stap Apache effectief goed gestart werd dan kunnen daar proberen naartoe te surfen. Met een ad-hoc command dat we lokaal gaan uitvoeren op onze Ansible Control Host maken we een http-request naar onze webserver op node1 (uiteraard zou je ook met een gewone browser moeten kunnen surfen naar deze webserver).

[student@ControlHost ansible-files]$ ansible localhost -m uri -a "url=http://A.A.A.A"
localhost | SUCCESS => {
"accept_ranges": "bytes",
"changed": false,
"connection": "close",
"content_length": "10671",
"content_type": "text/html",
"cookies": {},
"cookies_string": "",
"date": "Tue, 18 Apr 2023 11:51:37 GMT",
"elapsed": 0,
"etag": "\"29af-5f99adb6c9004\"",
"last_modified": "Tue, 18 Apr 2023 11:46:25 GMT",
"msg": "OK (10671 bytes)",
"redirected": false,
"server": "Apache/2.4.52 (Ubuntu)",
"status": 200,
"url": "http://10.129.24.184",
"vary": "Accept-Encoding"
}
[student@ControlHost ansible-files]$

Je zou hier als resultaat opnieuw een SUCCESS moeten krijgen. Verifieer ook de HTTP status code die 200 zou moeten zijn voor een succesvolle request. De default installatie van Apache geeft immers een default informatie pagina weer. We willen nu via Ansible deze default pagina aanpassen met een eigen HTML-pagina.

  • Maak in de huidige projectmap een submap aan met de naam files en creëer er een tekstbestand met de naam web.html:
[student@ControlHost ansible-files]$ mkdir files
[student@ControlHost ansible-files]$ nano ./files/web.html
<body>
<h1>Apache is running fine</h1>
</body>

Je gebruikte al eerder de Ansible module copy in een ad-hoc command. Nu gaan we deze zelfde module gebruiken als task in een Playbook om een file te kopiëren van de lokale Ansible Control Host naar de webserver(s) (documentatie module copy).

  • Pas je playbook verder aan met opnieuw een nieuwe task:
---
- name: Apache server installed
hosts: node1
become: true
tasks:
- name: Latest Apache version installed
ansible.builtin.apt:
name: apache2
state: latest

- name: Apache enable and running
ansible.builtin.service:
name: apache2
enabled: true
state: started

- name: Copy html file
ansible.builtin.copy:
src: web.html
dest: /var/www/html/index.html

  • Voer je playbook nogmaals uit:
[student@ControlHost ansible-files]$ ansible-playbook apache.yml
info

Ondanks web.html in een subfolder zit (files) werd het pad naar deze subfolder niet opgegeven bij src. Toch wist Ansible het bestand te vinden. Ansible gaat steeds in de huidige directory én in de /files map op zoek naar de gewenste bestanden. Uiteraard kan je ook absolute en relatieve paden meegeven bij de src-parameter.

  • Controleer het resulaat eens via een browser. Surf naar je webserver node1 (http://X.X.X.X). De pagina zou nu de aangepaste tekst moeten vertonen.

Stap 6 - Ansible @ Full Force

Tot nu toe hebben we Ansible telkens gebruikt om de configuratie van 1 host aan te passen. Uiteraard komt Ansible tot zijn volle waarde als we de playbooks kunnen gebruiken t.o.v. meerdere hosts (datacenter,...) tegelijkertijd.

Zoals je nog weet hebben we in de inventory (nu met de naam hosts in projectfolder) een groep voorzien met 3 webservers in (node1,node2 en node3).

  • Pas je playbook aan zodat de play nu uitgevoerd zal worden op alle hosts van de groep web:
---
- name: Apache server installed
hosts: web
become: true
tasks:
- name: latest Apache version installed
apt:
name: apache2
state: latest
- name: Apache enable and running
service:
name: apache2
enabled: true
state: started
- name: copy html file
copy:
src: web.html
dest: /var/www/html/index.html

Het uitvoeren van deze playbook zal even wat langer duren omdat alle taken (installeren Apache,...) nog moeten uitgevoerd worden. In het resulterende overzicht zou je mooi moeten zien welke hosts al in orde waren (node1) en welke hosts nog aangepast werden (node2 en node3).

[student@ControlHost ansible-files]$ ansible-playbook apache.yml

PLAY [Apache server installed] ************************************************************************

TASK [Gathering Facts] ********************************************************************************
ok: [node1]
ok: [node2]
ok: [node3]

TASK [latest Apache version installed] ****************************************************************
ok: [node1]
changed: [node2]
changed: [node3]

TASK [Apache enable and running] **********************************************************************
ok: [node1]
ok: [node3]
ok: [node2]

TASK [copy html file] *********************************************************************************
ok: [node1]
changed: [node2]
changed: [node3]

// highlight-start
PLAY RECAP ********************************************************************************************
node1 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node2 : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node3 : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
// highlight-end
  • Controleer nu even of je effectief kan surfen via je browser (of via curl-command) naar de 2 nieuwe hosts.

  • Eventueel kan je bovenstaande met 1 ad-hoccommand uitvoeren:

[student@ControlHost ansible-files]$ ansible node2,node3 -m uri -a "url=http://localhost/"

Hiermee gebruik je de module uri om op elke host even een web-request te doen naar zijn eigen (localhost). Als resultaat zou je 2 maal SUCCESS moeten krijgen en 2 maal status code 200.

tip

Zoals je ziet wordt ons Ansible-project al iets uitgebreider. Om alles goed gestructureerd bij te houden kan je best de aanbevolen mappen-structuur van Ansible volgen. Bekijk zeker eens de aanbevelingen daarover op de documentatie-site van Ansible. Dit zal je zeker nodig hebben bij je project.

(C) Deze workshop werd gebaseerd op de informatie van Red Hat Ansible Automation Platform