Introductie
Tools zoals CFEngine, Puppet, Chef en Ansible hebben infrastructure automation ingrijpend veranderd door een gestructureerd framework te bieden voor het organiseren en delen van configuration management code, waarbij Ansible een van de nieuwere tools is. Ansible wordt breed gebruikt om de configuratie van je IT-omgeving te automatiseren en is zeer flexibel. De makers noemen Ansible “open-source automation die simpel, flexibel en krachtig is”. Wat Ansible code betrouwbaar maakt, is daarmee nog niet duidelijk. En zo simpel als het klinkt is het in de praktijk niet altijd—zeker niet in complexere omgevingen en setups.
Binnen Ansible zijn roles een concept waarmee je code inkapselt. In roles zitten tasks, variables, handlers en files, samengebracht in een modulaire, herbruikbare unit die, als je hem goed schrijft, eenvoudig te delen is tussen projecten en teams. Of je nu een handvol servers beheert of complexe (multi)cloudomgevingen orkestreert: roles helpen je schonere, beter onderhoudbare code te schrijven. Tegelijk is het lastig om een role te ontwerpen die je betrouwbaar en compleet zou noemen. In dit blog geven we daarom vuistregels voor een betrouwbare Ansible role, zodat je betere roles schrijft.
Een aantal vuistregels
De vuistregels in deze blog zijn:
- De main.yml van een role gebruik je alleen om tasks te includen of importeren
- Alle tasks moeten geoptimaliseerd zijn voor efficiëntie
- Data en code moeten volledig gescheiden zijn
- Tasks moeten op include-basis draaien
- Roles moeten check mode correct afhandelen
- Alle tasks moeten idempotent zijn
- Variabelen moeten een correcte namespace hebben en hiërarchisch zijn
- Tags moeten geïmplementeerd én gedocumenteerd zijn
- Roles moeten gevalideerd worden
- Roles moeten gedocumenteerd worden
Let op: dit zijn vuistregels. Afhankelijk van het gebruik en de requirements van een role kunnen sommige regels minder relevant zijn of juist extra regels nodig zijn. Over het algemeen zijn ze in veel situaties toepasbaar. Hieronder werken we ze één voor één uit, met context en voorbeelden.
Illustraties met een role: linux_user
Om de vuistregels te illustreren maken we een role die Linux-users kan aanmaken. Dat doen we als volgt:
# Creation of a basic role structure using ansible-galaxy
ansible-galaxy role init linux_user
# Navigate into the new role
cd linux_user/
# Create a supplementary tasks file
touch tasks/linux_user.yml
Kopieer code
Onze directorystructuur ziet er nu als volgt uit:
# ls ./*
./README.md
./defaults:
main.yml
./files:
./handlers:
main.yml
./meta:
main.yml
./tasks:
linux_user.yml main.yml
./templates:
./tests:
inventory test.yml
./vars:
main.yml
Kopieer code
Dit biedt ons een basisstructuur om mee te werken. In main.yml nemen we de taken op voor alle user-gerelateerde taken:
# cat tasks/main.yml
---
- name: Include Linux user tasks
loop: "{{ linux_users }}"
loop_control:
loop_var: user
label: "{{ user }}"
tags:
- linux_user
- linux_user_authorized_keys
- linux_user_create
when:
- ansible_facts["os_family"] in linux_user_allowed_os_families
- linux_user_allowed_target_hostgroups | intersect(group_names)
ansible.builtin.include_tasks: linux_user.yml
Kopieer code
Het hierboven opgenomen linux_user.yml-bestand bevat de daadwerkelijke taken voor het aanmaken van users.
# cat tasks/linux_user.yml
---
- name: Create user "{{ user.name }}"
become: true
notify:
- "Send mail notification about user creation"
tags:
- linux_user
- linux_user_create
ansible.builtin.user:
# Required
name: "{{ user.name }}"
# Optional
append: "{{ user.append | default(omit) }}"
comment: "{{ user.comment | default(omit) }}"
create_home: "{{ user.create_home | default(omit) }}"
force: "{{ user.force | default(omit) }}"
group: "{{ user.group | default(omit) }}"
groups: "{{ user.groups | default(omit) }}"
hidden: "{{ user.hidden | default(omit) }}"
home: "{{ user.home | default(omit) }}"
password: "{{ user.password | default(omit) }}"
password_expire_account_disable: "{{ user.password_expire_account_disable | default(omit) }}"
password_expire_max: "{{ user.password_expire_max | default(omit) }}"
password_expire_min: "{{ user.password_expire_min | default(omit) }}"
password_expire_warn: "{{ user.password_expire_warn | default(omit) }}"
password_lock: "{{ user.password_lock | default(omit) }}"
remove: "{{ user.remove | default(omit) }}"
shell: "{{ user.shell | default(omit) }}"
state: "{{ user.state | default(omit) }}"
system: "{{ user.system | default(omit) }}"
uid: "{{ user.uid | default(omit) }}"
umask: "{{ user.umask | default(omit) }}"
update_password: "{{ user.update_password | default(omit) }}"
- name: Set authorized keys for user "{{ user.name }}"
become: true
loop: "{{ user.authorized_keys | default(linux_user_default_authorized_keys) }}"
loop_control:
loop_var: key
label: "{{ key.key }}"
tags:
- linux_user
- linux_user_authorized_keys
ansible.posix.authorized_key:
# Required
key: "{{ key.key }}"
user: "{{ user.name }}"
# Optional
comment: "{{ key.comment | default(omit) }}"
exclusive: "{{ key.exclusive | default(omit) }}"
key_options: "{{ key.key_options | default(omit) }}"
path: "{{ key.path | default(omit) }}"
state: "{{ key.state | default(omit) }}"
Kopieer code
In het bovenstaande takenbestand is een handler geconfigureerd met de naam ‘Send mail notification about user creation’, die er als volgt uitziet: