Hallo zusammen!
Ich arbeite als DevOps-Ingenieur bei einem Hotelbuchungsservice. . In diesem Artikel möchte ich über unsere Erfahrungen mit dem Testen von Ansible-Rollen berichten.
Bei Ostrovok.ru verwenden wir Ansible als Konfigurationsmanager. Kürzlich sind wir zu der Notwendigkeit gekommen, Rollen zu testen, aber es stellte sich heraus, dass es dafür nicht viele Werkzeuge gibt – das wohl bekannteste ist das Framework Molecule, also haben wir beschlossen, es zu verwenden. Allerdings stellte sich heraus, dass die Dokumentation viele Fallstricke verschweigt. Ein ausreichend detaillierter Leitfaden auf Russisch war nicht zu finden, also haben wir beschlossen, diesen Artikel zu schreiben.

Molecule
ist ein Framework zur Unterstützung beim Testen von Ansible-Rollen.
Eine vereinfachte Beschreibung: Molecule erstellt eine Instanz auf der von Ihnen angegebenen Plattform (Cloud, virtuelle Maschine, Container; näheres siehe Abschnitt ), führt Ihre Rolle darauf aus, führt dann Tests durch und löscht die Instanz. Sollte bei einem der Schritte ein Fehler auftreten, wird Molecule Sie darüber informieren.
Jetzt im Detail.
Ein wenig Theorie
Betrachten wir zwei wichtige Entitäten von Molecule: Scenario und Driver.
Scenario
Ein Szenario enthält eine Beschreibung dessen, was, wo, wie und in welcher Reihenfolge ausgeführt wird. Eine Rolle kann mehrere Szenarien haben, und jedes ist ein Verzeichnis unter /molecule/, das die Beschreibungen der erforderlichen Testaktionen enthält. Es sollte unbedingt ein Szenario vorhanden sein, das automatisch erstellt wird, wenn Sie die Rolle mit Molecule initialisieren. Die Namen der folgenden Szenarien können Sie nach Belieben wählen. defaultDie Reihenfolge der Aktionen im Test eines Szenarios wird
als bezeichnet, und standardmäßig sieht sie so aus: matrix(Schritte, die mit
markiert sind, werden standardmäßig übersprungen, sofern sie nicht vom Benutzer beschrieben werden) ?lint
ist der Durchlauf der Linter. Standardmäßig werdenflake8yamllintunddestroy,ist das Löschen der Instanzen vom vorherigen Durchlauf von Molecule (sofern noch vorhanden),syntaxdependency? — установка ansible-зависимости тестируемой роли,ist die Überprüfung der Syntax der Rolle mit Hilfe vonansible-playbook --syntax-checkist das Erstellen einer Instanz,,createist das Ausführen des zu testenden Playbooks,vorbereiten? — подготовка инстанса; например, проверка / установка python2convergeidempotenceist der wiederholte Start des Playbooks zum Test auf Idempotenz,side_effectist die Ausführung der Tests der resultierenden Konfiguration mit Hilfe von? — действия, не относящиеся непосредственно к роли, но нужные для тестов,verifytestinfra(standardmäßig) /gossinspec/ist das Löschen der Instanz.,Bereinigung? — (в новых версиях) — грубо говоря, «очистка» внешней инфраструктуры, задетой Молекулой,ist das Löschen der Instanzen vom vorherigen Durchlauf von Molecule (sofern noch vorhanden),Diese Reihenfolge deckt die meisten Fälle ab, kann aber bei Bedarf geändert werden.
Jeder der oben genannten Schritte kann auch einzeln mit Hilfe von
ausgeführt werden. molecule. Es ist jedoch wichtig zu verstehen, dass für jeden solchen CLI-Befehl eine eigene Aktionssequenz existieren kann, die man herausfinden kann, indem man molecule matrix. Zum Beispiel, wenn der Befehl ausgeführt wird converge (Durchlauf des getesteten Playbooks) werden folgende Aktionen ausgeführt:
$ molecule matrix converge
...
└── default # Szenarioname
├── dependency # Abhängigkeiten installieren
├── create # Instanz erstellen
├── prepare # Instanz vorbereiten
└── converge # Playbook durchlaufenDie Reihenfolge dieser Schritte kann bearbeitet werden. Wenn etwas aus der Liste bereits ausgeführt wurde, wird es übersprungen. Den aktuellen Zustand sowie die Konfiguration der Instanzen speichert Molecule im Verzeichnis $TMPDIR/molecule//.
Schritte mit ? kann hinzugefügt werden, indem die gewünschten Aktionen im Format eines Ansible-Playbooks beschrieben werden, und der Dateiname entsprechend dem Schritt lautet: prepare.yml/side_effect.yml. Molecule erwartet diese Dateien im Szenariopaket.
Driver
Ein Treiber ist eine Entität, in der Instanzen für Tests erstellt werden.
Die Liste der Standardtreiber, für die Molecule Vorlagen hat, ist wie folgt: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.
In den meisten Fällen sind Vorlagen Dateien create.yml und destroy.yml im Szenariopaket, die das Erstellen und Löschen von Instanzen beschreiben.
Ausnahmen sind Docker und Vagrant, da die Interaktionen mit ihren Modulen ohne die oben genannten Dateien erfolgen können.
Es ist wichtig, den Treiber Delegated hervorzuheben, da bei dessen Verwendung in den Dateien zum Erstellen und Löschen von Instanzen nur die Arbeit mit der Konfiguration der Instanzen beschrieben wird; alles andere muss der Ingenieur beschreiben.
Der Standardtreiber ist Docker.
Jetzt gehen wir zur Praxis über und werden die weiteren Besonderheiten dort betrachten.
Erste Schritte
Als 'Hello World' testen wir eine einfache Rolle zur Installation von nginx. Wir wählen Docker als Treiber – ich denke, er ist bei den meisten von Ihnen installiert (und denken Sie daran, dass Docker der Standardtreiber ist).
Lassen Sie uns virtualenv und in ihn installieren molecule:
> pip install virtualenv
> virtualenv -p `which python2` venv
> source venv/bin/activate
> pip install molecule docker # Molecule installiert Ansible als Abhängigkeit; Docker für den TreiberIm nächsten Schritt initialisieren wir eine neue Rolle.
Die Initialisierung einer neuen Rolle erfolgt, wie die einer neuen Szenario, mit dem Befehl molecule init:
> molecule init role -r nginx
--> Initialisierung der neuen Rolle nginx...
Rolle erfolgreich in /nginx initialisiert.
> cd nginx
> tree -L 1
.
├── README.md
├── defaults
├── handlers
├── meta
├── molecule
├── tasks
└── vars
6 Verzeichnisse, 1 DateiEs ist eine typische Ansible-Rolle entstanden. Alle weiteren Interaktionen mit der Molecule-CLI erfolgen vom Hauptverzeichnis der Rolle aus.
Sehen wir uns an, was sich im Verzeichnis der Rolle befindet:
> tree molecule/default/
molecule/default/
├── Dockerfile.j2 # Jinja-Vorlage für Dockerfile
├── INSTALL.rst. # Einige Informationen zur Installation der Abhängigkeiten
├── molecule.yml # Konfigurationsdatei
├── playbook.yml # Playbook zum Ausführen der Rolle
└── tests # Verzeichnis mit Tests für die Verifizierung
└── test_default.py
1 Verzeichnis, 6 DateienLassen Sie uns die Konfiguration analysieren molecule/default/molecule.yml (wir ändern nur das 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: flake8dependency
Dieser Abschnitt beschreibt die Quelle der Abhängigkeiten.
Mögliche Optionen: , , shell.
Shell ist einfach eine Kommandozeile, die verwendet wird, wenn galaxy und gilt Ihre Anforderungen nicht abdecken.
Ich werde hier nicht lange verweilen, es ist bereits ausreichend beschrieben in .
driver
Name des Treibers. In unserem Fall ist das Docker.
ist der Durchlauf der Linter. Standardmäßig werden
Als Linter wird yamllint verwendet.
Nützliche Optionen in diesem Teil der Konfiguration sind die Möglichkeit, eine Konfigurationsdatei für yamllint anzugeben, Umgebungsvariablen weiterzugeben oder den Linter zu deaktivieren:
lint:
name: yamllint
options:
config-file: foo/bar
env:
FOO: bar
enabled: Falseplatforms
Beschreibt die Konfiguration der Instanzen.
Im Fall von Docker als Treiber iteriert Molecule durch diesen Abschnitt, und jedes Element der Liste ist zugänglich in Dockerfile.j2 als Variable item.
Im Fall des Treibers, bei dem Pflicht ist create.yml und destroy.yml, ist der Abschnitt darin verfügbar als molecule_yml.platforms, und die Iteration darüber ist bereits in diesen Dateien beschrieben.
Da Molecule die Verwaltung der Instanzen für Ansible-Module bereitstellt, müssen auch die möglichen Einstellungen dort gesucht werden. Für Docker wird beispielsweise das Modul . Welche Module in den anderen Treibern verwendet werden, kann man finden in .
Ebenso lassen sich Beispiele für die Verwendung verschiedener Treiber finden .
Ersetzen wir hier centos:7 auf ubuntu.
provisioner
«Provider» ist die Entität, die die Instanzen verwaltet. Im Fall von Molecule ist dies ansible, Unterstützung für andere ist nicht geplant, daher kann dieser Abschnitt mit Vorbehalt als erweiterte Konfiguration von ansible bezeichnet werden.
Hier können viele Aspekte erwähnt werden; ich werde die meiner Meinung nach wichtigsten Punkte hervorheben:
- Playbooks: Es kann angegeben werden, welche Playbooks in bestimmten Phasen verwendet werden sollen.
Provisionierer:
Name: ansible
Playbooks:
erstellen: create.yml
löschen: ..\/default\/destroy.yml
konvergieren: playbook.yml
seiteneffekt: side_effect.yml
bereinigen: cleanup.yml- Konfigurationsoptionen:
Provisionierer:
Name: ansible
Konfigurationsoptionen:
Voreinstellungen:
Faktenerfassung: jsonfile
ssh_verbindung:
scp_if_ssh: True- Verbindungsoptionen: Parameter
Provisionierer:
Name: ansible
Verbindungsoptionen:
ansible_ssh_common_args: "-o 'UserKnownHostsFile=\/dev\/null' -o 'ForwardAgent=yes'"- optionen: Ansible-Parameter und Umgebungsvariablen
Provisionierer:
Name: ansible
Optionen:
vvv: true
diff: true
env:
FOO: BARSzenario
Titel und Beschreibung der Szenariensequenzen.
Die Standardaktionsmatrix eines Befehls kann geändert werden, indem der Schlüssel <Befehl>_sequenz hinzugefügt wird, und der gewünschte Schritt als Wert definiert wird.
Angenommen, wir möchten die Reihenfolge der Aktionen beim Ausführen des Befehls zum Ausführen des Playbooks ändern: Molekül konvergieren
# изначально:
# - dependency
# - create
# - prepare
# - converge
scenario:
name: default
converge_sequence:
- create
- convergeÜberprüfer
Konfiguration des Testframeworks und des Linters. Standardmäßig wird der Linter verwendet (standardmäßig) / und destroy. Mögliche Optionen sind ähnlich wie die oben genannten:
Überprüfer:
Name: testinfra
zusätzliche_Dateien_oder_Verzeichnisse:
- ..\/path\/to\/test_1.py
- ..\/path\/to\/test_2.py
- ..\/path\/to\/directory\/*
Optionen:
n: 1
aktiviert: False
env:
FOO: bar
lint:
Name: flake8
Optionen:
benchmark: True
aktiviert: False
env:
FOO: barKommen wir zurück zu unserer Rolle. Bearbeiten wir die Datei tasks\/main.yml zu diesem Format:
---
- Name: Installiere nginx
apt:
Name: nginx
Zustand: vorhanden
- Name: Starte nginx
Dienst:
Name: nginx
Zustand: gestartet
Und fügen wir Tests in molekül\/standard\/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")
Fertig, jetzt muss nur noch ausgeführt werden (von der Root-Rolle, erinnere ich mich):
> molekulartestLange Ausgabe unter dem Spoiler:
--> Validierung des Schemas <path>\/nginx\/molecule\/default\/molecule.yml.
Die Validierung wurde erfolgreich abgeschlossen.
--> Testmatrix
└── default
├── lint
├── destroy
├── dependency
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
└── destroy
--> Szenario: 'default'
--> Aktion: 'lint'
--> Ausführung von Yamllint auf Dateien gefunden in <path>\/nginx\/...
Lint wurde erfolgreich abgeschlossen.
--> Ausführung von Flake8 auf Dateien gefunden in <path>\/nginx\/molecule\/default\/tests\/...
Lint wurde erfolgreich abgeschlossen.
--> Ausführung von Ansible Lint auf <path>\/nginx\/molecule\/default\/playbook.yml...
Lint wurde erfolgreich abgeschlossen.
--> Szenario: 'default'
--> Aktion: 'destroy'
SPIEL [Zerstören] *****************************************************************
AUFGABE [Zerstörung der Molecule-Instanz(en)] ********************************************
geändert: [localhost] => (item=None)
geändert: [localhost]
AUFGABE [Warten auf den Abschluss der Löschung der Instanz(en)] *******************************
ok: [localhost] => (item=None)
ok: [localhost]
AUFGABE [Löschen des Docker-Netzwerks(e)] ************************************************
SPIEL-ZUSAMMENFASSUNG *********************************************************************
localhost : ok=2 geändert=1 unerreichbar=0 fehlgeschlagen=0
--> Szenario: 'default'
--> Aktion: 'dependency'
Überspringen, Anforderungen Datei fehlt.
--> Szenario: 'default'
--> Aktion: 'syntax'
playbook: <path>\/nginx\/molecule\/default\/playbook.yml
--> Szenario: 'default'
--> Aktion: 'create'
SPIEL [Erstellen] ******************************************************************
AUFGABE [Anmelden bei einem Docker-Registry] **********************************************
überspringen: [localhost] => (item=None)
AUFGABE [Erstellen von Dockerfiles aus Bildnamen] *************************************
geändert: [localhost] => (item=None)
geändert: [localhost]
AUFGABE [Entdecken lokaler Docker-Bilder] ********************************************
ok: [localhost] => (item=None)
ok: [localhost]
AUFGABE [Erstellen eines Ansible-kompatiblen Bildes] ***************************************
geändert: [localhost] => (item=None)
geändert: [localhost]
AUFGABE [Erstellen des Docker-Netzwerk(e)] ************************************************
AUFGABE [Erstellen von Molecule-Instanz(en)] *********************************************
geändert: [localhost] => (item=None)
geändert: [localhost]
AUFGABE [Warten auf den Abschluss der Erstellung der Instanz(en)] *******************************
geändert: [localhost] => (item=None)
geändert: [localhost]
SPIEL-ZUSAMMENFASSUNG *********************************************************************
localhost : ok=5 geändert=4 unerreichbar=0 fehlgeschlagen=0
--> Szenario: 'default'
--> Aktion: 'prepare'
Überspringen, Vorbereitungs-Playbook ist nicht konfiguriert.
--> Szenario: 'default'
--> Aktion: 'converge'
SPIEL [Konvergieren] ****************************************************************
AUFGABE [Fakten sammeln] *********************************************************
ok: [instance]
AUFGABE [nginx : Installiere nginx] ***************************************************
geändert: [instance]
AUFGABE [nginx : Starte nginx] *****************************************************
geändert: [instance]
SPIEL-ZUSAMMENFASSUNG *********************************************************************
instance : ok=3 geändert=2 unerreichbar=0 fehlgeschlagen=0
--> Szenario: 'default'
--> Aktion: 'idempotence'
Idempotenz wurde erfolgreich abgeschlossen.
--> Szenario: 'default'
--> Aktion: 'side_effect'
Überspringen, Nebenwirkungs-Playbook ist nicht konfiguriert.
--> Szenario: 'default'
--> Aktion: 'verify'
--> Ausführung von Testinfra-Tests gefunden in <path>\/nginx\/molecule\/default\/tests\/...
============================= Test-Sitzung startet ==============================
Plattform 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
Gesammelte 4 Elemente
tests\/test_default.py .... [100%]
========================== 4 bestanden in 27.23 Sekunden ===========================
Überprüfung wurde erfolgreich abgeschlossen.
--> Szenario: 'default'
--> Aktion: 'destroy'
SPIEL [Zerstören] *****************************************************************
AUFGABE [Zerstörung der Molecule-Instanz(en)] ********************************************
geändert: [localhost] => (item=None)
geändert: [localhost]
AUFGABE [Warten auf den Abschluss der Löschung der Instanz(en)] *******************************
geändert: [localhost] => (item=None)
geändert: [localhost]
AUFGABE [Löschen des Docker-Netzwerks(e)] ************************************************
SPIEL-ZUSAMMENFASSUNG *********************************************************************
localhost : ok=2 geändert=2 unerreichbar=0 fehlgeschlagen=0
Unsere einfache Rolle hat ohne Probleme getestet.
Es ist wichtig zu beachten, dass, wenn Probleme auftreten molecule test, falls Sie die Standardsequenz nicht geändert haben, wird Molecule die Instanz löschen.
Für das Debugging sind die folgenden Befehle hilfreich:
> molecule --debug # Debug-Informationen. Bei normalem Start verbirgt Molecule die Protokolle.
> molecule converge # Lässt die Instanz nach dem Testlauf der Rolle bestehen.
> molecule login # Melden Sie sich bei der erstellten Instanz an.
> molecule --help # Vollständige Liste der Befehle.Vorhandene Rolle
Das Hinzufügen eines neuen Szenarios zu einer bestehenden Rolle erfolgt aus dem Rollenverzeichnis mit den folgenden Befehlen:
# полный список доступных параметров
> molecule init scenarion --help
# создание нового сценария
> molecule init scenario -r <role_name> -s <scenario_name>Falls dies das erste Szenario in der Rolle ist, kann der Parameter -s weggelassen werden, da ein Szenario erstellt wird. default.
Fazit
Wie Sie sehen, ist Molecule nicht sehr kompliziert, und bei Verwendung eigener Vorlagen kann das Bereitstellen eines neuen Szenarios auf das Editieren von Variablen in den Playbooks zur Erstellung und Löschung von Instanzen reduziert werden. Molecule integriert sich problemlos mit CI-Systemen, was die Entwicklungsgeschwindigkeit durch verkürzte manuelle Testzeiten der Playbooks erhöht.
Vielen Dank für Ihre Aufmerksamkeit. Wenn Sie Erfahrungen mit dem Testen von Ansible-Rollen haben, die nicht mit Molecule zusammenhängen, erzählen Sie uns davon in den Kommentaren!
Quelle: habr.com
