Hlavní navigace

Ansible: role a postupný deploy

David Karban

Z předchozích dílů našeho seriálu o Ansible víme, jak je možné provádět ad-hoc příkazy a nebo jak vytvořit scénáře pro složitější konfiguraci. Dnes si ukážeme, jak pomocí rolí provést dekompozici celého nastavení a konfiguraci Ansible rozdělit na menší opakovaně použitelné logické částí.

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é.

Našli jste v článku chybu?