Návod: ako otestovať Ansible role a zistiť problémy pred výrobou

Ahoj všetci!

Pracujem ako inžinier DevOps pre hotelovú rezervačnú službu. Ostrovok.ru. V tomto článku chcem hovoriť o našich skúsenostiach s testovaním rolí Ansible.

Na Ostrovok.ru používame ansible ako správcu konfigurácie. Nedávno sme dospeli k potrebe testovať roly, ale ako sa ukázalo, nie je na to veľa nástrojov - asi najobľúbenejší je rámec Molecule, takže sme sa ho rozhodli použiť. Ale ukázalo sa, že jeho dokumentácia o mnohých nástrahách mlčí. Nenašli sme dostatočne podrobného sprievodcu v ruštine, preto sme sa rozhodli napísať tento článok.

Návod: ako otestovať Ansible role a zistiť problémy pred výrobou

molekula

Molekula — rámec na pomoc pri testovaní rolí Ansible.

Zjednodušený popis: Molecule vytvorí inštanciu na platforme, ktorú určíte (cloud, virtuálny stroj, kontajner; ďalšie podrobnosti nájdete v časti Vodič), spustí na ňom vašu rolu, potom spustí testy a odstráni inštanciu. Ak dôjde k zlyhaniu jedného z krokov, Molekula vás na to upozorní.

Teraz viac.

Niektoré teórie

Uvažujme o dvoch kľúčových entitách molekuly: Scenár a Vodič.

Scenár

Skript obsahuje popis toho, čo, kde, ako a v akom poradí sa bude vykonávať. Jedna rola môže mať niekoľko skriptov a každý je adresár pozdĺž cesty <role>/molecule/<scenario>, ktorá obsahuje popisy činností požadovaných pre test. Musí existovať scenár default, ktorý sa automaticky vytvorí, ak rolu inicializujete pomocou Molecule. Názvy nasledujúcich skriptov sú podľa vášho uváženia.

Postupnosť testovacích akcií v skripte sa nazýva maticea predvolene je to takto:

(Označené kroky ?, sú predvolene preskočené, ak nie sú špecifikované používateľom)

  • lint - beh linters. Predvolene yamllint и flake8,
  • destroy — vymazanie inštancií z posledného spustenia Molecule (ak nejaké zostávajú),
  • dependency? — inštalácia prípustnej závislosti testovanej roly,
  • syntax - kontrola syntaxe roly pomocou ansible-playbook --syntax-check,
  • create - vytvorenie inštancie,
  • prepare? — príprava inštancie; napríklad kontrola/inštalácia python2
  • converge — spustenie testovanej príručky,
  • idempotence — znovu spustiť príručku pre test idempotencie,
  • side_effect? — činnosti, ktoré priamo nesúvisia s úlohou, ale sú potrebné pre testy,
  • verify — spustenie testov výslednej konfigurácie pomocou testinfra(predvolené) /goss/inspec,
  • cleanup? - (v nových verziách) - zhruba povedané „čistenie“ vonkajšej infraštruktúry ovplyvnenej molekulou,
  • destroy — vymazanie inštancie.

Táto sekvencia pokrýva väčšinu prípadov, ale v prípade potreby ju možno upraviť.

Každý z vyššie uvedených krokov je možné spustiť samostatne pomocou molecule <command>. Mali by ste však pochopiť, že pre každý takýto príkaz cli môže existovať vlastný sled akcií, ktorý môžete zistiť spustením molecule matrix <command>. Napríklad pri spustení príkazu converge (spustením testovanej príručky) sa vykonajú nasledujúce akcie:

$ molecule matrix converge
...
└── default         # название сценария
    ├── dependency  # установка зависимостей
    ├── create      # создание инстанса
    ├── prepare     # преднастройка инстанса
    └── converge    # прогон плейбука

Postupnosť týchto akcií je možné upraviť. Ak už bolo niečo zo zoznamu dokončené, bude to preskočené. Aktuálny stav, ako aj konfigurácia inštancie, sú uložené v adresári Molecule $TMPDIR/molecule/<role>/<scenario>.

Pridajte kroky s ? Môžete opísať požadované akcie vo formáte Ansible playbook a vytvoriť názov súboru podľa kroku: prepare.yml/side_effect.yml. Očakávajte, že tieto súbory Molecule budú v priečinku skriptov.

Vodič

Ovládač je entita, v ktorej sa vytvárajú inštancie pre testy.
Zoznam štandardných ovládačov, pre ktoré má Molecule pripravené šablóny, je: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.

Vo väčšine prípadov sú šablóny súbory create.yml и destroy.yml v priečinku skript, ktoré popisujú vytvorenie a odstránenie inštancie, resp.
Výnimkou sú Docker a Vagrant, pretože interakcie s ich modulmi môžu prebiehať bez vyššie uvedených súborov.

Za zvýraznenie stojí Delegovaný ovládač, pretože ak sa použije, v súboroch na vytváranie a odstraňovanie inštancií je popísaná iba práca s konfiguráciou inštancie, zvyšok by mal popísať inžinier.

Predvolený ovládač je Docker.

Teraz prejdime k praxi a zvážme ďalšie funkcie.

Začíname

Ako „ahoj svet“ otestujeme jednoduchú rolu inštalácie nginx. Ako ovládač zvolíme docker – myslím, že väčšina z vás ho má nainštalovaný (a pamätajte, že docker je predvolený ovládač).

Pripraviť virtualenv a nainštalujte ho do nej molecule:

> pip install virtualenv
> virtualenv -p `which python2` venv
> source venv/bin/activate
> pip install molecule docker  # molecule установит ansible как зависимость; docker для драйвера

Ďalším krokom je inicializácia novej roly.
Inicializácia novej roly, ako aj nového skriptu, sa vykonáva pomocou príkazu molecule init <params>:

> molecule init role -r nginx
--> Initializing new role nginx...
Initialized role in <path>/nginx successfully.
> cd nginx
> tree -L 1
.
├── README.md
├── defaults
├── handlers
├── meta
├── molecule
├── tasks
└── vars

6 directories, 1 file

Výsledkom je typická ansible rola. Ďalej, všetky interakcie s molekulami CLI sú vytvorené z koreňa roly.

Pozrime sa, čo je v adresári rolí:

> tree molecule/default/
molecule/default/
├── Dockerfile.j2  # Jinja-шаблон для Dockerfile
├── INSTALL.rst.   # Немного информации об установке зависимостей сценария
├── molecule.yml   # Файл конфигурации
├── playbook.yml   # Плейбук запуска роли
└── tests          # Директория с тестами стадии verify
    └── test_default.py

1 directory, 6 files

Pozrime sa na konfiguráciu molecule/default/molecule.yml (nahradíme iba obrázok docker):

---
dependency:
  name: galaxy
driver:
  name: docker
lint:
  name: yamllint
platforms:
  - name: instance
    image: centos:7
provisioner:
  name: ansible
  lint:
    name: ansible-lint
scenario:
  name: default
verifier:
  name: testinfra
  lint:
    name: flake8

závislosť

Táto časť popisuje zdroj závislostí.

Možnosti sú: galaxie, Giltar,škrupina.

Shell je jednoducho príkazový shell, ktorý môžete použiť, ak galaxia a pozlátenie nepokrývajú vaše potreby.

Nezostanem tu dlho, je to dostatočne popísané dokumentáciu.

vodič

Meno vodiča. Pre nás je to docker.

cupanina

Yamllint sa používa ako linter.

Užitočné možnosti v tejto časti konfigurácie sú možnosť zadať konfiguračný súbor pre yamllint, premenné prostredia dopredu alebo vypnúť linter:

lint:
  name: yamllint
  options:
    config-file: foo/bar
  env:
    FOO: bar
  enabled: False

plošiny

Popisuje konfiguráciu inštancií.
V prípade dockera ako ovládača sa Molecule iteruje cez túto sekciu a každý prvok zoznamu je dostupný v Dockerfile.j2 ako premenná item.

V prípade vodiča, v ktorom create.yml и destroy.yml, sekcia je v nich dostupná ako molecule_yml.platforms, a jej iterácie sú už popísané v týchto súboroch.

Keďže Molecule poskytuje správu inštancií modulom Ansible, mali by ste tam vyhľadať zoznam možných nastavení. Modul sa používa napríklad pre Docker docker_container_module. Ktoré moduly sa používajú v iných ovládačoch, nájdete v dokumentáciu.

Môžete tiež nájsť príklady použitia rôznych ovládačov v testoch samotnej molekuly.

Poďme nahradiť tu centos: 7 na ubuntu.

proviant

„Poskytovateľ“ je entita, ktorá spravuje inštancie. V prípade Molecule je to ansible, podpora pre ostatných sa neplánuje, takže túto sekciu možno s výhradou nazvať rozšírenou konfiguráciou ansible.
Je toho veľa, na čo by ste tu mohli poukázať, ale podľa môjho názoru zdôrazním hlavné body:

  • playbooks: Môžete určiť, ktoré zošity sa majú použiť v určitých fázach.

provisioner:
  name: ansible
  playbooks:
    create: create.yml
    destroy: ../default/destroy.yml
    converge: playbook.yml
    side_effect: side_effect.yml
    cleanup: cleanup.yml

provisioner:
  name: ansible
  config_options:
    defaults:
      fact_caching: jsonfile
    ssh_connection:
      scp_if_ssh: True

provisioner:
  name: ansible  
  connection_options:
    ansible_ssh_common_args: "-o 'UserKnownHostsFile=/dev/null' -o 'ForwardAgent=yes'"

  • možnosti: Ansible parametre a premenné prostredia

provisioner:
  name: ansible  
  options:
    vvv: true
    diff: true
  env:
    FOO: BAR

scenár

Názov a popis sekvencií skriptov.
Predvolenú maticu akcií príkazu môžete zmeniť pridaním kľúča <command>_sequence a ako hodnotu pre to definovať zoznam krokov, ktoré potrebujeme.
Povedzme, že chceme zmeniť postupnosť akcií pri spustení príkazu playbook run: molecule converge

# изначально:
# - dependency
# - create
# - prepare
# - converge
scenario:
  name: default
  converge_sequence:
    - create
    - converge

overovateľ

Nastavenie rámca pre testy a linter preň. Štandardne sa používa linter testinfra и flake8. Možné možnosti sú podobné ako vyššie:

verifier:
  name: testinfra
  additional_files_or_dirs:
    - ../path/to/test_1.py
    - ../path/to/test_2.py
    - ../path/to/directory/*
  options:
    n: 1
  enabled: False
  env:
    FOO: bar
  lint:
    name: flake8
    options:
      benchmark: True
    enabled: False
    env:
      FOO: bar

Vráťme sa k našej úlohe. Upravme súbor tasks/main.yml na tento pohľad:

---
- name: Install nginx
  apt:
    name: nginx
    state: present

- name: Start nginx
  service:
    name: nginx
    state: started

A pridajte testy molecule/default/tests/test_default.py

def test_nginx_is_installed(host):
    nginx = host.package("nginx")
    assert nginx.is_installed

def test_nginx_running_and_enabled(host):
    nginx = host.service("nginx")
    assert nginx.is_running
    assert nginx.is_enabled

def test_nginx_config(host):
    host.run("nginx -t")

Hotovo, zostáva už len bežať (od koreňa roly, dovoľte mi pripomenúť):

> molecule test

Dlhý výfuk pod spojlerom:

--> Validating schema <path>/nginx/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix

└── default
    ├── lint
    ├── destroy
    ├── dependency
    ├── syntax
    ├── create
    ├── prepare
    ├── converge
    ├── idempotence
    ├── side_effect
    ├── verify
    └── destroy

--> Scenario: 'default'
--> Action: 'lint'
--> Executing Yamllint on files found in <path>/nginx/...
Lint completed successfully.
--> Executing Flake8 on files found in <path>/nginx/molecule/default/tests/...
Lint completed successfully.
--> Executing Ansible Lint on <path>/nginx/molecule/default/playbook.yml...
Lint completed successfully.
--> Scenario: 'default'
--> Action: 'destroy'

    PLAY [Destroy] *****************************************************************

    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) deletion to complete] *******************************
    ok: [localhost] => (item=None)
    ok: [localhost]

    TASK [Delete docker network(s)] ************************************************

    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=1    unreachable=0    failed=0

--> Scenario: 'default'
--> Action: 'dependency'
Skipping, missing the requirements file.
--> Scenario: 'default'
--> Action: 'syntax'

    playbook: <path>/nginx/molecule/default/playbook.yml

--> Scenario: 'default'
--> Action: 'create'

    PLAY [Create] ******************************************************************

    TASK [Log into a Docker registry] **********************************************
    skipping: [localhost] => (item=None)

    TASK [Create Dockerfiles from image names] *************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Discover local Docker images] ********************************************
    ok: [localhost] => (item=None)
    ok: [localhost]

    TASK [Build an Ansible compatible image] ***************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Create docker network(s)] ************************************************

    TASK [Create molecule instance(s)] *********************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) creation to complete] *******************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    PLAY RECAP *********************************************************************
    localhost                  : ok=5    changed=4    unreachable=0    failed=0

--> Scenario: 'default'
--> Action: 'prepare'
Skipping, prepare playbook not configured.
--> Scenario: 'default'
--> Action: 'converge'

    PLAY [Converge] ****************************************************************

    TASK [Gathering Facts] *********************************************************
    ok: [instance]

    TASK [nginx : Install nginx] ***************************************************
    changed: [instance]

    TASK [nginx : Start nginx] *****************************************************
    changed: [instance]

    PLAY RECAP *********************************************************************
    instance                   : ok=3    changed=2    unreachable=0    failed=0

--> Scenario: 'default'
--> Action: 'idempotence'
Idempotence completed successfully.
--> Scenario: 'default'
--> Action: 'side_effect'
Skipping, side effect playbook not configured.
--> Scenario: 'default'
--> Action: 'verify'
--> Executing Testinfra tests found in <path>/nginx/molecule/default/tests/...
    ============================= test session starts ==============================
    platform darwin -- Python 2.7.15, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
    rootdir: <path>/nginx/molecule/default, inifile:
    plugins: testinfra-1.16.0
collected 4 items

    tests/test_default.py ....                                               [100%]

    ========================== 4 passed in 27.23 seconds ===========================
Verifier completed successfully.
--> Scenario: 'default'
--> Action: 'destroy'

    PLAY [Destroy] *****************************************************************

    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) deletion to complete] *******************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Delete docker network(s)] ************************************************

    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=2    unreachable=0    failed=0

Naša jednoduchá rola bola otestovaná bez problémov.
Stojí za to pamätať, že ak sa počas prevádzky vyskytnú problémy molecule test, potom ak ste nezmenili štandardnú sekvenciu, Molecule inštanciu vymaže.

Nasledujúce príkazy sú užitočné na ladenie:

> molecule --debug <command> # debug info. При обычном запуске Молекула скрывает логи.
> molecule converge          # Оставляет инстанс после прогона тестируемой роли.
> molecule login             # Зайти в созданный инстанс.
> molecule --help            # Полный список команд.

Existujúca rola

Do existujúcej roly sa pridá nový skript z adresára rolí s nasledujúcimi príkazmi:

# полный список доступных параметров
> molecule init scenarion --help
# создание нового сценария
> molecule init scenario -r <role_name> -s <scenario_name>

Ak je toto prvý skript v role, potom parameter -s možno vynechať, pretože sa vytvorí skript default.

Záver

Ako vidíte, Molecule nie je veľmi zložitá a pri použití vlastných šablón môžete zredukovať nasadenie nového skriptu na úpravu premenných v playbookoch na vytváranie a odstraňovanie inštancií. Molekula sa bez problémov integruje so systémami CI, čo vám umožňuje zvýšiť rýchlosť vývoja znížením času na manuálne testovanie zošitov.

Ďakujem za tvoju pozornosť. Ak máte skúsenosti s testovaním ansible rolí a nesúvisí to s Molecule, povedzte nám o tom v komentároch!

Zdroj: hab.com

Pridať komentár