Instruktioner: hur man testar eventuella roller och tar reda på problem innan produktion

Hej alla!

Jag arbetar som DevOps-ingenjör i en hotellbokningstjänst. Ostrovok.ru. I den här artikeln vill jag prata om vår erfarenhet av att testa möjliga roller.

På Ostrovok.ru använder vi ansible som konfigurationshanterare. Nyligen kom vi till behovet av att testa roller, men som det visade sig finns det inte så många verktyg för detta - det mest populära är kanske Molecule-ramverket, så vi bestämde oss för att använda det. Men det visade sig att hans dokumentation är tyst om många fallgropar. Vi kunde inte hitta en tillräckligt detaljerad manual på ryska, så vi bestämde oss för att skriva den här artikeln.

Instruktioner: hur man testar eventuella roller och tar reda på problem innan produktion

molekyl

Molekyl - ett ramverk för att testa möjliga roller.

Förenklad beskrivning: Molekylen skapar en instans på plattformen du anger (moln, virtuell maskin, behållare; för mer information, se avsnittet Chaufför), kör din roll på den, kör sedan tester och tar bort instansen. I händelse av fel på ett av stegen kommer molekylen att informera dig om det.

Nu mer.

Lite teori

Tänk på två nyckelenheter i molekylen: scenario och drivrutin.

Scenario

Manuset innehåller en beskrivning av vad, var, hur och i vilken sekvens som kommer att utföras. En roll kan ha flera skript, och var och en är en katalog längs vägen <role>/molecule/<scenario>, som innehåller beskrivningar av de åtgärder som krävs för testet. Manus måste finnas med default, som skapas automatiskt om du initierar rollen med en molekyl. Namnen på följande skript är upp till dig.

Sekvensen av teståtgärder i ett skript kallas matris, och som standard är det:

(Steg märkta ?, hoppade över som standard om det inte anges av användaren)

  • lint - löpande linters. Som standard används yamllint и flake8,
  • destroy - ta bort instanser från den senaste lanseringen av molekylen (om någon),
  • dependency? — installation av det möjliga beroendet för den testade rollen,
  • syntax - kontrollera syntaxen för rollen med hjälp av ansible-playbook --syntax-check,
  • create - skapa en instans,
  • prepare? — Förberedelse av instansen. t.ex. kontrollera/installera python2
  • converge — lansering av lekboken som testas,
  • idempotence - starta om spelboken för idempotenstestet,
  • side_effect? - åtgärder som inte är direkt relaterade till rollen, men nödvändiga för tester,
  • verify - köra tester av den resulterande konfigurationen med hjälp av testinfra(standard) /goss/inspec,
  • cleanup? - (i nya versioner) - grovt sett "städa" den externa infrastrukturen som påverkas av molekylen,
  • destroy - Ta bort en instans.

Denna sekvens täcker de flesta fall, men kan ändras vid behov.

Vart och ett av stegen ovan kan köras separat med molecule <command>. Men det bör förstås att för varje sådant cli-kommando kan det finnas en egen sekvens av åtgärder, som du kan ta reda på genom att köra molecule matrix <command>. Till exempel när du kör kommandot converge (kör spelboken som testas), kommer följande åtgärder att utföras:

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

Sekvensen av dessa åtgärder kan redigeras. Om något från listan redan är gjort kommer det att hoppas över. Det aktuella tillståndet, såväl som konfigurationen av instanserna, lagrar Molecule i katalogen $TMPDIR/molecule/<role>/<scenario>.

Lägg till steg med ? du kan beskriva de önskade åtgärderna i ansible-playbook-formatet och skapa filnamnet enligt steget: prepare.yml/side_effect.yml. Förvänta dig dessa filer Molekylen kommer att finnas i skriptmappen.

Chaufför

En drivrutin är en enhet där instanser för tester skapas.
Listan över standarddrivrutiner för vilka Molecule har mallar redo är följande: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegerad.

I de flesta fall är mallar filer create.yml и destroy.yml i skriptmappen som beskriver skapandet och raderingen av en instans, respektive.
Undantagen är Docker och Vagrant, eftersom interaktioner med deras moduler kan ske utan de tidigare nämnda filerna.

Det är värt att markera den delegerade drivrutinen, eftersom om den används i filerna för att skapa och ta bort en instans, beskrivs bara arbetet med konfigurationen av instanser, resten ska beskrivas av ingenjören.

Standarddrivrutinen är Docker.

