Instruktion: hur man testar ansible-roller och upptäcker problem före produktion

Hej alla!

Jag arbetar som DevOps-ingenjör på en hotellbokningstjänst Ostrovok.ru. I den här artikeln vill jag berätta om vår erfarenhet av att testa ansible-roller.

På Ostrovok.ru använder vi ansible som konfigurationshanterare. Nyligen kom vi till behovet av att testa roller, men det finns inte så många verktyg för detta — det mest populära är nog ramverket Molecule, så vi bestämde oss för att använda det. Men det visade sig att dess dokumentation utelämnar många fallgropar. Vi kunde inte hitta en tillräckligt detaljerad handledning på ryska, så vi bestämde oss för att skriva denna artikel.

Instruktion: hur man testar ansible-roller och upptäcker problem före produktion

Molecule

Molekyl — ett ramverk för att hjälpa till med testning av ansible-roller.

En förenklad beskrivning: Molekyl skapar en instans på den plattform du anger (moln, virtuell maskin, container; se mer i avsnittet Driver), kör din roll på den, kör sedan tester och tar bort instansen. Om ett av stegen misslyckas, kommer Molekyl att informera dig om detta.

Nu mer detaljerat.

Lite teori

Låt oss titta på två nyckelkomponenter i Molekyl: Scenario och Driver.

Scenario

Ett scenario innehåller en beskrivning av vad, var, hur och i vilken ordning som ska utföras. En roll kan ha flera scenarier, och varje scenario är en katalog vid sökvägen /molecule/, som innehåller beskrivningar av de nödvändiga åtgärderna för testet. Ett scenario måste alltid finnas, vilket automatiskt skapas om du initierar rollen med hjälp av Molekyl. Namnen på följande scenarier väljs efter din egen vilja. defaultSekvensen av åtgärder som testas i scenariot kallas

matrix , och som standard ser den ut så här:(Steg som är markerade

, hoppas som standard över om de inte beskrivs av användaren) ?lint

  • — körning av linters. Som standard används yamllint flake8 och destroy,
  • — borttagning av instanser från föregående körning av Molekyl (om några finns kvar), dependency
  • syntax? — установка ansible-зависимости тестируемой роли,
  • — kontroll av rollens syntaks med hjälp av ansible-playbook --syntax-check create,
  • — skapande av instans, prepare
  • converge? — подготовка инстанса; например, проверка / установка python2
  • — körning av den testade playbooken, idempotence
  • — återkörning av playbooken för test av idempotens, side_effect
  • verify? — действия, не относящиеся непосредственно к роли, но нужные для тестов,
  • — körning av tester av den resulterande konfigurationen med hjälp av testinfra (som standard) /gossinspec/cleanup,
  • — borttagning av instansen.? — (в новых версиях) — грубо говоря, «очистка» внешней инфраструктуры, задетой Молекулой,
  • — borttagning av instanser från föregående körning av Molekyl (om några finns kvar), — borttagning av instans.

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

Varje steg ovan kan köras separat med hjälp av molecule. Men det är viktigt att förstå att det kan finnas en specifik sekvens av steg för varje sådan cli-kommando, vilket kan fås genom att köra molecule matrix. Till exempel, när kommandot — körning av den testade playbooken, (körning av den testerade playbooken) kommer följande steg att utföras:

$ molecule matrix converge
...
└── default         # scenariets namn
    ├── dependency  # installation av beroenden
    ├── create      # skapande av instans
    ├── prepare     # förberedelse av instans
    └── converge    # körning av playbooken

Sekvensen av dessa steg kan redigeras. Om något från listan redan har utförts, kommer det att hoppas över. Den aktuella statusen samt konfigurationen av instanserna sparas av Molecule i katalogen $TMPDIR/molecule//.

Att lägga till steg med ? kan göras genom att beskriva önskade åtgärder i formatet av en ansible-playbook, och namnet på filen görs enligt steget: prepare.yml/side_effect.yml. Dessa filer förväntas av Molecule i scenarikatalogen.

Driver

