Hej alla!
Jag arbetar som DevOps-ingenjör i en hotellbokningstjänst. . 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.

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 ), 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ändsyamllintи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 avansible-playbook --syntax-check,create- skapa en instans,prepare? — Förberedelse av instansen. t.ex. kontrollera/installera python2converge— 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 avtestinfra(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 fileDet 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 filesLå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: flake8beroende
Det här avsnittet beskriver källan till beroenden.
Möjliga alternativ: , , 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 .
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: Falseplattformar
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 . Vilka moduler som används i andra drivrutiner finns i .
Samt exempel på användningen av olika drivrutiner kan hittas .
Byt ut här centos:7 på ubuntu.
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- config_options:
provisioner:
name: ansible
config_options:
defaults:
fact_caching: jsonfile
ssh_connection:
scp_if_ssh: True- anslutningsalternativ: alternativ
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: BARscenario
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
- convergekontrollö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: barLå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 testLå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
