Instrucțiuni: cum să testați rolurile ansible și să aflați despre probleme înainte de producție

Bună ziua tuturor!

Lucrez ca inginer DevOps într-un serviciu de rezervări hoteliere. Ostrovok.ru. În acest articol vreau să vorbesc despre experiența noastră în testarea rolurilor Ansible.

La Ostrovok.ru, folosim ansible ca manager de configurare. Recent, am ajuns la necesitatea de a testa roluri, dar după cum s-a dovedit, nu există atât de multe instrumente pentru aceasta - cel mai popular, poate, este cadrul Molecule, așa că am decis să-l folosim. Dar s-a dovedit că documentația sa tace despre multe capcane. Nu am putut găsi un manual suficient de detaliat în limba rusă, așa că am decis să scriem acest articol.

Instrucțiuni: cum să testați rolurile ansible și să aflați despre probleme înainte de producție

Moleculă

Moleculă — un cadru pentru a ajuta la testarea rolurilor Ansible.

Descriere simplificată: molecula creează o instanță pe platforma pe care o specificați (cloud, mașină virtuală, container; pentru mai multe detalii, consultați secțiunea Şofer), rulează rolul tău pe acesta, apoi rulează teste și șterge instanța. În caz de eșec la unul dintre trepte, Molecula vă va informa despre aceasta.

Acum mai mult.

Un pic de teorie

Să luăm în considerare două entități cheie ale Moleculei: Scenariu și Driver.

Scenariu

Scriptul conține o descriere a ce, unde, cum și în ce secvență va fi realizat. Un rol poate avea mai multe scripturi și fiecare este un director de-a lungul căii <role>/molecule/<scenario>, care conțin descrieri ale acțiunilor necesare testului. Trebuie să existe un scenariu default, care va fi creat automat dacă inițializați rolul cu o Moleculă. Numele următoarelor scripturi depinde de dvs.

Secvența acțiunilor de testare dintr-un script este numită matrice, iar implicit este:

(Pașii marcați ?, sărit în mod implicit dacă nu este specificat de utilizator)

  • lint - rularea linters. În mod implicit sunt utilizate yamllint и flake8,
  • destroy - ștergerea instanțelor de la ultima lansare a Moleculei (dacă există),
  • dependency? — instalarea dependenței ansible a rolului testat;
  • syntax - verificarea sintaxei rolului folosind ansible-playbook --syntax-check,
  • create - crearea unei instanțe,
  • prepare? — pregătirea instanței; de exemplu, verificați/instalați python2
  • converge — lansarea playbook-ului testat,
  • idempotence — reluați manualul de joc pentru testul de idempotnță,
  • side_effect? - acțiuni care nu au legătură directă cu rolul, dar sunt necesare pentru teste,
  • verify - executarea de teste ale configurației rezultate folosind testinfra(Mod implicit) /goss/inspec,
  • cleanup? - (în versiuni noi) - aproximativ vorbind, „curățarea” infrastructurii externe afectate de Moleculă,
  • destroy — ștergerea unei instanțe.

Această secvență acoperă majoritatea cazurilor, dar poate fi modificată dacă este necesar.

Fiecare dintre pașii de mai sus poate fi rulat separat molecule <command>. Dar trebuie înțeles că pentru fiecare astfel de comandă cli poate exista propria sa secvență de acțiuni, pe care o puteți afla executând molecule matrix <command>. De exemplu, când rulați comanda converge (rularea playbook-ului testat) vor fi efectuate următoarele acțiuni:

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

Secvența acestor acțiuni poate fi editată. Dacă ceva din listă a fost deja finalizat, acesta va fi omis. Starea curentă, precum și configurația instanței, sunt stocate în directorul Molecule $TMPDIR/molecule/<role>/<scenario>.

Adăugați pași cu ? puteți descrie acțiunile dorite în formatul ansible-playbook și puteți crea numele fișierului conform pasului: prepare.yml/side_effect.yml. Așteptați-vă ca aceste fișiere Molecule să fie în folderul script.

Şofer

Un driver este o entitate în care sunt create instanțe pentru teste.
Lista driverelor standard pentru care Molecule are șabloane gata făcute este: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.

În cele mai multe cazuri, șabloanele sunt fișiere create.yml и destroy.yml în folderul script, care descriu crearea și respectiv ștergerea instanței.
Excepțiile sunt Docker și Vagrant, deoarece interacțiunile cu modulele lor pot avea loc fără fișierele de mai sus.

Merită evidențiat driverul delegat, deoarece dacă este utilizat în fișierele pentru crearea și ștergerea unei instanțe, este descrisă doar funcționarea cu configurarea instanțelor, restul ar trebui descris de inginer.

Driverul implicit este Docker.

Acum să trecem la exersare și să luăm în considerare alte funcții acolo.

Noțiuni de bază

Ca „bună lume”, vom testa un rol simplu de instalare nginx. Să alegem docker ca driver - cred că majoritatea dintre voi îl aveți instalat (și amintiți-vă că docker este driverul implicit).

Pregăti virtualenv și instalează-l în el molecule:

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

Următorul pas este inițializarea noului rol.
Inițializarea unui nou rol, precum și a unui nou script, se realizează folosind comanda 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

Sa dovedit un rol tipic ansible. În plus, toate interacțiunile cu moleculele CLI sunt realizate de la rădăcina rolului.

Să vedem ce este în directorul de roluri:

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

1 directory, 6 files

Să analizăm configurația molecule/default/molecule.yml (înlocuiește doar imaginea 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

dependenţă

Această secțiune descrie sursa dependențelor.

Opțiuni posibile: galaxie, se aplică, coajă.

Shell este doar un shell de comandă care este folosit în cazul în care galaxy și gilt nu vă acoperă nevoile.

Nu voi sta aici mult timp, este descris suficient în documentație.

şofer

Numele șoferului. Al nostru este docker.

scamă

Yamllint este folosit ca linter.

Opțiunile utile din această parte a configurației sunt capacitatea de a specifica un fișier de configurare pentru yamllint, de a transmite variabile de mediu sau de a dezactiva linter:

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

platforme

Descrie configurația instanțelor.
În cazul docker ca driver, Molecula iterează peste această secțiune, iar fiecare element al listei este disponibil în Dockerfile.j2 ca variabilă item.

În cazul unui şofer în care create.yml и destroy.yml, secțiunea este disponibilă în ele ca molecule_yml.platforms, iar iterațiile peste acesta sunt deja descrise în aceste fișiere.

Deoarece Molecule oferă gestionarea instanțelor modulelor Ansible, ar trebui să căutați acolo lista de setări posibile. Pentru Docker, de exemplu, se folosește modulul docker_container_module. Ce module sunt utilizate în alte drivere pot fi găsite în documentație.

De asemenea, puteți găsi exemple de utilizare a diferitelor drivere în testele Moleculei în sine.

Să înlocuim aici centos:7 pe ubuntu.

asigurator

„Furnizor” - o entitate care gestionează instanțe. În cazul Molecule, acesta este ansible, suportul pentru alții nu este planificat, așa că această secțiune poate fi numită configurație extinsă ansible cu o avertizare.
Aici puteți preciza o mulțime de lucruri, voi evidenția punctele principale, după părerea mea:

  • cărți de joacă: Puteți specifica ce cărți de joc ar trebui utilizate în anumite etape.

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

  • Opțiuni: Opțiuni Ansible și variabile de mediu

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

scenariu

Titlul și descrierea secvențelor de script.
Puteți modifica matricea de acțiuni implicită a oricărei comenzi adăugând cheia <command>_sequence și ca valoare pentru acesta, definirea listei de pași de care avem nevoie.
Să presupunem că vrem să schimbăm secvența de acțiuni atunci când rulăm comanda playbook run: molecule converge

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

verifica

Stabilirea unui cadru pentru teste și a unui linter pentru acesta. În mod implicit, linterul este utilizat testinfra и flake8. Opțiunile posibile sunt similare cu cele de mai sus:

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

Să revenim la rolul nostru. Să edităm fișierul tasks/main.yml la acest fel:

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

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

Și adăugați teste la 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")

Gata, rămâne doar să alerg (de la rădăcina rolului, permiteți-mi să vă reamintesc):

> molecule test

Evacuare lungă sub spoiler:

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

Rolul nostru simplu a fost testat fără probleme.
Merită să ne amintim că, dacă apar probleme în timpul funcționării molecule test, atunci dacă nu ați modificat secvența standard, Molecule va șterge instanța.

Următoarele comenzi sunt utile pentru depanare:

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

Rolul existent

Are loc adăugarea unui nou script la un rol existent din directorul de roluri cu următoarele comenzi:

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

Dacă acesta este primul script din rol, atunci parametrul -s poate fi omis deoarece va crea un script default.

Concluzie

După cum puteți vedea, Molecule nu este foarte complicat și, atunci când utilizați propriile șabloane, puteți reduce implementarea unui nou script la editarea variabilelor din manualele pentru crearea și ștergerea instanțelor. Molecula se integrează perfect cu sistemele CI, ceea ce vă permite să creșteți viteza de dezvoltare prin reducerea timpului pentru testarea manuală a manualelor.

Vă mulțumim pentru atenție. Dacă aveți experiență în testarea rolurilor ansible și nu are legătură cu Molecule, spuneți-ne despre asta în comentarii!

Sursa: www.habr.com

Adauga un comentariu