WORKSHOP 5: Conditionals, Handlers and Loops
Nu we de basis wat vast hebben kunnen we onze Ansible Playbooks wat intelligenter en flexibelder maken.
Doelstelling
In deze workshop leer je:
- Conditionals gebruiken ⇒ voorwaarden inbouwen in je playbook
- Handlers toepasssen ⇒ Taken uitvoeren, enkel indien nodig.
- Loops gebruiken ⇒ iteratieve opdrachten uitvoeren.
Workshop
Stap 1 - Conditionals
Ansible kan gebruik maken van conditionals om taken uit te voeren enkel wanneer aan bepaalde voorwaarden voldaan werd.
Om een conditional te implementeren gebruiken we het keyword when bij een task in de playbook, gevolgd door een voorwaarde.
De conditie maakt steeds gebruik van de gekende operators: ==, !=, >, >=, < en <=
Meer informatie kan je hierover vinden in de documentatie.
Als voorbeeld om dit duidelijk te maken zullen we via Ansible een FTP-server installeren enkel als de host in de inventory groep ftpserver zit.
Om dit te doen passen we eerst onze inventory wat aan. We voegen er een nieuwe groep ftpserver aan toe en plaatsen node2 daarbij.
-
Pas je
hosts-file aan met de nieuwe groep. Zorg er ook voor dat de variabelen onderwebop het hoogste niveau komen. De specifieke variabelen voor de groepcontrolnemen toch de overhand voor de hosts in die groep.hosts.yml---all:hosts:children:web:hosts:node1:ansible_host: X.X.X.Xnode2:ansible_host: Y.Y.Y.Ynode3:ansible_host: Z.Z.Z.Zftpserver:hosts:node2:ansible_host: A.A.A.Acontrol:hosts:ansible-1:ansible_host: B.B.B.Bvars:ansible_user: studentansible_password: studentansible_connection: localansible_become_pass: studentvars:ansible_port: 22ansible_user: ubuntuansible_password: Azerty123ansible_connection: sshansible_become_pass: Azerty123 -
Maak nu ook een nieuwe playbook aan met de naam
ftpserver.ymlin de basismap./ansible-files/van je project:ftpserver.yml---- name: Install vsftpd on ftpservershosts: web, ftpserverbecome: truetasks:- name: Install FTP server when host in ftpserver groupansible.builtin.apt:name: vsftpdstate: latestwhen: inventory_hostname in groups["ftpserver"]
inventory_hostname is de naam van de host in de inventory (in ons geval hosts.yml). Dit is één van de speciale variabelen in Ansible (zie documentatie)
-
Voer deze playbook uit en bekijk de output:
TASK [Install FTP server when host in ftpserver group] *************************************************************skipping: [node1]skipping: [node3]// highlight-next-line[ok]: [node2]Als het goed is zou de ftp-service enkel op node2 mogen geïnstalleerd zijn, desondanks alle nodes uit zowel web als ftpserver opgegeven werden.
Stap 2 - Handlers
Soms gebeurt het dat, wanneer een taak een aanpassing doorvoert, er een extra taak moet uitgevoerd worden. Een typisch voorbeeld is een wijziging aan een bepaalde service (vb. web- of mysql server) en na die wijziging moet de service herstart worden zodat de wijziging actief wordt.
In bovenstaande geval kunnen we handlers gebruiken. Handlers kan je bekijken als inactieve taken die enkel getriggerd worden indien ze expliciet door een notify worden aangeroepen. Meer info daarover in de documentatie.
Om dit duidelijk te maken creëren we een nieuwe playbook die:
-
de configuratie van Apache aanpast in de het bestand
/etc/apache2/ports.conf -
de Apache-service herstart, enkel als er een "wijziging" was in het configuratiebestand
ports.conf -
Haal eerst een Apache configuratiebestand voor de
tcp port-configuratie binnen op je Control Host zodat we deze later kunnen aanpassen en via Ansible naar de host kopiëren. We gebruiken hier de Ansible modulefetchom zo'n bestand (ports.conf) van een reeds bestaande webserver binnen te halen naar onze Ansible Control host:[student@ControlHost ansible-files]$ ansible node1 -m fetch -a "src=/etc/apache2/ports.conf dest=./files/ flat=yes"Controleer of het
ports.confbestand zich nu ook effectief bevindt in de map./ansible-files/files/. -
Maak een nieuwe playbook aan met de naam
apache2_port_conf.ymlin je projectfolder./ansible-files/:apache2_port_conf.yml---- name: Manage port.confhosts: webbecome: truetasks:- name: Copy Apache port configuration fileansible.builtin.copy:src: ports.confdest: /etc/apache2notify:- Restart_apachehandlers:- name: Restart_apacheansible.builtin.service:name: apache2state: restarted -
Wat is er nieuw in bovenstaande playbook:
- De
notify-sectie roept de handler aan wanneer de taak effectief een aangepast bestand kopieert. Op deze manier zal de serviceapache2enkel herstart worden indien nodig en niet elke keer dat de playbook uitgevoerd zal worden. - Een
handlers-sectie die aangeeft wat er moet gebeuren indien deze aangeroepen wordt door denotify.
- De
-
Voer de playbook uit. We hebben op dit moment eigenlijk nog geen veranderingen aangebracht. Dus in het resultaat zouden enkel
oklijnen mogen voorkomen en geenchanged. De handler zal dus ook nog niet uitgevoerd worden.TASK [Copy Apache port configuration file] *******************************************ok: [node2]ok: [node3]ok: [node1]PLAY RECAP **********************************************************************node1 : ok=2 changed=0 unreachable=0node2 : ok=2 changed=0 unreachable=0node3 : ok=2 changed=0 unreachable=0 -
Pas nu in de
ports.confde webserver-poort aan:Listen 8080 -
Laat je playbook nog eens lopen. De output van je playbook zou nu wat interessanter moeten zijn:
-
ports.conf zou moeten gekopieerd zijn
-
de
handlerzou Apache moeten herstart hebben.
-
Apache zou nu moeten "luisteren" op poort 8080
-
controleer even via het eerder gebruikte
ad-hoccommand:[student@ControlHost ansible-files]$ ansible web -m command -a "curl http://localhost"...[student@ControlHost ansible-files]$ ansible web -m command -a "curl http://localhost:8080" -
Pas gerust het bestand
ports.confnog eens aan, of zet het terug naar de default-port 80
Stap 3 - Eenvoudige Loops
Loops worden gebruikt om dezelfde taak meerdere keren te herhalen. Een typisch voorbeeld is het aanmaken van gebruikers (met hun wachtwoord, mailadres, rechten,etc...). Dit kan nu makkelijk uitgevoerd worden in een Ansible playbook met 1 tasks. Meer info over loops kan je vinden in de documentatie.
Om het principe van loops aan te tonen willen we drie nieuwe users aanmaken op node1.
-
Maak opnieuw een playbook aan met de naam
loop_users.ymlin de projectmap./ansible-files. Je gebruikt hiervoor de moduleuser(zie documentatie).loop_users.yml---- name: Ensure users are presenthosts: node1become: truetasks:- name: Ensure three users are presentansible.builtin.user:name: "{{ item }}"state: presentloop:- dev_user- qa_user- prod_user -
Probeer bovenstaande playbook te begrijpen:
-
de namen van de
userswerden niet direct aan de module gegeven. Er werd daarvoor een variabele{{ item }} gebruikt als parameter. -
Het keyword
loopbevat de eigenlijke gebruikersnamen. -
Tijdens de uitvoering van de playbook wordt deze taak maar één keer uitgevoerd maar er worden drie
changesdoorgevoerd.TASK [Ensure three users are present] ****************************************************************changed: [node1] => (item=dev_user)changed: [node1] => (item=qa_user)changed: [node1] => (item=prod_user)
-
Stap 4 - Loops over hashes
Stel dat de gebruikers die je wil toevoegen ook aan een bepaalde groep moeten toegevoegd worden. Met de module user kan je ook onmiddellijk de gebruiker in de juiste groep steken.
| Gebruiker | Groep |
|---|---|
| dev_user | ftp |
| qa_user | ftp |
| prod_user | apache |
Naast de parameter name kan de parameter groups gebruikt worden. Om de loop nu te voorzien van 2 waardes creëren we meerdere key/value pairs (hash). De variabele {{item}} zal nu gebruik moeten maken van de subkey {{ item.groups }}.
-
Herschrijf je playbook zodat eerst, indien nodig, de groepen aangemaakt worden en daarna de gebruikers ook in die correcte groepen toegevoegd worden:
loop_users.yml---- name: Ensure users are presenthosts: node1become: truetasks:- name: Ensure groups are presentansible.builtin.group:name: "{{ item }}"state: presentloop:- ftp- apache- name: Ensure three users are presentansible.builtin.user:name: "{{ item.username }}"state: presentgroups: "{{ item.groups }}"loop:- { username: "dev_user", groups: "ftp" }- { username: "qa_user", groups: ["ftp", "apache"]}- { username: "prod_user", groups: "apache" } -
Controleer na het uitvoeren of dit succesvol gelukt is:
[student@ControlHost ansible-files]$ ansible node1 -m command -a "groups qa_user"
© Deze workshop werd gebaseerd op de informatie van Red Hat Ansible Automation Platform