Instruktion: hvordan man tester ansible-roller og opdager problemer før produktion

Hej alle sammen!

Jeg arbejder som DevOps-ingeniør i en hotelbookingtjeneste Ostrovok.ru. I denne artikel vil jeg gerne fortælle om vores erfaring med test af ansible-roller.

Hos Ostrovok.ru bruger vi ansible som konfigurationsmanager. For nylig kom vi til behovet for at teste roller, men det viste sig, at der ikke findes så mange værktøjer til dette — den mest populære er nok Molecule-rammen, så vi besluttede at bruge den. Men det viste sig, at dens dokumentation undlader mange faldgruber. Vi kunne ikke finde en tilstrækkelig detaljeret vejledning på russisk, så vi besluttede at skrive denne artikel.

Instruktion: hvordan man tester ansible-roller og opdager problemer før produktion

Molecule

Molecule — en ramme til hjælp med test af ansible-roller.

Forenklet beskrivelse: Molecule opretter en instans på den platform, du angiver (cloud, virtual machine, container; se nærmere i afsnittet Driver), kører din rolle på den, udfører derefter testene og sletter instansen. Hvis der opstår en fejl under et af trinene, vil Molecule informere dig om dette.

Nu mere detaljeret.

Lidt teori

Lad os se på to centrale enheder i Molecule: Scenario og Driver.

Scenario

Et scenarie indeholder en beskrivelse af, hvad, hvor, hvordan og i hvilken rækkefølge der skal udføres. En rolle kan have flere scenarier, og hvert scenarie er et katalog på stien /molecule/, som indeholder beskrivelserne af de nødvendige handlinger til testen. Der skal nødvendigvis være et scenarie default, som automatisk vil blive oprettet, hvis du initialiserer rollen med Molecule. Navnene på de følgende scenarier vælges efter dit valg.

Handlingsrækkefølgen ved test i et scenarie kaldes matrix, og som standard er den sådan:

(Trin, der er markeret ?, springes som standard over, medmindre de er beskrevet af brugeren)

  • lint — kørsel af linters. Som standard bruges yamllint og flake8,
  • destroy — sletning af instanser fra den tidligere kørsel af Molecule (hvis der er nogen tilbage),
  • dependency? — установка ansible-зависимости тестируемой роли,
  • syntax — syntaksprøvning af rollen med hjælp fra ansible-playbook --syntax-check,
  • create — oprettelse af en instans,
  • forbered? — подготовка инстанса; например, проверка / установка python2
  • converge — kørsel af den testede playbook,
  • idempotence — genkørsel af playbooken for at teste for idempotens,
  • side_effect? — действия, не относящиеся непосредственно к роли, но нужные для тестов,
  • verify — kørsel af testene for den resulterende konfiguration ved hjælp af testinfra(som standard) /goss/inspec,
  • cleanup? — (в новых версиях) — грубо говоря, «очистка» внешней инфраструктуры, задетой Молекулой,
  • destroy — sletning af instansen.

Denne rækkefølge dækker de fleste tilfælde, men hvis nødvendigt, kan den ændres.

Hvert af de ovennævnte trin kan køres individuelt ved hjælp af molecule. Men det er vigtigt at forstå, at hver sådan CLI-kommando kan have sin egen sekvens af handlinger, som kan findes ved at udføre molecule matrix. For eksempel, når man kører kommandoen converge (kørsel af den testede playbook) vil følgende handlinger blive udført:

$ molecule matrix converge
...
└── default         # navnet på scenariet
    ├── dependency  # installation af afhængigheder
    ├── create      # oprettelse af instans
    ├── prepare     # forberedelse af instans
    └── converge    # kørsel af playbooken

Sekvensen af disse handlinger kan redigeres. Hvis noget på listen allerede er udført, vil det blive sprunget over. Den aktuelle tilstand samt konfigurationen af instanserne gemmer Molecule i mappen $TMPDIR/molecule//.

Tilføj trin med ? kan gøres ved at beskrive de ønskede handlinger i ansible-playbook-format, og filnavnet skal være i overensstemmelse med trin: prepare.yml/side_effect.yml. Molecule forventer disse filer i scenariemappen.

Driver

En driver er en enhed, hvor instanser til tests oprettes.
Listen over standarddrivere, for hvilke der er klargjorte skabeloner i Molecule, er som følger: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.

I de fleste tilfælde er skabelonerne filer create.yml og destroy.yml i scenariemappen, som beskriver oprettelse og sletning af instanser henholdsvis.
Undtagelserne er Docker og Vagrant, da interaktion med deres moduler kan finde sted uden de ovennævnte filer.

Det er værd at fremhæve driveren Delegated, da der ved brug af den kun beskrives arbejde med konfigurationen af instanserne i filerne til oprettelse og sletning, mens alt andet skal beskrives af ingeniøren.

Standarddriveren er Docker.

Lad os nu gå videre til praktisk arbejde og se på yderligere funktioner der.

Getting Started

Som et 'hello world' vil vi teste en simpel rolle til installation af nginx. Vi vælger Docker som driver – jeg tror, de fleste af jer har det installeret (og husk, at Docker er standarddriveren).