Låt oss nu gå vidare till övningen och överväga ytterligare funktioner där.

Komma igång

Som en "hej värld", låt oss testa en enkel nginx-installationsroll. Låt oss välja docker som drivrutin - jag tror att de flesta av er har det installerat (och kom ihåg att docker är standarddrivrutinen).

Låt oss förbereda oss virtualenv och installera i den molecule:

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

Nästa steg är att initiera den nya rollen.
Initiering av en ny roll, såväl som ett nytt skript, utförs med kommandot 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 blev en typisk ansible-roll. Vidare görs all interaktion med CLI-molekyler från roten av rollen.

Låt oss se vad som finns i rollkatalogen:

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

1 directory, 6 files

Låt oss analysera konfigurationen molecule/default/molecule.yml (ersätt endast docker-bild):

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

beroende

Det här avsnittet beskriver källan till beroenden.

Möjliga alternativ: galax, applicerar, skal.

Shell är bara ett kommandoskal som används i fall galaxy och gilt inte täcker dina behov.

Jag kommer inte att stanna här länge, det är nog beskrivet i dokumentation.

chaufför

Namnet på föraren. Vår är hamnarbetare.

ludd

Lintern är yamllint.

Användbara alternativ i den här delen av konfigurationen är möjligheten att ange en konfigurationsfil för yamllint, vidarebefordra miljövariabler eller inaktivera linter:

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

plattformar

Beskriver konfigurationen av instanserna.
När det gäller hamnarbetare som förare, itereras molekylen över detta avsnitt, och varje element i listan är tillgängligt i Dockerfile.j2 som en variabel item.

I fallet med en förare som kräver create.yml и destroy.yml, avsnittet finns i dem som molecule_yml.platforms, och iterationer över det beskrivs redan i dessa filer.

Eftersom Molecule ger kontroll av instanser till eventuella moduler, bör listan över möjliga inställningar också letas efter där. För docker, till exempel, används modulen docker_container_module. Vilka moduler som används i andra drivrutiner finns i dokumentation.

Samt exempel på användningen av olika drivrutiner kan hittas i testerna av själva molekylen.

Byt ut här centos:7ubuntu.

proviantör

"Leverantör" - en enhet som hanterar instanser. När det gäller Molecule är detta möjligt, stöd för andra är inte planerat, så det här avsnittet kan kallas ansible utökad konfiguration med en varning.
Här kan du specificera många saker, jag kommer att belysa huvudpunkterna, enligt min åsikt:

  • spelböcker: du kan ange vilka spelböcker som ska användas i vissa skeden.

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

  • alternativ: Möjliga alternativ och miljövariabler

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

scenario

Namn och beskrivning av manussekvenser.
Du kan ändra standardåtgärdsmatrisen för alla kommandon genom att lägga till nyckeln <command>_sequence och som ett värde för det genom att definiera listan med steg vi behöver.
Låt oss säga att vi vill ändra sekvensen av åtgärder när du kör kommandot playbook run: molecule converge

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

kontrollör

Att sätta upp ett ramverk för tester och en linter till det. Standard linter är testinfra и flake8. De möjliga alternativen är desamma som ovan:

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

Låt oss återgå till vår roll. Låt oss redigera filen tasks/main.yml till denna typ:

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

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

Och lägg till 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")

Klart, det återstår bara att köra (från roten av rollen, låt mig påminna dig):

> molecule test

Långt avgassystem under spoilern:

--> 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 enkla roll testades utan problem.
Det är värt att komma ihåg att om det finns problem under arbetet molecule test, sedan om du inte ändrade standardsekvensen, kommer Molecule att ta bort instansen.

Följande kommandon är användbara för felsökning:

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

Befintlig roll

Att lägga till ett nytt skript till en befintlig roll är från rollkatalogen med följande kommandon:

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

Om detta är det första scenariot i rollen, då parametern -s kan utelämnas eftersom det skapar ett skript default.

Slutsats

Som du kan se är molekylen inte särskilt komplicerad, och genom att använda dina egna mallar kan implementering av ett nytt skript reduceras till att redigera variabler i spelböckerna för att skapa och ta bort instanser. Molekylen integreras sömlöst med CI-system, vilket gör att du kan öka utvecklingshastigheten genom att minska tiden för manuell testning av playbooks.

Tack för din uppmärksamhet. Om du har erfarenhet av att testa möjliga roller, och det inte är relaterat till molekylen, berätta om det i kommentarerna!

Källa: will.com

Lägg en kommentar