Instruksjoner: hvordan teste mulige roller og finne ut om problemer før produksjon

Hei!

Jeg jobber som DevOps-ingeniør i en hotellbestillingstjeneste. Ostrovok.ru. I denne artikkelen vil jeg snakke om vår erfaring med å teste mulige roller.

Hos Ostrovok.ru bruker vi ansible som en konfigurasjonsbehandling. Nylig kom vi til behovet for å teste roller, men det viste seg at det ikke er mange verktøy for dette - det mest populære er kanskje Molecule-rammeverket, så vi bestemte oss for å bruke det. Men det viste seg at dokumentasjonen hans er taus om mange fallgruver. Vi kunne ikke finne en tilstrekkelig detaljert guide på russisk, så vi bestemte oss for å skrive denne artikkelen.

Instruksjoner: hvordan teste mulige roller og finne ut om problemer før produksjon

Molecule

Molekyl - et rammeverk for å teste mulige roller.

Forenklet beskrivelse: Molekylet oppretter en forekomst på plattformen du spesifiserer (sky, virtuell maskin, container; for flere detaljer, se avsnittet Driver), kjører rollen din på den, kjører deretter tester og sletter forekomsten. I tilfelle feil på ett av trinnene, vil molekylet informere deg om det.

Nå mer.

Litt teori

Vurder to nøkkelenheter i molekylet: Scenario og Driver.

Scenario

Manuset inneholder en beskrivelse av hva, hvor, hvordan og i hvilken rekkefølge som skal utføres. En rolle kan ha flere skript, og hver er en katalog langs banen <role>/molecule/<scenario>, som inneholder beskrivelser av handlingene som kreves for testen. Manus må inkluderes default, som opprettes automatisk hvis du initialiserer rollen ved hjelp av Molecule. Navnene på følgende skript er opp til deg.

Sekvensen av testhandlinger i et skript kalles matrise, og som standard er det:

(Trinn merket ?, hoppet over som standard hvis ikke spesifisert av brukeren)

  • lint - løpende linters. Som standard brukes yamllint и flake8,
  • destroy - slette forekomster fra den siste lanseringen av molekylet (hvis noen),
  • dependency? – installere den mulige avhengigheten til den testede rollen,
  • syntax - sjekke syntaksen til rollen ved å bruke ansible-playbook --syntax-check,
  • create - opprette en instans,
  • prepare? — forberedelse av instansen; for eksempel sjekk/installer python2
  • converge – lansering av lekeboken som testes,
  • idempotence - starte playbook på nytt for idempotenstesten,
  • side_effect? - handlinger som ikke er direkte relatert til rollen, men nødvendige for tester,
  • verify - kjører tester av den resulterende konfigurasjonen ved hjelp av testinfra(standard) /goss/inspec,
  • cleanup? - (i nye versjoner) - grovt sett "rense" den eksterne infrastrukturen påvirket av molekylet,
  • destroy - Sletter en forekomst.

Denne sekvensen dekker de fleste tilfeller, men kan endres om nødvendig.

Hvert av trinnene ovenfor kan kjøres separat med molecule <command>. Men du bør forstå at for hver slik cli-kommando kan det være sin egen sekvens av handlinger, som du kan finne ut ved å kjøre molecule matrix <command>. For eksempel når du kjører kommandoen converge (kjører den testede spilleboken) vil følgende handlinger bli utført:

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

Rekkefølgen av disse handlingene kan redigeres. Hvis noe fra listen allerede er gjort, vil det bli hoppet over. Den nåværende tilstanden, samt konfigurasjonen av forekomstene, lagrer Molecule i katalogen $TMPDIR/molecule/<role>/<scenario>.

Legg til trinn med ? Du kan beskrive de ønskede handlingene i Ansible playbook-formatet, og lage filnavnet i henhold til trinnet: prepare.yml/side_effect.yml. Forvent disse filene Molekylet vil være i skriptmappen.

Driver

En driver er en enhet der testforekomster opprettes.
Listen over standarddrivere som Molecule har ferdige maler for er: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.

I de fleste tilfeller er maler filer create.yml и destroy.yml i skriptmappen, som beskriver henholdsvis opprettelse og sletting av forekomsten.
Unntakene er Docker og Vagrant, da interaksjoner med modulene deres kan skje uten de nevnte filene.

Det er verdt å fremheve den delegerte driveren, siden hvis den brukes, er bare arbeidet med forekomstkonfigurasjonen beskrevet i filene for opprettelse og sletting av forekomster; resten bør beskrives av ingeniøren.

Standarddriveren er Docker.

La oss nå gå videre til praksis og vurdere ytterligere funksjoner der.

Komme i gang

Som en "hei verden", la oss teste en enkel nginx-installasjonsrolle. La oss velge docker som driver - jeg tror de fleste av dere har den installert (og husk at docker er standarddriveren).

Forbered virtualenv og installere i den molecule:

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

Det neste trinnet er å initialisere den nye rollen.
Initialisering av en ny rolle, samt et nytt skript, utføres ved hjelp av kommandoen 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

Det ble en typisk ansible-rolle. Videre er all interaksjon med CLI-molekyler laget fra roten til rollen.

La oss se hva som er i rollekatalogen:

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

1 directory, 6 files

La oss analysere konfigurasjonen molecule/default/molecule.yml (erstatt kun docker-bilde):

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

avhengighet

Denne delen beskriver kilden til avhengigheter.

Mulige alternativer: galakse, gjelder, skall.

Shell er bare et kommandoskall som brukes i tilfelle galaxy og gilt ikke dekker dine behov.

Jeg blir ikke her lenge, det er tilstrekkelig beskrevet i dokumentasjon.

sjåfør

Førernavn. For oss er dette docker.

lo

Linteren er yamllint.

Nyttige alternativer i denne delen av konfigurasjonen er muligheten til å spesifisere en konfigurasjonsfil for yamllint, videresende miljøvariabler eller deaktivere linter:

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

plattformer

Beskriver konfigurasjonen av forekomstene.
Når det gjelder docker som sjåfør, itereres molekylet over denne delen, og hvert element i listen er tilgjengelig i Dockerfile.j2 som en variabel item.

I tilfelle av en sjåfør som krever create.yml и destroy.yml, er delen tilgjengelig i dem som molecule_yml.platforms, og iterasjoner over det er allerede beskrevet i disse filene.

Siden Molecule gir kontroll over forekomster til mulige moduler, bør listen over mulige innstillinger også ses etter der. For docker, for eksempel, brukes modulen docker_container_module. Hvilke moduler som brukes i andre drivere finner du i dokumentasjon.

I tillegg til eksempler på bruk av ulike drivere finnes i tester av selve molekylet.

La oss bytte ut her centos: 7ubuntu.

provianter

"Leverandør" - en enhet som administrerer forekomster. Når det gjelder Molecule, er dette mulig, støtte for andre er ikke planlagt, så denne delen kan med forbehold kalles en utvidet mulig konfigurasjon.
Her kan du spesifisere mange ting, jeg vil fremheve hovedpunktene, etter min mening:

  • lekebøker: du kan spesifisere hvilke lekebøker som skal brukes på bestemte stadier.

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

  • alternativer: Ansible alternativer og miljøvariabler

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

scenario

Navn og beskrivelse av manussekvenser.
Du kan endre standard handlingsmatrise for enhver kommando ved å legge til nøkkelen <command>_sequence og som en verdi for det ved å definere listen over trinn vi trenger.
La oss si at vi ønsker å endre rekkefølgen av handlinger når du kjører playbook run-kommandoen: molecule converge

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

verifier

Sette opp et rammeverk for tester og en linter til det. Standard linter er testinfra и flake8. De mulige alternativene er de samme som ovenfor:

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

La oss gå tilbake til vår rolle. La oss redigere filen tasks/main.yml til denne typen:

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

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

Og legg til tester 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")

Ferdig, det gjenstår bare å kjøre (fra roten av rollen, la meg minne deg på):

> molecule test

Lang eksos under spoileren:

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

Vår enkle rolle ble testet uten problemer.
Det er verdt å huske at hvis det er problemer under arbeidet molecule test, så hvis du ikke endret standardsekvensen, vil Molecule slette forekomsten.

Følgende kommandoer er nyttige for feilsøking:

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

Eksisterende rolle

Det skjer å legge til et nytt skript til en eksisterende rolle fra rollekatalogen med følgende kommandoer:

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

I tilfelle dette er det første scenariet i rollen, så parameteren -s kan utelates da det vil lage et skript default.

Konklusjon

Som du kan se, er ikke molekylet veldig komplekst, og ved å bruke dine egne maler, kan distribusjon av et nytt skript reduseres til å redigere variabler i instansopprettings- og slettingsspillebøkene. Molekylet integreres sømløst med CI-systemer, som lar deg øke hastigheten på utviklingen ved å redusere tiden for manuell testing av playbooks.

Takk for din oppmerksomhet. Hvis du har erfaring med å teste mulige roller, og det ikke er relatert til Molecule, fortell oss om det i kommentarfeltet!

Kilde: www.habr.com

Legg til en kommentar