Lad os forberede virtualenv og installere molecule:

> pip install virtualenv
> virtualenv -p `which python2` venv
> source venv/bin/activate
> pip install molecule docker  # molecule installerer ansible som afhængighed; docker til driveren

Næste skridt er at initialisere en ny rolle.
Initialisering af en ny rolle, ligesom et nyt scenarie, sker ved hjælp af kommandoen molecule init:

> molecule init role -r nginx
--> Initializing new role nginx...
Initialized role in /nginx successfully.
> cd nginx
> tree -L 1
.
├── README.md
├── defaults
├── handlers
├── meta
├── molecule
├── tasks
└── vars

6 directories, 1 file

Det er blevet en typisk ansible-rolle. Fremover foregår alle interaktioner med Molecule CLI fra roden af rollen.

Lad os se, hvad der er i rolens mappe:

> tree molecule/default/
molecule/default/
├── Dockerfile.j2  # Jinja-skabelon til Dockerfile
├── INSTALL.rst.   # Nogle oplysninger om installation af scriptafhængigheder
├── molecule.yml   # Konfigurationsfil
├── playbook.yml   # Playbook til at køre rollen
└── tests          # Mappe med verificeringstests
    └── test_default.py

1 directory, 6 files

Lad os gennemgå konfigurationen molecule/default/molecule.yml (vi ændrer kun docker image):

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

dependency

Denne sektion beskriver kilden til afhængigheder.

Mulige valgmuligheder: galaxy, gilt, shell.

Shell er simpelthen en kommandoskall, der bruges, hvis galaxy og gilt ikke dækker dine behov.

Jeg vil ikke dvæle ved det her, da det er tilstrækkeligt beskrevet i dokumentationen.

driver

Navnet på driveren. I vores tilfælde er det docker.

lint

Som linter bruges yamllint.

Nyttige indstillinger i denne del af konfigurationen er muligheden for at angive en konfigurationsfil til yamllint, videregive miljøvariabler eller deaktivere linteren:

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

platforms

Beskriver konfigurationen af instanser.
Når docker er driveren, itererer Molecule over denne sektion, og hvert element i listen er tilgængeligt i Dockerfile.j2 som variablen item.

I tilfælde af en driver, hvor det er påkrævet, create.yml og destroy.yml, sektionen er tilgængelig i dem som molecule_yml.platforms, og iterationerne over den er allerede beskrevet i disse filer.

Da Molecule muliggør styring af instanser til ansible-moduler, skal listen over mulige indstillinger også findes der. For docker f.eks. bruges modulet docker_container_module. Hvilke moduler der bruges i de øvrige drivere kan findes i dokumentationen.

Og også eksempler på brug af forskellige drivere kan findes i testene af selve Molecule.

Lad os ændre her centos:7 og få en fungerende kommando. ubuntu.

provisioner

«Leverandøren» er en enhed, der styrer instanser. I tilfælde af Molecule er det ansible, og støtte til andre planlægges ikke, så denne sektion kan med forbehold kaldes en udvidet konfiguration af ansible.
Her kan du angive mange ting, jeg vil fremhæve de vigtigste punkter, efter min mening:

  • playbooks: du kan angive, hvilke playbooks der skal anvendes på bestemte trin.

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

  • options: Ansible-parametre og miljøvariabler

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

scenario

Navn og beskrivelse af sekvenserne i scenariet.
Du kan ændre standard handlingsmatrixen for en given kommando ved at tilføje nøglen <command>_sequence og definere den ønskede liste over trin som værdi for denne nøgle.
Lad os sige, vi ønsker at ændre rækkefølgen af handlinger ved udførelse af kommandokommandoen for at køre playbook: molecule converge

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

verifier

Opsætning af rammeværket til tests og linter dertil. Som standard anvendes testinfra og flake8. Mulige indstillinger ligner dem, der er beskrevet 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

Lad os vende tilbage til vores rolle. Vi redigerer filen tasks\/main.yml til følgende format:

---
- name: Installer nginx
  apt:
    name: nginx
    state: present

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

Og vi tilføjer testene 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ærdig, det er kun tilbage at køre (fra roden af rollen, husk):

> molecule test

Lang output under spoiler:

--> Validerer skema /nginx/molecule/default/molecule.yml. 
Valideringen blev gennemført med succes. 
--> Testmatrix 

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

--> Scenarie: 'default' 
--> Handling: 'lint' 
--> Udfører Yamllint på filer fundet i /nginx/... 
Lint blev gennemført med succes. 
--> Udfører Flake8 på filer fundet i /nginx/molecule/default/tests/... 
Lint blev gennemført med succes. 
--> Udfører Ansible Lint på /nginx/molecule/default/playbook.yml... 
Lint blev gennemført med succes. 
--> Scenarie: 'default' 
--> Handling: 'destroy' 

    PLAY [Destroy] ***************************************************************** 

    OPGAVE [Ødelæg molekyleinstans(er)] ******************************************** 
    ændret: [localhost] => (item=None) 
    ændret: [localhost] 

    OPGAVE [Vent på, at sletning af instans(er) er afsluttet] ******************************* 
    ok: [localhost] => (item=None) 
    ok: [localhost] 

    OPGAVE [Slet Docker-netværk(e)] ************************************************ 

    PLAY OPSUMMERING ********************************************************************* 
    localhost                  : ok=2    ændret=1    utilgængelig=0    fejlet=0 

--> Scenarie: 'default' 
--> Handling: 'dependency' 
Springer over, kravfilen mangler. 
--> Scenarie: 'default' 
--> Handling: 'syntax' 

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

--> Scenarie: 'default' 
--> Handling: 'create' 

    PLAY [Oprettelse] ****************************************************************** 

    OPGAVE [Log ind på et Docker-register] ********************************************** 
    springer over: [localhost] => (item=None) 

    OPGAVE [Opret Dockerfiler fra billede navne] ************************************* 
    ændret: [localhost] => (item=None) 
    ændret: [localhost] 

    OPGAVE [Opdag lokale Docker-billeder] ******************************************** 
    ok: [localhost] => (item=None) 
    ok: [localhost] 

    OPGAVE [Byg et Ansible-kompatibelt billede] *************************************** 
    ændret: [localhost] => (item=None) 
    ændret: [localhost] 

    OPGAVE [Opret Docker-netværk(e)] ************************************************ 

    OPGAVE [Opret molekyleinstans(er)] ********************************************* 
    ændret: [localhost] => (item=None) 
    ændret: [localhost] 

    OPGAVE [Vent på, at oprettelsen af instans(er) er afsluttet] ******************************* 
    ændret: [localhost] => (item=None) 
    ændret: [localhost] 

    PLAY OPSUMMERING ********************************************************************* 
    localhost                  : ok=5    ændret=4    utilgængelig=0    fejlet=0 

--> Scenarie: 'default' 
--> Handling: 'prepare' 
Springer over, forberedelses playbook ikke konfigureret. 
--> Scenarie: 'default' 
--> Handling: 'converge' 

    PLAY [Sammensmeltning] *************************************************************** 

    OPGAVE [Indsamling af fakta] **************************************************** 
    ok: [instans] 

    OPGAVE [nginx : Installer nginx] ************************************************* 
    ændret: [instans] 

    OPGAVE [nginx : Start nginx] ***************************************************** 
    ændret: [instans] 

    PLAY OPSUMMERING ********************************************************************* 
    instans                   : ok=3    ændret=2    utilgængelig=0    fejlet=0 

--> Scenarie: 'default' 
--> Handling: 'idempotence' 
Idempotens blev gennemført med succes. 
--> Scenarie: 'default' 
--> Handling: 'side_effect' 
Springer over, side effect playbook ikke konfigureret. 
--> Scenarie: 'default' 
--> Handling: 'verify' 
--> Udfører Testinfra-tests fundet i /nginx/molecule/default/tests/... 
    ============================= test session starter ==============================
    platform 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 
indsamlet 4 elementer 

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

    ========================== 4 bestået på 27.23 sekunder =========================== 
Verifikationen blev gennemført med succes. 
--> Scenarie: 'default' 
--> Handling: 'destroy' 

    PLAY [Destroy] ***************************************************************** 

    OPGAVE [Ødelæg molekyleinstans(er)] ******************************************** 
    ændret: [localhost] => (item=None) 
    ændret: [localhost] 

    OPGAVE [Vent på, at sletning af instans(er) er afsluttet] ******************************* 
    ændret: [localhost] => (item=None) 
    ændret: [localhost] 

    OPGAVE [Slet Docker-netværk(e)] ************************************************ 

    PLAY OPSUMMERING ********************************************************************* 
    localhost                  : ok=2    ændret=2    utilgængelig=0    fejlet=0

Vores enkle rolle blev testet uden problemer.
Det er vigtigt at huske, at hvis der opstår problemer under brugen, molecule test, så hvis du ikke har ændret den standard sekvens, fjerner Molecule instansen.

Til debugging er følgende kommandoer nyttige:

> molecule --debug  # debug info. Ved normal kørsel skjuler Molecule logs.
> molecule converge # Beholder instansen efter at have kørt den testede rolle.
> molecule login # Log ind på den oprettede instans.
> molecule --help # Fuld liste over kommandoer.

Eksisterende rolle

Når du tilføjer et nyt scenarie til en eksisterende rolle, fra rollekataloget gør dette med følgende kommandoer:

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

Hvis dette er det første scenarie i rollen, kan parameteren -s udsættes, da et scenarie vil blive oprettet. default.

Konklusion

Som du kan se, er Molecule ikke særlig kompliceret, og ved at bruge egne skabeloner kan du reducere implementeringen af et nyt scenarie til at redigere variabler i playbooks til at oprette og slette instanser. Molecule integreres problemfrit med CI-systemer, hvilket gør det muligt at øge udviklingshastigheden ved at forkorte tiden til manuel test af playbooks.

Tak for din opmærksomhed. Hvis du har erfaring med test af ansible-roller, som ikke er relateret til Molecule - del det i kommentarerne!

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster