Upute: kako testirati ansible uloge i saznati o problemima prije produkcije

Pozdrav svima!

Radim kao DevOps inženjer u servisu za rezervacije hotela. Ostrovok.ru. U ovom članku želim govoriti o našem iskustvu u testiranju ansible uloga.

Na Ostrovok.ru koristimo ansible kao konfiguracijski menadžer. Nedavno smo došli do potrebe da testiramo uloge, ali kako se pokazalo, nema toliko alata za to - najpopularniji je, možda, okvir Molecule, pa smo ga odlučili koristiti. Ali pokazalo se da njegova dokumentacija šuti o mnogim zamkama. Nismo mogli pronaći dovoljno detaljan priručnik na ruskom, pa smo odlučili napisati ovaj članak.

Upute: kako testirati ansible uloge i saznati o problemima prije produkcije

Molecule

Molekula - okvir koji pomaže u testiranju ansible uloga.

Pojednostavljeni opis: Molekul kreira instancu na platformi koju navedete (oblak, virtuelna mašina, kontejner; za više detalja pogledajte odeljak vozač), pokreće vašu ulogu na njemu, zatim pokreće testove i briše instancu. U slučaju kvara na jednom od koraka, Molekul će vas obavijestiti o tome.

Sada više.

Malo teorije

Razmotrite dva ključna entiteta Molekula: scenario i pokretač.

scenario

Skripta sadrži opis šta, gdje, kako i kojim redoslijedom će se izvoditi. Jedna uloga može imati nekoliko skripti, a svaka je direktorij duž putanje <role>/molecule/<scenario>, koji sadrži opise radnji potrebnih za test. Skripta mora biti uključena default, koji će se automatski kreirati ako inicijalizirate ulogu molekulom. Nazivi sljedećih skripti su na vama.

Poziva se niz radnji testiranja u skripti matrica, a po defaultu je:

(Označeni koraci ?, preskočeno prema zadanim postavkama ako nije navedeno od strane korisnika)

  • lint - trčanje lintera. Standardno se koriste yamllint и flake8,
  • destroy - brisanje instanci iz posljednjeg lansiranja molekula (ako ih ima),
  • dependency? — instalacija ansible zavisnosti testirane uloge,
  • syntax - provjera sintakse uloge koristeći ansible-playbook --syntax-check,
  • create - kreiranje instance,
  • prepare? — priprema instance; npr. provjeri/instaliraj python2
  • converge — lansiranje priručnika koji se testira,
  • idempotence - ponovno pokretanje priručnika za testiranje idempotencije,
  • side_effect? - radnje koje nisu direktno vezane za ulogu, ali su neophodne za testiranje,
  • verify - izvođenje testova rezultirajuće konfiguracije korištenjem testinfra(zadano) /goss/inspec,
  • cleanup? - (u novim verzijama) - grubo rečeno, "čišćenje" vanjske infrastrukture na koju utiče Molekul,
  • destroy - Brisanje instance.

Ovaj redoslijed pokriva većinu slučajeva, ali se može promijeniti ako je potrebno.

Svaki od gore navedenih koraka može se pokrenuti zasebno sa molecule <command>. Ali treba shvatiti da za svaku takvu kli-naredbu može postojati vlastiti slijed radnji, što možete saznati izvršavanjem molecule matrix <command>. Na primjer, kada se izvodi naredba converge (pokretanje playbook-a koji se testira), izvršit će se sljedeće radnje:

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

Redoslijed ovih radnji se može uređivati. Ako je nešto sa liste već urađeno, biće preskočeno. Trenutno stanje, kao i konfiguraciju instanci, Molecule pohranjuje u direktorij $TMPDIR/molecule/<role>/<scenario>.

Dodajte korake sa ? možete opisati željene radnje u ansible-playbook formatu i napraviti naziv datoteke prema koraku: prepare.yml/side_effect.yml. Očekujte ove datoteke Molekul će biti u fascikli skripte.

vozač

Upravljački program je entitet u kojem se kreiraju testne instance.
Lista standardnih drajvera za koje Molecule ima spremne šablone je sljedeća: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.

U većini slučajeva, predlošci su datoteke create.yml и destroy.yml u fascikli skripte koja opisuje kreiranje i brisanje instance, respektivno.
Izuzetak su Docker i Vagrant, jer se interakcije s njihovim modulima mogu dogoditi bez gore navedenih datoteka.

Vrijedno je istaknuti Delegirani drajver, jer ako se koristi u datotekama za kreiranje i brisanje instance, opisan je samo rad sa konfiguracijom instanci, ostalo bi trebao opisati inženjer.

Podrazumevani drajver je Docker.

Sada pređimo na praksu i razmotrimo dalje karakteristike.

Prvi koraci

Kao "zdravo svijete", hajde da testiramo jednostavnu nginx instalacijsku ulogu. Odabraćemo docker kao drajver - mislim da ga je većina vas instalirala (i zapamtite da je docker podrazumevani drajver).

Pripremite se virtualenv i instalirati u njega molecule:

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

Sljedeći korak je inicijalizacija nove uloge.
Inicijalizacija nove uloge, kao i nove skripte, vrši se pomoću naredbe 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

Ispalo je tipična ansible-uloga. Nadalje, sve interakcije s CLI Molecules su napravljene iz korijena uloge.

Hajde da vidimo šta se nalazi u direktorijumu uloga:

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

1 directory, 6 files

Hajde da analiziramo konfiguraciju molecule/default/molecule.yml (zamijenite samo docker sliku):

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

zavisnost

Ovaj odjeljak opisuje izvor ovisnosti.

Moguće opcije: galaksija, pozlata, školjka.

Shell je samo komandna ljuska koja se koristi u slučaju da galaksija i pozlata ne pokriju vaše potrebe.

Neću se dugo zadržavati ovdje, dovoljno je opisano u dokumentaciju.

Vozač

Ime vozača. Naš je docker.

obloga

Linter je yamllint.

Korisne opcije u ovom dijelu konfiguracije su mogućnost specificiranja konfiguracijske datoteke za yamllint, prosljeđivanje varijabli okruženja ili onemogućavanje lintera:

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

platforme

Opisuje konfiguraciju instanci.
U slučaju dokera kao drajvera, molekul se ponavlja u ovom odeljku, a svaki element liste je dostupan u Dockerfile.j2 kao varijabla item.

U slučaju vozača koji zahtijeva create.yml и destroy.yml, odjeljak je dostupan u njima kao molecule_yml.platforms, a iteracije preko njega su već opisane u ovim datotekama.

Pošto Molecule omogućava kontrolu instanci ansible modulima, listu mogućih podešavanja takođe treba potražiti tamo. Za docker, na primjer, koristi se modul docker_container_module. Koji se moduli koriste u drugim drajverima možete pronaći u dokumentaciju.

Kao i primjeri korištenja raznih drajvera se mogu naći u testovima samog Molekula.

Zamijenite ovdje centos:7 na ubuntu.

dobavljač

"Dobavljač" - entitet koji upravlja instancama. U slučaju Molecule-a, ovo je ansible, podrška za druge nije planirana, tako da se ovaj dio može nazvati ansible proširenom konfiguracijom uz upozorenje.
Ovdje možete navesti dosta stvari, ja ću istaknuti glavne tačke, po mom mišljenju:

  • playbooks: možete odrediti koji bi se priručniki trebali koristiti u određenim fazama.

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

  • connection_options: opcije veze

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

  • opcije: Ansible opcije i varijable okruženja

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

scenario

Naziv i opis sekvenci skripta.
Možete promijeniti zadanu matricu akcija bilo koje komande dodavanjem ključa <command>_sequence i kao vrijednost za to definiranjem liste koraka koji su nam potrebni.
Recimo da želimo promijeniti redoslijed radnji prilikom pokretanja naredbe playbook run: molecule converge

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

provjeriti

Postavljanje okvira za testove i linter za njega. Zadani linter je testinfra и flake8. Moguće opcije su iste kao gore:

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

Vratimo se našoj ulozi. Uredimo fajl tasks/main.yml na ovu vrstu:

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

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

I dodajte testove 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")

Gotovo, ostaje samo da trčim (iz korijena uloge, da vas podsjetim):

> molecule test

Dugi auspuh ispod spojlera:

--> 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 jednostavna uloga je testirana bez problema.
Vrijedi zapamtiti da ako dođe do problema tokom rada molecule test, onda ako niste promijenili zadanu sekvencu, Molekul će izbrisati instancu.

Sljedeće naredbe su korisne za otklanjanje grešaka:

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

Postojeća uloga

Dodavanje nove skripte postojećoj ulozi je iz direktorija uloga sa sljedećim naredbama:

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

U slučaju da je ovo prvi scenario u ulozi, onda parametar -s može biti izostavljen jer će kreirati skriptu default.

zaključak

Kao što vidite, Molecule nije mnogo složen, a korištenjem vaših vlastitih predložaka, postavljanje nove skripte može se svesti na uređivanje varijabli u priručnicima za kreiranje i brisanje instance. Molekul se neprimetno integriše sa CI sistemima, što vam omogućava da povećate brzinu razvoja smanjenjem vremena za ručno testiranje knjiga.

Hvala vam na pažnji. Ako imate iskustva u testiranju ansible uloga, a nije vezano za Molecule, recite nam o tome u komentarima!

izvor: www.habr.com

Dodajte komentar