Instruccions: com provar rols ansibles i esbrinar els problemes abans de la producció

Hola a tots!

Treballo com a enginyer de DevOps en un servei de reserves d'hotels. Ostrovok.ru. En aquest article, vull parlar de la nostra experiència en provar rols ansibles.

A Ostrovok.ru, utilitzem ansible com a gestor de configuració. Recentment, hem arribat a la necessitat de provar els rols, però, com va resultar, no hi ha tantes eines per a això; la més popular, potser, és el marc de Molecule, així que vam decidir utilitzar-lo. Però va resultar que la seva documentació calla sobre moltes trampes. No vam trobar un manual prou detallat en rus, així que vam decidir escriure aquest article.

Instruccions: com provar rols ansibles i esbrinar els problemes abans de la producció

Molècula

Molècula - un marc per ajudar a provar rols ansibles.

Descripció simplificada: la molècula crea una instància a la plataforma que especifiqueu (núvol, màquina virtual, contenidor; per a més detalls, consulteu la secció Conductor), executa el vostre rol, després executa proves i elimina la instància. En cas de fallada en algun dels passos, la Molècula t'informarà.

Ara més.

Una mica de teoria

Considereu dues entitats clau de la molècula: l'escenari i el conductor.

Escenari

El guió conté una descripció de què, on, com i en quina seqüència es realitzarà. Un rol pot tenir diversos scripts, i cadascun és un directori al llarg del camí <role>/molecule/<scenario>, que conté descripcions de les accions necessàries per a la prova. S'ha d'incloure el guió default, que es crearà automàticament si inicialitzeu el rol amb una Molècula. Els noms dels scripts següents depenen de vostè.

S'anomena la seqüència d'accions de prova en un script matriu, i per defecte és:

(Pasos etiquetats ?, saltat per defecte si no l'especifica l'usuari)

  • lint - corrent linters. Per defecte s'utilitzen yamllint и flake8,
  • destroy - suprimir instàncies de l'últim llançament de la molècula (si n'hi ha),
  • dependency? — instal·lació de la dependència ansible del rol provat,
  • syntax - Comprovació de la sintaxi del rol utilitzant ansible-playbook --syntax-check,
  • create - Creació d'una instància,
  • prepare? — Preparació de la instància; per exemple, comproveu/instal·leu python2
  • converge — llançament del llibre de jocs que s'està provant,
  • idempotence - reiniciar el llibre de jugades per a la prova d'idempotència,
  • side_effect? - accions no directament relacionades amb el rol, però necessàries per a les proves,
  • verify - executar proves de la configuració resultant utilitzant testinfra(per defecte) /goss/inspec,
  • cleanup? - (en noves versions) - a grans trets, "netejar" la infraestructura externa afectada per la molècula,
  • destroy - Eliminació d'una instància.

Aquesta seqüència cobreix la majoria dels casos, però es pot canviar si cal.

Cadascun dels passos anteriors es pot executar per separat molecule <command>. Però s'ha d'entendre que per a cada comandament cli hi pot haver la seva pròpia seqüència d'accions, que podeu esbrinar executant molecule matrix <command>. Per exemple, quan s'executa l'ordre converge (executant el llibre de jugades a prova), es realitzaran les accions següents:

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

La seqüència d'aquestes accions es pot editar. Si alguna cosa de la llista ja està feta, s'ometrà. L'estat actual, així com la configuració de les instàncies, la Molècula emmagatzema al directori $TMPDIR/molecule/<role>/<scenario>.

Afegeix passos amb ? podeu descriure les accions desitjades en el format ansible-playbook i fer el nom del fitxer segons el pas: prepare.yml/side_effect.yml. Espereu aquests fitxers La molècula estarà a la carpeta de l'script.

Conductor

Un controlador és una entitat on es creen instàncies de prova.
La llista de controladors estàndard per als quals Molecule té plantilles preparades és la següent: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.

En la majoria dels casos, les plantilles són fitxers create.yml и destroy.yml a la carpeta d'scripts que descriuen la creació i la supressió d'una instància, respectivament.
Les excepcions són Docker i Vagrant, ja que les interaccions amb els seus mòduls es poden produir sense els fitxers esmentats anteriorment.

Val la pena destacar el controlador Delegat, ja que si s'utilitza en els fitxers per crear i esborrar una instància, només es descriu el treball amb la configuració d'instàncies, la resta ha de ser descrita per l'enginyer.

El controlador predeterminat és Docker.

Ara passem a la pràctica i considerem més funcions allà.

primers passos

Com a "hola món", provem un rol d'instal·lació nginx senzill. Escollirem docker com a controlador; crec que la majoria de vosaltres el teniu instal·lat (i recordeu que docker és el controlador predeterminat).

Prepareu-vos virtualenv i instal·lar-hi molecule:

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

El següent pas és inicialitzar el nou rol.
La inicialització d'un nou rol, així com d'un nou script, es realitza mitjançant l'ordre 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

Va resultar un paper ansible típic. A més, totes les interaccions amb les molècules CLI es fan des de l'arrel del paper.

Vegem què hi ha al directori de rols:

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

1 directory, 6 files

Analitzem la configuració molecule/default/molecule.yml (substitueix només la imatge de 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

dependència

Aquesta secció descriu l'origen de les dependències.

Opcions possibles: galàxia, Giltar, closca.

Shell és només un intèrpret d'ordres que s'utilitza en cas que la galàxia i el daurat no cobreixin les vostres necessitats.

No em quedaré aquí durant molt de temps, ja n'hi ha prou descrit documentació.

conductor

El nom del conductor. El nostre és docker.

pelussa

El linter és yamllint.

Les opcions útils d'aquesta part de la configuració són la possibilitat d'especificar un fitxer de configuració per a yamllint, reenviar variables d'entorn o desactivar el linter:

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

plataformes

Descriu la configuració de les instàncies.
En el cas de docker com a controlador, la molècula s'itera sobre aquesta secció i cada element de la llista està disponible a Dockerfile.j2 com a variable item.

En el cas d'un conductor que ho requereixi create.yml и destroy.yml, la secció està disponible en ells com a molecule_yml.platforms, i les iteracions sobre ell ja es descriuen en aquests fitxers.

Atès que la molècula proporciona control de les instàncies als mòduls ansibles, també s'hauria de cercar allà la llista de configuracions possibles. Per a docker, per exemple, s'utilitza el mòdul docker_container_module. A quins mòduls s'utilitzen en altres controladors es poden trobar documentació.

Així com exemples de l'ús de diversos controladors es poden trobar en les proves de la pròpia molècula.

Substitueix aquí centos:7 en ubuntu.

subministrador

"Proveïdor": una entitat que gestiona les instàncies. En el cas de Molecule, això és ansible, el suport per a altres no està previst, de manera que aquesta secció es pot anomenar configuració ampliada ansible amb una advertència.
Aquí podeu especificar moltes coses, en destacaré els punts principals, al meu entendre:

  • llibres de jugades: podeu especificar quins llibres de jugades s'han d'utilitzar en determinades etapes.

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

  • Opcions: Opcions d'Ansible i variables d'entorn

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

guió

Nom i descripció de les seqüències de guió.
Podeu canviar la matriu d'accions per defecte de qualsevol ordre afegint la clau <command>_sequence i com a valor per a això definint la llista de passos que necessitem.
Suposem que volem canviar la seqüència d'accions quan executem l'ordre d'execució del llibre de jocs: molecule converge

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

verificador

Configuració d'un marc per a les proves i un linter. El linter predeterminat és testinfra и flake8. Les opcions possibles són les mateixes que les anteriors:

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

Tornem al nostre paper. Editem el fitxer tasks/main.yml a aquest tipus:

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

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

I afegir proves 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")

Fet, només queda córrer (des de l'arrel del paper, permeteu-me que us recordi):

> molecule test

Escapament llarg sota l'aleró:

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

El nostre senzill paper es va provar sense problemes.
Val la pena recordar que si hi ha problemes durant el treball molecule test, aleshores, si no heu canviat la seqüència per defecte, la molècula suprimirà la instància.

Les ordres següents són útils per a la depuració:

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

Rol existent

Afegir un script nou a un rol existent és del directori de rols amb les ordres següents:

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

En cas que aquest sigui el primer escenari del rol, llavors el paràmetre -s es pot ometre, ja que crearà un script default.

Conclusió

Com podeu veure, la Molècula no és gaire complexa i, utilitzant les vostres pròpies plantilles, el desplegament d'un nou script es pot reduir a l'edició de variables en els llibres de jugades de creació i supressió d'instàncies. La molècula s'integra perfectament amb els sistemes CI, la qual cosa us permet augmentar la velocitat de desenvolupament reduint el temps per a les proves manuals dels llibres de joc.

Gràcies per la vostra atenció. Si tens experiència en provar rols ansibles i no està relacionat amb la molècula, explica'ns-ho als comentaris!

Font: www.habr.com

Afegeix comentari