Návod: jak otestovat dostupné role a zjistit problémy před výrobou

Ahoj všichni!

Pracuji jako inženýr DevOps v hotelové rezervační službě. Ostrovok.ru. V tomto článku chci mluvit o našich zkušenostech s testováním ansible rolí.

Na Ostrovok.ru používáme ansible jako správce konfigurace. Nedávno jsme přišli na potřebu testovat role, ale jak se ukázalo, není k tomu tolik nástrojů - nejpopulárnější je snad framework Molecule, tak jsme se rozhodli ho použít. Ukázalo se ale, že jeho dokumentace o mnoha úskalích mlčí. Nenašli jsme dostatečně podrobný manuál v ruštině, a tak jsme se rozhodli napsat tento článek.

Návod: jak otestovat dostupné role a zjistit problémy před výrobou

Molekula

Molekula - rámec, který pomáhá testovat dostupné role.

Zjednodušený popis: Molekula vytvoří instanci na platformě, kterou určíte (cloud, virtuální stroj, kontejner; další podrobnosti naleznete v části Řidič), spustí na něm vaši roli, poté spustí testy a odstraní instanci. V případě selhání na jednom z kroků vás o tom Molekula bude informovat.

Nyní více.

Některé teorie

Zvažte dvě klíčové entity molekuly: Scénář a Ovladač.

Scénář

Skript obsahuje popis toho, co, kde, jak a v jakém pořadí se bude provádět. Jedna role může mít několik skriptů a každý je adresář podél cesty <role>/molecule/<scenario>, který obsahuje popis akcí požadovaných pro test. Musí být zahrnut skript default, který se automaticky vytvoří, pokud roli inicializujete pomocí molekuly. Názvy následujících skriptů jsou na vás.

Zavolá se sekvence testovacích akcí ve skriptu maticea ve výchozím nastavení je:

(Kroky označené ?, ve výchozím nastavení přeskočeno, pokud není určeno uživatelem)

  • lint - běžící lintry. Ve výchozím nastavení se používají yamllint и flake8,
  • destroy - odstranění instancí z posledního spuštění molekuly (pokud existují),
  • dependency? — instalace možné závislosti testované role,
  • syntax - kontrola syntaxe role pomocí ansible-playbook --syntax-check,
  • create - vytvoření instance,
  • prepare? — příprava instance; např. zkontrolovat/instalovat python2
  • converge — spuštění testované příručky,
  • idempotence - restartování příručky pro test idempotence,
  • side_effect? - akce přímo nesouvisející s rolí, ale nezbytné pro testy,
  • verify - spuštění testů výsledné konfigurace pomocí testinfra(výchozí) /goss/inspec,
  • cleanup? - (v nových verzích) - zhruba řečeno "čištění" externí infrastruktury ovlivněné molekulou,
  • destroy - Odstranění instance.

Tato sekvence pokrývá většinu případů, ale v případě potřeby ji lze změnit.

Každý z výše uvedených kroků lze spustit samostatně molecule <command>. Je však třeba si uvědomit, že pro každý takový cli-příkaz může existovat vlastní sekvence akcí, kterou můžete zjistit provedením molecule matrix <command>. Například při spuštění příkazu converge (spuštění testované příručky) budou provedeny následující akce:

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

Pořadí těchto akcí lze upravit. Pokud je něco ze seznamu již provedeno, bude přeskočeno. Aktuální stav, stejně jako konfiguraci instancí, Molecule ukládá do adresáře $TMPDIR/molecule/<role>/<scenario>.

Přidejte kroky pomocí ? můžete popsat požadované akce ve formátu ansible-playbook a vytvořit název souboru podle kroku: prepare.yml/side_effect.yml. Očekávejte tyto soubory Molekula bude ve složce skriptu.

Řidič

Ovladač je entita, kde se vytvářejí testovací instance.
Seznam standardních ovladačů, pro které má Molecule připravené šablony, je následující: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.

Ve většině případů jsou šablony soubory create.yml и destroy.yml ve složce skriptu, které popisují vytvoření a odstranění instance, resp.
Výjimkou jsou Docker a Vagrant, protože interakce s jejich moduly mohou probíhat bez výše uvedených souborů.

Za zvýraznění stojí Delegovaný ovladač, protože pokud se používá v souborech pro vytváření a mazání instance, je popsána pouze práce s konfigurací instancí, zbytek by měl popsat technik.

Výchozí ovladač je Docker.

