Istruzioni: come testare i ruoli ansible e scoprire i problemi prima della produzione

Ciao a tutti!

Lavoro come ingegnere DevOps per un servizio di prenotazione alberghiera. Ostrovok.ru. In questo articolo voglio parlare della nostra esperienza nel testare i ruoli ansible.

Su Ostrovok.ru utilizziamo ansible come gestore della configurazione. Di recente, siamo giunti alla necessità di testare i ruoli, ma come si è scoperto, non ci sono così tanti strumenti per questo: il più popolare, forse, è il framework Molecule, quindi abbiamo deciso di utilizzarlo. Ma si è scoperto che la sua documentazione tace su molte insidie. Non siamo riusciti a trovare un manuale sufficientemente dettagliato in russo, quindi abbiamo deciso di scrivere questo articolo.

Istruzioni: come testare i ruoli ansible e scoprire i problemi prima della produzione

molecola

Molecola - un framework per aiutare a testare i ruoli ansible.

Descrizione semplificata: La molecola crea un'istanza sulla piattaforma da te specificata (cloud, macchina virtuale, container; per maggiori dettagli consultare la sezione Guidatore), esegue il tuo ruolo su di esso, quindi esegue i test ed elimina l'istanza. Se si verifica un errore in uno dei passaggi, la Molecola ti avviserà.

Ora di più.

Un po 'di teoria

Consideriamo due entità chiave della Molecola: Scenario e Driver.

Scenario

Lo script contiene una descrizione di cosa, dove, come e in quale sequenza verrà eseguito. Un ruolo può avere diversi script e ciascuno è una directory lungo il percorso <role>/molecule/<scenario>, che contiene le descrizioni delle azioni richieste per il test. La sceneggiatura deve essere inclusa default, che verrà creato automaticamente se inizializzi il ruolo con una Molecola. I nomi dei seguenti script dipendono da te.

Viene chiamata la sequenza delle azioni di test in uno script matricee per impostazione predefinita è:

