Teorie
Role jsou v Ansible od verze 1.2. Jejich smyslem je popsat ucelenou jednotku konfigurace tak, aby byla opakovatelně použitelná. Podobají se například třídám z Puppetu (jen je nelze vnořovat). Standardně jsou uloženy v adresáři /etc/ansible/roles/. Adresářová struktura obsahuje:
files/ handlers/ tasks/ templates/ vars/
Roli přiřadíte do scénáře v main.yml:
---
- hosts: webservers
roles:
- common
- apache
- {role: apache_vhost, vhost_domain=simplewebpage.com, vhost_dir=/var/www/simplewebpage.com/www}
Common a apache jsou zavolány bez parametrů. Apache_vhost je parametrizována, vytvoří apache vhost pro simplewebpage.com. Při přehrání Ansible hledá soubory tasks/main.yml, handlers/main.yml, vars/main.yml, pokud je najde, přidá je do hry. Moduly copy a script automaticky hledají soubory ve files/ a modul template v templates/, není potřeba uvádět cesty. Pojďme si to ukázat v praxi.
Praktická ukázka, role pro instalaci utilit
Při své práci mám pod správou několik instalací různých zákazníků. Abych si ulehčil převzetí správy u nového zákazníka, vytvořil jsem si roli common, s pomocí které nainstaluji na server základní utility, ssh klíč (i když bývá na server nahrán zákazníkem, toto mi umožní jej budoucnu snadno kdykoliv hromadně změnit) a zapnu si zobrazování syntaxe ve vim u root uživatele. V main.yml v tasks adresáři najdeme:
# /etc/ansible/roles/common/tasks/main.yml
---
- name: nahravam ssh klic
authorized_key: user=root state=present key={{ item }}
with_file:
- public-keys/david.karban.pub
- name: ladim .vimrc
lineinfile: dest=/root/.vimrc regexp=^syntax line="syntax on" create=yes
- name: instaluji utility na RHEL systemy
yum: name={{ item }} state=present
with_items: common_packages
when: ansible_os_family == "RedHat"
- name: instaluji utility na Debian systemy
apt: name={{ item }} state=present
with_items: common_packages
when: ansible_os_family == "Debian"
- name: instaluji dalsi utility na Debian systemy
apt: name={{ item }} state=present
with_items: common_debian_packages
when: ansible_os_family == "Debian"
Je vidět, že jde o běžnou hru s jedním rozdílem, vše v main.yml je automaticky bráno jako task. Ve hře jsou dodány další modifikátory kroků, které jsem zatím nezmínil. With_files nahraje data ze souboru,v tomto případě můj veřejný SSH klíč. Zde pozor, jak jsem psal, že u role se automaticky berou relativní cesty, tak to skutečně platí jen pro moduly copy, script a template, v tomto případě se bere cesta relativně k playbooku (tedy k /etc/ansible).
Další modul, lineinfile, umí změnit v cílovém souboru konkrétní řádek na základě regexp. výrazu, zde jej používám místo celého konfiguračního souboru, abych nepřemazal jiné zákaznické nastavení. V další části instaluji balíky, které obvykle používám, když je potřeba. Jejich seznam jsem uložil do proměnných, které jsou nadefinovány v adresáři vars, viz níže. Ansible nemá univerzální modul pro package, proto instaluji utility 2×, vždy s filtrem na druh distribuce, viz. sekce when. Fakt ansible_os_family obsahuje rodinu distribucí, tj. Debian platí i pro Ubuntu, Red Hat zase i pro CentOS a Scientific Linux. Samotná distribuce je ve faktu ansible_distribution.
Pokračujme k nadefinovaným proměnným.
# /etc/ansible/roles/common/vars/main.yml --- common_packages: - links - mc - strace common_debian_packages: - htop
Ansible nemá podporu pro lokální proměnné, proměnné nadefinované v rámci rolí jsou na stejné úrovni, jako například proměnné nadefinované ve scénáři v sekci vars. Aby se daly role jednoduše opakovaně použít, doporučuje se mít u proměnných prefix jejich názvu, aby byly unikátní. Proto oba seznamy balíčků začínají common_.
Postupné nasazení aplikace a práce s monitoringem
S dobře popsanými rolemi je instalace nového serveru velmi jednoduchá, prostě přidáme jeho hostname do hosts souboru do vhodné skupiny. Ale co v případě, že provádíme deploy aplikace, jak nám Ansible pomůže zde? Pomocí delegace a postupného zpracování scénáře. Standardně se scénáře aplikují na skupiny serverů paralelně, pomocí klíčového slova serial si vynutíme jejich postupné zpracování:
---
- hosts: webservers
serial: 1
roles:
- common
- apache
- {role: apache_vhost, vhost_domain=simplwebpage.com, vhost_dir=/var/www/simplewebpage.com/www}
Hodnota u atributu serial určuje kolik serverů ze skupiny najednou se má přehrávat. V tomto případě jde jeden po druhém. Abychom odstínili návštěvníky stránek od serverů, na kterých se zrovna provádí deploy, deaktivujeme jej na tu dobu z load balanceru (v tomto případě z haproxy) a z nagiosu, použijeme delegate_to:
- hosts: webservers
serial: 1
# Ukoly ke spusteni pred provedením update aplikace
pre_tasks:
- name: vypinam nagios alert pro web na serveru
nagios: action=disable_alerts host={{ ansible_hostname }} services=webserver
delegate_to: "{{ item }}"
with_items: groups.monitoring
- name: deaktivuji server v haproxy
shell: echo "disable server myapplb/{{ ansible_hostname }}" | socat stdio /var/lib/haproxy/stats
delegate_to: "{{ item }}"
with_items: groups.lbservers
roles:
.
.
.
# Ukoly po provedeni update aplikace
post_tasks:
- name: Aktivuji server v haproxy
shell: echo "enable server myapplb/{{ ansible_hostname }}" | socat stdio /var/lib/haproxy/stats
delegate_to: "{{ item }}"
with_items: groups.lbservers
- name: zapinam nagios alerty pro web na serveru
nagios: action=enable_alerts host={{ ansible_hostname }} services=webserver
delegate_to: "{{ item }}"
with_items: groups.monitoring
Úkoly vypnutí/zapnutí nagiosu a deaktivace/aktivace serveru v haproxy provádějí na všech monitoring serverech (definováni v hosts souboru jako skupina monitoring) a na všech load balancerech (skupina lbservers). Pre_tasks a post_tasks se provádějí na začátku, resp. konci scénáře před hlavní sekci tasks.
Protože není potřeba při každé změně konfigurace na serverech provádět i deploy, je v tomhle případě výhodné mít jej v samostatném scénáří. Ukázka takového skriptu je na GitHubu Ansible. Ukázku nastavení skupiny serverů, vč. haproxy a monitoringu najdete tamtéž.
Závěrem, aneb mé důvody pro Ansible
Vždy, když to lze, snažím se konfiguraci přenést do her a scénářů, abych ji mohl příště aplikovat jen dodáním role k serveru. Ptáte se jak je to odlišné od Puppet a Chef? Je to flexibilnější. Při své práci mám pod správou více druhů instalací u různých zákazníků. V Ansible pro ně mám jak individuální konfiguraci, tak specifické konfigurace jen pro ně. Proto jsem si vytvořil adresář customers, co zákazník to podadresář. Role, které mají všichni zákazníci stejně (základní utility, nastavení httpd.conf, nastavení ntp) mám v /etc/ansible/roles/ a do customers je jen symlinkuji.
Když takto přebírám správu serverů u nového zákazníka, tak příprava prostředí je jen dodání adres do hosts souborů. U zákazníka nemám nainstalován žádný dodatečný software, který by mu zabíral systémové prostředky. Ačkoliv bych mohl dosáhnout dost podobného s Puppetem, musel bych mít všechny manifesty v jednom stromu konfigurace a musel bych mít všude nainstalován Puppet agent. Pro mě zbytečně komplikované.