Nyní přejděme k praxi a zvažte další funkce.

Začínáme

Jako „ahoj světe“ otestujme jednoduchou instalační roli nginx. Jako ovladač zvolíme docker – myslím, že většina z vás ho má nainstalovaný (a pamatujte, že docker je výchozí ovladač).

Připravit virtualenv a nainstalovat do něj molecule:

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

Dalším krokem je inicializace nové role.
Inicializace nové role, stejně jako nového skriptu, se provádí pomocí pří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

Ukázalo se, že je to typická ansible-role. Dále jsou všechny interakce s molekulami CLI vytvářeny z kořene role.

Podívejme se, co je v adresáři 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

Pojďme analyzovat konfiguraci molecule/default/molecule.yml (nahradit pouze obrázek dockeru):

---
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ávislost

Tato část popisuje zdroj závislostí.

Možné možnosti: galaxie, platí, skořápka.

Shell je pouze příkazový shell, který se používá v případě, že galaxie a pozlacené nepokryjí vaše potřeby.

Nebudu se zde dlouho zdržovat, je to dostatečně popsáno v dokumentace.

řidič

Jméno řidiče. Náš je docker.

nepouští

Linter je yamllint.

Užitečné možnosti v této části konfigurace jsou možnost zadat konfigurační soubor pro yamllint, předat proměnné prostředí nebo zakázat linter:

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

plošiny

Popisuje konfiguraci instancí.
V případě dockeru jako ovladače je Molecule iterována přes tuto sekci a každý prvek seznamu je dostupný v Dockerfile.j2 jako proměnná item.

V případě řidiče, který vyžaduje create.yml и destroy.yml, sekce je v nich dostupná jako molecule_yml.platforms, a iterace nad ním jsou již popsány v těchto souborech.

Vzhledem k tomu, že Molecule poskytuje kontrolu nad instancemi modulů, je třeba hledat také seznam možných nastavení. Modul se používá například pro docker docker_container_module. Které moduly jsou použity v jiných ovladačích, najdete v dokumentace.

Stejně jako příklady použití různých ovladačů lze nalézt v testech samotné molekuly.

Vyměňte zde centos: 7 na ubuntu.

proviant

"Dodavatel" - subjekt, který spravuje instance. V případě Molecule je to ansible, podpora pro ostatní se neplánuje, takže tuto sekci lze s výhradou nazvat ansible rozšířenou konfigurací.
Zde můžete specifikovat spoustu věcí, podle mého názoru zdůrazním hlavní body:

  • playbooky: můžete určit, které příručky se mají v určitých fázích používat.

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: Dostupné možnosti a proměnné prostředí

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

scénář

Název a popis sekvencí skriptů.
Výchozí matici akcí libovolného příkazu můžete změnit přidáním klávesy <command>_sequence a jako hodnotu pro to definováním seznamu kroků, které potřebujeme.
Řekněme, že chceme změnit pořadí akcí při spuštění příkazu playbook run: molecule converge

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

ověřovatel

Nastavení rámce pro testy a linter k němu. Výchozí linter je testinfra и flake8. Možné možnosti jsou stejné jako výše:

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

Vraťme se k naší roli. Pojďme upravit soubor tasks/main.yml k tomuto druhu:

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

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

A přidejte k tomu 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, zbývá pouze spustit (připomínám, že z kořene role):

> molecule test

Dlouhý výfuk pod spoilerem:

--> 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še jednoduchá role byla testována bez problémů.
Je třeba si uvědomit, že pokud se během práce vyskytnou problémy molecule test, pak, pokud jste nezměnili výchozí sekvenci, Molecule odstraní instanci.

Následující příkazy jsou užitečné pro ladění:

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

Stávající role

Přidání nového skriptu do existující role je z adresáře rolí s následujícími příkazy:

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

V případě, že se jedná o první scénář v roli, pak parametr -s lze vynechat, protože vytvoří skript default.

Závěr

Jak vidíte, Molecule není příliš složitá a pomocí vlastních šablon lze nasazení nového skriptu zredukovat na úpravy proměnných v příručkách pro vytváření a mazání instancí. Molekula se hladce integruje se systémy CI, což vám umožňuje zvýšit rychlost vývoje snížením času pro ruční testování herních knih.

Děkuji za pozornost. Pokud máte zkušenosti s testováním ansible rolí a nesouvisí to s Molekulou, řekněte nám o tom v komentářích!

Zdroj: www.habr.com

Přidat komentář