(Passi etichettati ?, vengono saltati per impostazione predefinita se non specificato dall'utente)

  • lint - esecuzione di linter. Per impostazione predefinita vengono utilizzati yamllint и flake8,
  • destroy - eliminazione delle istanze dall'ultimo avvio della Molecola (se presente),
  • dependency? — installazione della dipendenza ansible del ruolo testato,
  • syntax - verificare la sintassi del ruolo utilizzando ansible-playbook --syntax-check,
  • create - creazione di un'istanza,
  • prepare? — preparazione dell'istanza; ad esempio controlla/installa python2
  • converge — lancio del playbook in fase di test,
  • idempotence - riavviare il playbook per il test di idempotenza,
  • side_effect? — azioni non direttamente legate al ruolo, ma necessarie per i test,
  • verify - eseguire test della configurazione risultante utilizzando testinfra(predefinito) /goss/inspec,
  • cleanup? - (nelle nuove versioni) - grosso modo, "pulendo" l'infrastruttura esterna interessata dalla Molecola,
  • destroy — eliminazione di un'istanza.

Questa sequenza copre la maggior parte dei casi, ma può essere modificata se necessario.

Ciascuno dei passaggi precedenti può essere eseguito separatamente utilizzando molecule <command>. Ma dovrebbe essere chiaro che per ciascuno di questi comandi cli può esserci una propria sequenza di azioni, che puoi scoprire eseguendo molecule matrix <command>. Ad esempio, quando si esegue il comando converge (eseguendo il playbook in prova), verranno eseguite le seguenti azioni:

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

La sequenza di queste azioni può essere modificata. Se qualcosa dall'elenco è già stato fatto, verrà saltato. Lo stato corrente, così come la configurazione delle istanze, la Molecola memorizza nella directory $TMPDIR/molecule/<role>/<scenario>.

Aggiungi passaggi con ? puoi descrivere le azioni desiderate nel formato ansible-playbook e assegnare il nome al file in base al passaggio: prepare.yml/side_effect.yml. Aspettatevi questi file. La molecola sarà nella cartella dello script.

Guidatore

Un driver è un'entità in cui vengono create le istanze di test.
L'elenco dei driver standard per i quali Molecule dispone di modelli pronti è il seguente: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegate.

Nella maggior parte dei casi, i modelli sono file create.yml и destroy.yml nella cartella degli script che descrivono rispettivamente la creazione e l'eliminazione di un'istanza.
Le eccezioni sono Docker e Vagrant, poiché le interazioni con i loro moduli possono avvenire senza i file sopra menzionati.

Vale la pena evidenziare il driver delegato, poiché se viene utilizzato nei file per creare ed eliminare un'istanza, viene descritto solo il funzionamento con la configurazione delle istanze, il resto dovrebbe essere descritto dall'ingegnere.

Il driver predefinito è Docker.

Passiamo ora alla pratica e consideriamo ulteriori funzionalità lì.

Guida introduttiva

Come "ciao mondo", testiamo un semplice ruolo di installazione di nginx. Scegliamo docker come driver: penso che molti di voi lo abbiano installato (e ricordate che docker è il driver predefinito).

Preparare virtualenv e installarlo al suo interno molecule:

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

Il passaggio successivo consiste nell'inizializzare il nuovo ruolo.
L'inizializzazione di un nuovo ruolo, nonché di un nuovo script, viene eseguita utilizzando il comando 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

Si è rivelato un tipico ruolo ansible. Inoltre, tutte le interazioni con CLI Molecules vengono effettuate dalla radice del ruolo.

Vediamo cosa c'è nella directory dei ruoli:

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

1 directory, 6 files

Analizziamo la configurazione molecule/default/molecule.yml (sostituisci solo l'immagine della finestra mobile):

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

dipendenza

Questa sezione descrive l'origine delle dipendenze.

Opzioni possibili: galassia, dorato, conchiglia.

Shell è solo una shell di comando che viene utilizzata nel caso in cui Galaxy e Gilt non coprano le tue esigenze.

Non resterò qui a lungo, è sufficientemente descritto documentazione.

autista

Il nome dell'autista. Il nostro è docker.

garza

La linter è yamllint.

Opzioni utili in questa parte della configurazione sono la possibilità di specificare un file di configurazione per yamllint, inoltrare variabili di ambiente o disabilitare il linter:

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

piattaforme

Descrive la configurazione delle istanze.
Nel caso della finestra mobile come driver, la Molecola viene ripetuta su questa sezione e ogni elemento dell'elenco è disponibile in Dockerfile.j2 come variabile item.

Nel caso di un conducente che lo richiede create.yml и destroy.yml, la sezione è disponibile in essi come molecule_yml.platformse le iterazioni su di esso sono già descritte in questi file.

Poiché la Molecola fornisce il controllo delle istanze ai moduli ansible, è necessario cercare anche l'elenco delle possibili impostazioni lì. Per la finestra mobile, ad esempio, viene utilizzato il modulo docker_container_module. Quali moduli vengono utilizzati in altri driver possono essere trovati in documentazione.

Si possono trovare anche esempi di utilizzo di vari driver nei test della Molecola stessa.

Sostituisci qui cento:7 su ubuntu.

provveditore

“Provider” è l’entità che gestisce le istanze. Nel caso di Molecule, questo è ansible, il supporto per altri non è pianificato, quindi questa sezione può essere chiamata configurazione estesa ansible con un avvertimento.
Qui puoi specificare molte cose, io evidenzierò i punti principali, secondo me:

  • playbook: puoi specificare quali playbook devono essere utilizzati in determinate fasi.

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

  • Opzioni: Opzioni Ansible e variabili di ambiente

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

scenario

Nome e descrizione delle sequenze di script.
È possibile modificare la matrice di azioni predefinita di qualsiasi comando aggiungendo la chiave <command>_sequence e come valore per esso definendo l'elenco dei passaggi di cui abbiamo bisogno.
Supponiamo di voler modificare la sequenza di azioni durante l'esecuzione del comando playbook run: molecule converge

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

verificatore

Impostazione di un framework per i test e di un linter per esso. Per impostazione predefinita, viene utilizzato il linter testinfra и flake8. Le opzioni possibili sono simili alle precedenti:

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

Torniamo al nostro ruolo. Modifichiamo il file tasks/main.yml a questo modulo:

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

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

E aggiungi test a 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")

Fatto, non resta che eseguire (dalla radice del ruolo, lasciatemelo ricordare):

> molecule test

Scarico lungo sotto 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

Il nostro semplice ruolo è stato testato senza problemi.
Vale la pena ricordarlo se ci sono problemi durante il lavoro molecule test, quindi se non hai modificato la sequenza predefinita, la Molecola eliminerà l'istanza.

I seguenti comandi sono utili per il debug:

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

Ruolo esistente

Aggiungere un nuovo script a un ruolo esistente lo è dalla directory dei ruoli con i seguenti comandi:

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

Nel caso in cui questo sia il primo scenario nel ruolo, allora il parametro -s può essere omesso poiché verrà creato uno script default.

conclusione

Come puoi vedere, la molecola non è molto complessa e, utilizzando i tuoi modelli, la distribuzione di un nuovo script può essere ridotta alla modifica di variabili nei playbook di creazione ed eliminazione dell'istanza. La molecola si integra perfettamente con i sistemi CI, consentendo di aumentare la velocità di sviluppo riducendo i tempi per i test manuali dei playbook.

Grazie per l'attenzione. Se hai esperienza nel testare ruoli ansible e non è correlata alla Molecola, raccontacelo nei commenti!

Fonte: habr.com

Aggiungi un commento