Configuratiebeheer 101: Het schrijven van Ansible Playbooks

Introductie

In het kort is serverconfiguratiebeheer (ook populair bekend als IT-automatisering) een oplossing om uw infrastructuurbeheer om te zetten in een codebase, waarin alle processen die nodig zijn voor het implementeren van een server worden beschreven in een reeks provisioning-scripts die kunnen worden versiebeheerd en gemakkelijk hergebruikt. Het kan aanzienlijk de integriteit van elke serverinfrastructuur in de loop van de tijd verbeteren.

In een vorige handleiding spraken we over de belangrijkste voordelen van het implementeren van een configuratiebeheerstrategie voor uw serverinfrastructuur, hoe configuratiebeheertools werken, en wat deze tools doorgaans gemeen hebben.

In dit deel van de serie zullen we u door het proces leiden van het automatiseren van serverprovisioning met behulp van Ansible, een configuratiebeheertool die een volledig automatiseringsframework en orkestratiemogelijkheden biedt, met behoud van het doel van ultieme eenvoud en minimalisme. We zullen ons richten op de taalterminologie, syntaxis en functies die nodig zijn om een vereenvoudigd voorbeeld te maken om de implementatie van een Ubuntu 18.04-webserver met Apache volledig te automatiseren.

De volgende lijst bevat alle stappen die we moeten automatiseren om ons doel te bereiken:

  1. Update de apt-cache
  2. Installeer Apache
  3. Maak een aangepaste document root-directory
  4. Plaats een index.html bestand in de aangepaste document root
  5. Pas een sjabloon toe om onze aangepaste virtuele host in te stellen
  6. Herstart Apache

We beginnen met het bekijken van de terminologie die wordt gebruikt door Ansible, gevolgd door een overzicht van de belangrijkste taalkenmerken die kunnen worden gebruikt om playbooks te schrijven. Aan het einde van de gids vindt u de inhoud van een volledig voorbeeld van provisioning om de stappen te automatiseren die zijn beschreven voor het instellen van Apache op Ubuntu 18.04.

Opmerking: deze gids is bedoeld om u kennis te laten maken met de Ansible-taal en hoe u playbooks kunt schrijven om uw serverprovisioning te automatiseren. Voor een meer introductieve kijk op Ansible, inclusief de stappen die nodig zijn voor het installeren en aan de slag gaan met deze tool, evenals hoe u Ansible-opdrachten en playbooks kunt uitvoeren, bekijk onze Hoe u Ansible installeert en configureert op Ubuntu 18.04 gids.

Aan de slag

Voordat we naar een meer praktische weergave van Ansible kunnen gaan, is het belangrijk dat we vertrouwd raken met belangrijke terminologie en concepten die door deze tool worden geïntroduceerd.

Terminologie

De volgende lijst bevat een snel overzicht van de meest relevante termen die worden gebruikt door Ansible:

  • Besturingsknooppunt: de machine waarop Ansible is geïnstalleerd, verantwoordelijk voor het uitvoeren van de provisioning op de servers die u beheert.
  • Inventaris: een INI-bestand dat informatie bevat over de servers die u beheert.
  • Playbook: een YAML-bestand met een reeks procedures die geautomatiseerd moeten worden.
  • Taak: een blok dat een enkele procedure definieert die moet worden uitgevoerd, bijvoorbeeld: het installeren van een pakket.
  • Module: een module abstraheert typisch een systeemtaak, zoals het omgaan met pakketten of het maken en wijzigen van bestanden. Ansible heeft een veelheid aan ingebouwde modules, maar u kunt ook aangepaste modules maken.
  • Rol: een set gerelateerde playbooks, sjablonen en andere bestanden, georganiseerd op een vooraf gedefinieerde manier om hergebruik en delen te vergemakkelijken.
  • Play: een provisioning die van begin tot einde wordt uitgevoerd, wordt een play genoemd.
  • Feiten: globale variabelen die informatie bevatten over het systeem, zoals netwerkinterfaces of besturingssysteem.
  • Handlers: gebruikt om wijzigingen in de servicestatus te activeren, zoals het opnieuw starten of herladen van een service.

Taakindeling

A task defines a single automated step that should be executed by Ansible. It typically involves the usage of a module or the execution of a raw command. This is how a task looks:

- name: This is a task
  apt: name=vim state=latest

Het name-gedeelte is eigenlijk optioneel, maar aanbevolen, omdat het verschijnt in de uitvoer van de provisioning wanneer de taak wordt uitgevoerd. Het apt-gedeelte is een ingebouwde Ansible-module die het beheer van pakketten op op Debian gebaseerde distributies abstract maakt. Deze voorbeeldtaak vertelt Ansible dat het pakket vim van staat moet veranderen naar latest, wat ervoor zal zorgen dat de pakketbeheerder dit pakket installeert als het nog niet is geïnstalleerd.

Opmaak van Playbook

Playbooks zijn YAML-bestanden met een reeks aanwijzingen om de provisioning van een server te automatiseren. Het volgende voorbeeld is een eenvoudig playbook dat twee taken uitvoert: de apt-cache bijwerken en vervolgens vim installeren:

---
- hosts: all
  become: true
  tasks:
     - name: Update apt-cache 
       apt: update_cache=yes

     - name: Install Vim
       apt: name=vim state=latest

YAML vertrouwt op inspringing om gegevensstructuren te serialiseren. Daarom moet je bij het schrijven van playbooks en vooral bij het kopiëren van voorbeelden extra voorzichtig zijn om de juiste inspringing te behouden.

Voor het einde van deze gids zullen we een meer realistisch voorbeeld van een playbook zien, uitvoerig uitgelegd. Het volgende gedeelte geeft een overzicht van de belangrijkste elementen en functies die kunnen worden gebruikt om Ansible-playbooks te schrijven.

Playbooks schrijven

Nu je bekend bent met de basisbegrippen en de algemene opmaak van playbooks en taken in Ansible, zullen we enkele functies van playbooks leren die ons kunnen helpen bij het maken van meer veelzijdige automatiseringen.

Werken met variabelen

Er zijn verschillende manieren waarop je variabelen kunt definiëren in Ansible. De eenvoudigste manier is door de vars-sectie van een playbook te gebruiken. Het onderstaande voorbeeld definieert bijvoorbeeld een variabele package die later wordt gebruikt binnen een taak:

---
- hosts: all
  become: true
  vars:
     package: vim
  tasks:
     - name: Install Package
       apt: name={{ package }} state=latest

De variabele package heeft een globale reikwijdte, wat betekent dat deze vanuit elk punt van de voorziening kan worden benaderd, zelfs vanuit opgenomen bestanden en sjablonen.

Gebruik van lussen

Lussen worden typisch gebruikt om een taak te herhalen met verschillende invoerwaarden. In plaats van 10 taken te maken voor het installeren van 10 verschillende pakketten, kun je een enkele taak maken en een lus gebruiken om de taak te herhalen met alle verschillende pakketten die je wilt installeren.

Om een lus binnen een taak te maken, voeg je de optie with_items toe met een array van waarden. De inhoud kan worden benaderd via de lusvariabele item, zoals getoond in het onderstaande voorbeeld:

- name: Install Packages
  apt: name={{ item }} state=latest
  with_items:
     - vim
     - git
     - curl  

Je kunt ook een array-variabele gebruiken om je items te definiëren:

---
- hosts: all
  become: true
  vars:
     packages: [ 'vim', 'git', 'curl' ]
  tasks:
     - name: Install Package
       apt: name={{ item }} state=latest
       with_items: "{{ packages }}"

Het gebruik van Conditionals

Conditionals kunnen worden gebruikt om dynamisch te beslissen of een taak moet worden uitgevoerd, op basis van een variabele of een uitvoer van een opdracht, bijvoorbeeld.

Het volgende voorbeeld zal alleen Debian-gebaseerde systemen uitschakelen:

- name: Shutdown Debian Based Systems
  command: /sbin/shutdown -t now
  when: ansible_os_family == "Debian"

De conditional when ontvangt als argument een expressie die moet worden geëvalueerd. De taak wordt alleen uitgevoerd als de expressie wordt geëvalueerd naar true. In ons voorbeeld hebben we een feit getest om te controleren of het besturingssysteem afkomstig is van de Debian-familie.

A common use case for conditionals in IT automation is when the execution of a task depends on the output of a command. With Ansible, the way we implement this is by registering a variable to hold the results of a command execution, and then testing this variable in a subsequent task. We can test for the command’s exit status (if failed or successful). We can also check for specific contents inside the output, although this might require the usage of regex expressions and string parsing commands.

Het volgende voorbeeld toont twee conditionele taken op basis van de uitvoer van een php -v-opdracht. We zullen de exit-status van de opdracht testen, aangezien we weten dat deze niet zal worden uitgevoerd als PHP niet is geïnstalleerd op deze server. Het gedeelte ignore_errors van de taak is belangrijk om ervoor te zorgen dat de provisioning doorgaat, zelfs wanneer de opdracht mislukt.

- name: Check if PHP is installed
  register: php_installed
  command: php -v
  ignore_errors: true

- name: This task is only executed if PHP is installed
  debug: var=php_install
  when: php_installed|success
  
- name: This task is only executed if PHP is NOT installed
  debug: msg='PHP is NOT installed'
  when: php_installed|failed

De gebruikte debug-module hier is een handige module voor het tonen van de inhoud van variabelen of debugberichten. Het kan ofwel een string afdrukken (bij het gebruik van het msg-argument) of de inhoud van een variabele afdrukken (bij het gebruik van het var-argument).

Werken met Templates

Templates worden meestal gebruikt om configuratiebestanden in te stellen, waardoor het gebruik van variabelen en andere functies mogelijk is die bedoeld zijn om deze bestanden veelzijdiger en herbruikbaar te maken. Ansible gebruikt de Jinja2 template-engine.

Het volgende voorbeeld is een sjabloon voor het instellen van een Apache virtuele host, waarbij een variabele wordt gebruikt voor het instellen van de documentroot voor deze host:

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot {{ doc_root }}

    <Directory {{ doc_root }}>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

De ingebouwde module template wordt gebruikt om de sjabloon vanuit een taak toe te passen. Als je het bovenstaande sjabloonbestand bijvoorbeeld vhost.tpl hebt genoemd en het in dezelfde map als je playbook hebt geplaatst, is dit hoe je de sjabloon zou toepassen om de standaard Apache virtuele host te vervangen:

- name: Change default Apache virtual host
  template: 
    src: vhost.tpl
    dest: /etc/apache2/sites-available/000-default.conf

Hanlers Definiëren en Activeren

Handlers worden gebruikt om een statuswijziging in een service te activeren, zoals een herstart of een stop. Ook al lijken ze misschien vrij veel op gewone taken, handlers worden alleen uitgevoerd wanneer ze eerder zijn geactiveerd vanuit een notify-instructie in een taak. Ze worden meestal gedefinieerd als een array in een handlers-sectie van het playbook, maar ze kunnen ook in afzonderlijke bestanden staan.

Laten we ons vorige voorbeeld van het gebruik van een sjabloon in overweging nemen, waar we een Apache virtuele host hebben ingesteld. Als je ervoor wilt zorgen dat Apache opnieuw wordt gestart na een wijziging in de virtuele host, moet je eerst een handler voor de Apache-service maken. Zo worden handlers gedefinieerd in een playbook:

handlers:
    - name: restart apache
      service: name=apache2 state=restarted
    
    - name: other handler
      service: name=other state=restarted

De name directive hier is belangrijk omdat het de unieke identifier van deze handler zal zijn. Om deze handler te activeren vanuit een taak, moet je de notify optie gebruiken:

- name: Change default Apache virtual host
  template: 
    src: vhost.tpl
    dest: /etc/apache2/sites-available/000-default.conf
  notify: restart apache

We hebben enkele van de belangrijkste functies gezien die je kunt gebruiken om te beginnen met het schrijven van Ansible-playbooks. In de volgende sectie duiken we in een meer realistisch voorbeeld van een playbook dat de installatie en configuratie van Apache op Ubuntu automatiseert.

Voorbeeld Playbook

Laten we nu eens kijken naar een playbook dat de installatie van een Apache-webserver automatiseert binnen een Ubuntu 18.04-systeem, zoals besproken in de inleiding van deze gids.

Het volledige voorbeeld, inclusief het sjabloonbestand voor het opzetten van Apache en een HTML-bestand dat door de webserver wordt geserveerd, is te vinden op Github. De map bevat ook een Vagrantfile waarmee je het playbook kunt testen in een vereenvoudigde opstelling, met behulp van een virtuele machine beheerd door Vagrant.

Inhoud van het Playbook

De volledige inhoud van het playbook is hier beschikbaar voor jouw gemak:

playbook.yml
  1. ---
  2. - hosts: all
  3. become: true
  4. vars:
  5. doc_root: /var/www/example
  6. tasks:
  7. - name: Update apt
  8. apt: update_cache=yes
  9. - name: Install Apache
  10. apt: name=apache2 state=latest
  11. - name: Create custom document root
  12. file: path={{ doc_root }} state=directory owner=www-data group=www-data
  13. - name: Set up HTML file
  14. copy: src=index.html dest={{ doc_root }}/index.html owner=www-data group=www-data mode=0644
  15. - name: Set up Apache virtual host file
  16. template: src=vhost.tpl dest=/etc/apache2/sites-available/000-default.conf
  17. notify: restart apache
  18. handlers:
  19. - name: restart apache
  20. service: name=apache2 state=restarted

Laten we elk deel van dit playbook in meer detail bekijken:

hosts: all
Het playbook begint met te verklaren dat het moet worden toegepast op alle hosts in uw inventaris (hosts: all). Het is mogelijk om de uitvoering van het playbook te beperken tot een specifieke host of een groep hosts. Deze optie kan worden overschreven bij uitvoeringstijd.

become: true
Het gedeelte become: true vertelt Ansible om privilege-escalatie (sudo) te gebruiken voor het uitvoeren van alle taken in dit playbook. Deze optie kan per taak worden overschreven.

vars
Definieert een variabele, doc_root, die later wordt gebruikt in een taak. Deze sectie kan meerdere variabelen bevatten.

taken
Het gedeelte waar de daadwerkelijke taken worden gedefinieerd. De eerste taak werkt de apt-cache bij, en de tweede taak installeert het pakket apache2.

De derde taak gebruikt de ingebouwde module file om een directory aan te maken om te dienen als onze documentroot. Deze module kan worden gebruikt om bestanden en mappen te beheren.

De vierde taak gebruikt de module copy om een lokaal bestand naar de externe server te kopiëren. We kopiëren een eenvoudig HTML-bestand om te dienen als onze website gehost door Apache.

handlers
Tenslotte hebben we de handlers-sectie, waar de services worden gedeclareerd. We definiëren de restart apache handler die wordt geactiveerd door de vierde taak, waar de Apache-template wordt toegepast.

Het uitvoeren van een Playbook

Zodra u de inhoud van dit playbook hebt gedownload naar uw Ansible-besturingsknooppunt, kunt u ansible-playbook gebruiken om het uit te voeren op een of meer knooppunten uit uw inventaris. Het volgende commando voert het playbook uit op alle hosts uit uw standaardinventarisbestand, met SSH-sleutelpaarverificatie om verbinding te maken als de huidige systeemgebruiker:

  1. ansible-playbook playbook.yml

U kunt ook -l gebruiken om de uitvoering te beperken tot een enkele host of een groep hosts uit uw inventaris:

  1. ansible-playbook -l host_or_group playbook.yml

Als u een andere SSH-gebruiker moet opgeven om verbinding te maken met de externe server, kunt u het argument -u gebruiker opnemen in dat commando:

  1. ansible-playbook -l host_or_group playbook.yml -u remote-user

Voor meer informatie over het uitvoeren van Ansible-opdrachten en playbooks kunt u onze handleiding raadplegen over Hoe Ansible te installeren en configureren op Ubuntu 18.04.

Conclusie

Ansible is een minimalistisch IT-automatiseringstool dat een lage leercurve heeft en YAML gebruikt voor zijn voorzieningsscripts. Het heeft een groot aantal ingebouwde modules die kunnen worden gebruikt om taken zoals het installeren van pakketten en werken met sjablonen abstract te maken. De vereenvoudigde infrastructuurvereisten en eenvoudige taal kunnen een goede keuze zijn voor degenen die beginnen met configuratiebeheer. Het kan echter wel enkele geavanceerde functies missen die je kunt vinden bij complexere tools zoals Puppet en Chef.

In het volgende deel van deze serie zullen we een praktisch overzicht zien van Puppet, een populair en goed gevestigd configuratiebeheerstool die een expressieve en krachtige aangepaste DSL op basis van Ruby gebruikt om voorzieningsscripts te schrijven.

Source:
https://www.digitalocean.com/community/tutorials/configuration-management-101-writing-ansible-playbooks