En drivrutin är en entitet där instanser för tester skapats.
Listan över standarddrivrutiner, för vilka Molecule har färdiga mallar, är följande: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.

I de flesta fall är mallarna filer create.yml och destroy.yml i scenarikatalogen som beskriver skapandet och borttagning av en instans respektive.
Undantagen är Docker och Vagrant, eftersom interaktionen med deras moduler kan ske utan de ovan nämnda filerna.

Det är värt att nämna drivrutinen Delegated, eftersom vid dess användning i skapande- och borttagningsfilerna av instansen endast arbete med instanskonfigurationen beskrivs, resten måste beskrivas av ingenjören.

Standarddrivrutinen är Docker.

Nu går vi över till praktiken och kommer att se på ytterligare detaljer där.

Komma igång

Som en "hello world" kommer vi att testa en enkel roll för att installera nginx. Vi väljer Docker som drivrutin – jag tror att det är installerat hos de flesta av er (och kom ihåg att Docker är standarddrivrutinen).

Vi kommer att förbereda virtualenv och installera molecule:

> pip install virtualenv
> virtualenv -p `which python2` venv
> source venv/bin/activate
> pip install molecule docker  # molecule kommer att installera ansible som beroende; docker för drivrutinen

Som nästa steg initierar vi en ny roll.
Initiering av en ny roll, som en ny scenariel, görs med kommandot molecule init <params>:

> molecule init role -r nginx
--> Initierar ny roll nginx...
Roll initierad i <path>\/nginx framgångsrikt.
> cd nginx
> tree -L 1
.
├── README.md
├── defaults
├── handlers
├── meta
├── molecule
├── tasks
└── vars

6 kataloger, 1 fil

Det har blivit en typisk ansible-roll. Vidare görs all interaktion med Molekylens CLI från rotkatalogen för rollen.

Låt oss titta på vad som finns i rollkatalogen:

> tree molecule\/default\/\nmolecule\/default\/\n├── Dockerfile.j2  # Jinja-mall för Dockerfile
├── INSTALL.rst.   # Lite information om installation av beroenden för programmet
├── molecule.yml   # Konfigurationsfil
├── playbook.yml   # Spelbok för att köra rollen
└── tests          # Katalog med tester av verifieringsstadiet
    └── test_default.py

1 katalog, 6 filer

Låt oss gå igenom konfigurationen molecule\/default\/molecule.yml (vi ändrar bara docker-bilden):

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

syntax

Denna sektion beskriver källan för beroenden.

Möjliga alternativ: galaxy, gilt, shell.

Shell är helt enkelt en kommandotolk som används om galaxy och gilt inte täcker dina behov.

Jag kommer inte att stanna kvar här länge, det är tillräckligt beskrivet i dokumentationen.

driver

Namn på drivrutinen. I vårt fall är det docker.

— körning av linters. Som standard används

Yamllint används som linter.

Nyttiga alternativ i denna del av konfigurationen är att ange konfigurationsfil för yamllint, vidarebefordra miljövariabler eller stänga av linter:

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

platforms

Beskriver instanskonfigurationen.
I fallet med docker som drivrutin itererar Molekylen genom denna sektion, och varje element i listan är tillgängligt i Dockerfile.j2 som variabel item.

I fallet med drivrutinen där create.yml och destroy.yml, sektionen är tillgänglig i dem som molecule_yml.platforms, och iterationerna över den är redan beskrivna i dessa filer.

Eftersom Molekylen tillhandahåller hantering av instanser för ansible-moduler, så måste listan över möjliga inställningar också sökas där. För docker, till exempel, används modulen docker_container_module. Vilka moduler som används i övriga drivrutiner kan hittas i dokumentationen.

Och exempel på användning av olika drivrutiner kan hittas i testerna av själva Molekylen.

Låt oss ersätta här centos:7 till ubuntu.

provisioner

«Leverantör» är en entitet som hanterar instanser. I fallet med Molekyl är det ansible, stöd för andra planeras inte, så denna sektion kan med förbehåll kallas en utökad ansible-konfiguration.
Här kan man ange mycket, jag kommer att lyfta fram de viktigaste punkterna enligt min åsikt:

  • playbooks: man kan ange vilka playbooks som ska användas vid specifika faser.

provisioner:
  namn: ansible
  playbooks:
    skapa: create.yml
    förstöra: ..\/default\/destroy.yml
    konvergera: playbook.yml
    bieffekt: side_effect.yml
    städa: cleanup.yml

provisioner:
  namn: ansible
  config_options:
    defaults:
      fact_caching: jsonfile
    ssh_connection:
      scp_if_ssh: True

provisioner:
  namn: ansible  
  connection_options:
    ansible_ssh_common_args: "-o 'UserKnownHostsFile=\/dev\/null' -o 'ForwardAgent=yes'"

  • options: Ansible-parametrar och miljövariabler

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

scenario

Titel och beskrivning av scenariets sekvenser.
Man kan ändra standard åtgärdsmatrisen för en viss kommando genom att lägga till nyckeln <command>_sequence och som värde för den definiera den lista över steg som vi behöver.
Anta att vi vill ändra åtgärdssekvensen vid körning av kommandot för att köra playbook: molecule converge

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

verifierare

Inställning av ramverket för tester och dess linter. Som standard används (som standard) / och destroy. Möjliga alternativ liknar de ovan nämnda:

verifierare:
  namn: 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:
    namn: flake8
    options:
      benchmark: True
    enabled: False
    env:
      FOO: bar

Låt oss återgå till vår roll. Vi redigerar filen tasks\/main.yml till följande format:

---
- namn: Installera nginx
  apt:
    namn: nginx
    tillstånd: närvarande

- namn: Starta nginx
  service:
    namn: nginx
    tillstånd: startad

Och vi lägger till tester i 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")

Färdigt, vi måste bara köra (från roten av rollen, påminner jag om):

> molecule test

Lång utskrift under spoilern:

--> Validerar schema /nginx/molecule/default/molecule.yml.
Validering slutförd framgångsrikt.
--> Testmatris

└── default
    ├── lint
    ├── destroy
    ├── dependency
    ├── syntax
    ├── create
    ├── prepare
    ├── converge
    ├── idempotence
    ├── side_effect
    ├── verify
    └── destroy

--> Scenario: 'default'
--> Åtgärd: 'lint'
--> Utför Yamllint på filer som finns i /nginx/...
Lint slutförd framgångsrikt.
--> Utför Flake8 på filer som finns i /nginx/molecule/default/tests/...
Lint slutförd framgångsrikt.
--> Utför Ansible Lint på /nginx/molecule/default/playbook.yml...
Lint slutförd framgångsrikt.
--> Scenario: 'default'
--> Åtgärd: 'destroy'

    SPEL [Destroy] *****************************************************************

    UPPGIFT [Zdestroy molekylinstans(er)] ********************************************
    ändrad: [localhost] => (item=None)
    ändrad: [localhost]

    UPPGIFT [Vänta på att instans(er) raderas] *******************************
    ok: [localhost] => (item=None)
    ok: [localhost]

    UPPGIFT [Ta bort docker nätverk(en)] ************************************************

    SPEL RECAP *********************************************************************
    localhost                  : ok=2    ändrad=1    oanträffbar=0    misslyckad=0

--> Scenario: 'default'
--> Åtgärd: 'dependency'
Hoppa över, saknar kravfilen.
--> Scenario: 'default'
--> Åtgärd: 'syntax'

    spelbok: /nginx/molecule/default/playbook.yml

--> Scenario: 'default'
--> Åtgärd: 'create'

    SPEL [Skapa] ******************************************************************

    UPPGIFT [Logga in på en Docker-register] **********************************************
    hoppa över: [localhost] => (item=None)

    UPPGIFT [Skapa Dockerfiler från bildnamn] *************************************
    ändrad: [localhost] => (item=None)
    ändrad: [localhost]

    UPPGIFT [Upptäck lokala Docker-bilder] ********************************************
    ok: [localhost] => (item=None)
    ok: [localhost]

    UPPGIFT [Bygg en Ansible-kompatibel bild] ***************************************
    ändrad: [localhost] => (item=None)
    ändrad: [localhost]

    UPPGIFT [Skapa docker nätverk(en)] ************************************************

    UPPGIFT [Skapa molekylinstans(er)] *********************************************
    ändrad: [localhost] => (item=None)
    ändrad: [localhost]

    UPPGIFT [Vänta på att instans(er) skapas] *******************************
    ändrad: [localhost] => (item=None)
    ändrad: [localhost]

    SPEL RECAP *********************************************************************
    localhost                  : ok=5    ändrad=4    oanträffbar=0    misslyckad=0

--> Scenario: 'default'
--> Åtgärd: 'prepare'
Hoppa över, förberedelse spelbok är inte konfigurerad.
--> Scenario: 'default'
--> Åtgärd: 'converge'

    SPEL [Konvergera] ****************************************************************

    UPPGIFT [Samlar fakta] *********************************************************
    ok: [instans]

    UPPGIFT [nginx : Installera nginx] ***************************************************
    ändrad: [instans]

    UPPGIFT [nginx : Starta nginx] *****************************************************
    ändrad: [instans]

    SPEL RECAP *********************************************************************
    instans                   : ok=3    ändrad=2    oanträffbar=0    misslyckad=0

--> Scenario: 'default'
--> Åtgärd: 'idempotence'
Idempotens slutförd framgångsrikt.
--> Scenario: 'default'
--> Åtgärd: 'side_effect'
Hoppa över, side effect spelbok är inte konfigurerad.
--> Scenario: 'default'
--> Åtgärd: 'verify'
--> Utför Testinfra tester som finns i /nginx/molecule/default/tests/...
    ============================= test session startar ==============================
    plattform darwin -- Python 2.7.15, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
    rootdir: /nginx/molecule/default, inifile:
    plugins: testinfra-1.16.0
insamlat 4 objekt

    tester/test_default.py ....                                               [100%]

    ========================== 4 godkända på 27.23 sekunder ===========================
Verifiering slutförd framgångsrikt.
--> Scenario: 'default'
--> Åtgärd: 'destroy'

    SPEL [Destroy] *****************************************************************

    UPPGIFT [Zdestroy molekylinstans(er)] ********************************************
    ändrad: [localhost] => (item=None)
    ändrad: [localhost]

    UPPGIFT [Vänta på att instans(er) raderas] *******************************
    ändrad: [localhost] => (item=None)
    ändrad: [localhost]

    UPPGIFT [Ta bort docker nätverk(en)] ************************************************

    SPEL RECAP *********************************************************************
    localhost                  : ok=2    ändrad=2    oanträffbar=0    misslyckad=0

Vår enkla roll testades utan problem.
Tänk på att om det uppstår problem vid användning molecule test, så, om du inte har förändrat den standardiserade sekvensen, kommer Molecule att ta bort instansen.

För debug är följande kommandon användbara:

> molecule --debug  # debug info. Vid vanlig körning döljer Molecule loggar.
> molecule converge          # Lämnar instansen efter att ha kört den testade rollen.
> molecule login             # Logga in på den skapade instansen.
> molecule --help            # Fullständig lista över kommandon.

Befintlig roll

Att lägga till ett nytt scenario till en befintlig roll sker från rollens katalog 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 kan parametern -s utlämnas, eftersom ett scenario kommer att skapas. default.

Slutsats

Som du kan se är Molecule inte särskilt komplicerat, och genom att använda egna mallar kan utrullningen av ett nytt scenario reduceras till att redigera variabler i playbooks för att skapa och ta bort instanser. Molecule integreras smidigt med CI-system, vilket ökar utvecklingshastigheten genom att minska tiden för manuell testning av playbooks.

Tack för din uppmärksamhet. Om du har erfarenhet av att testa ansible-roller som inte är relaterade till Molecule - berätta gärna om det i kommentarerna!

Källa: habr.com

Köp pålitlig webbhotell med DDoS-skydd, VPS VDS-servrar 🔥 Köp pålitlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster