Hallo zusammen!
Ich arbeite als DevOps-Ingenieur bei einem Hotelbuchungsdienst. . 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 auf die Notwendigkeit gestoßen, Rollen zu testen, aber wie sich herausstellte, gibt es nicht viele Werkzeuge dafür – das wohl bekannteste ist das Framework Molecule, weshalb wir uns entschieden haben, es zu nutzen. Allerdings stellte sich heraus, dass die Dokumentation viele Fallstricke stillschweigend übersieht. Ein ausreichend detaillierter Leitfaden in Deutsch war nicht zu finden, daher haben wir uns entschlossen, diesen Artikel zu schreiben.

Molecule
– ein Framework zur Unterstützung beim Testen von Ansible-Rollen.
Eine vereinfachte Beschreibung: Molekül erstellt eine Instanz auf der von Ihnen angegebenen Plattform (Cloud, virtuelle Maschine, Container; weitere Details siehe Abschnitt ), führt Ihre Rolle darauf aus, startet daraufhin Tests und entfernt die Instanz. Bei einem Fehler in einem der Schritte wird Molekül Sie darüber informieren.
Jetzt näher im Detail.
Ein wenig Theorie
Betrachten wir zwei Schlüsselkonzepte von Molekül: Szenario und Treiber.
Szenario
Ein Szenario beschreibt, was, wo, wie und in welcher Reihenfolge durchgeführt wird. Eine Rolle kann mehrere Szenarien enthalten, und jedes ist ein Verzeichnis entlang des Pfads. /molecule/, das die erforderlichen Aktionen für den Test beschreibt. Ein Szenario muss unbedingt vorhanden sein. default, das automatisch erstellt wird, wenn Sie eine Rolle mit Molecule initialisieren. Die Namen der folgenden Szenarien werden nach Ihrem Ermessen ausgewählt.
Die Reihenfolge der Testaktionen im Szenario wird genannt matrix, und standardmäßig ist sie wie folgt:
(Schritte, die mit ?, werden standardmäßig übersprungen, sofern sie nicht vom Benutzer beschrieben sind.)
lint— Ausführung von Linters. Standardmäßig werden verwendet:yamllintundflake8,destroy— Löschung von Instanzen aus dem vorherigen Molecule-Lauf (sofern vorhanden),dependency? — установка ansible-зависимости тестируемой роли,syntax— Überprüfung der Syntax der Rolle mitansible-playbook --syntax-check,create— Erstellung einer Instanz,prepare? — подготовка инстанса; например, проверка / установка python2converge— Ausführung des getesteten Playbooks,idempotence— Wiederholte Ausführung des Playbooks zur Prüfung auf Idempotenz,side_effect? — действия, не относящиеся непосредственно к роли, но нужные для тестов,verify— Durchführung von Tests der resultierenden Konfiguration mittestinfra(standardmäßig) /goss/inspec,cleanup? — (в новых версиях) — грубо говоря, «очистка» внешней инфраструктуры, задетой Молекулой,destroy— Löschung der Instanz.
Diese Abfolge deckt die meisten Fälle ab, kann jedoch bei Bedarf geändert werden.
Jeder der oben genannten Schritte kann separat gestartet werden mit molecule. Es ist jedoch wichtig zu verstehen, dass für jeden dieser CLI-Befehle eine eigene Abfolge von Aktionen existieren kann, die Sie herausfinden können, indem Sie molecule matrixausführen. Zum Beispiel, bei der Ausführung des Befehls converge (Durchlauf des getesteten Playbooks) werden die folgenden Aktionen ausgeführt:
$ molecule matrix converge
...
└── default # Name des Szenarios
├── dependency # Abhängigkeiten installieren
├── create # Instanz erstellen
├── prepare # Instanz vorbereiten
└── converge # Playbook ausführenDiese Reihenfolge von Aktionen 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 ? können hinzugefügt werden, indem die gewünschten Aktionen im ansible-Playbook-Format beschrieben werden, und der Dateiname entsprechend dem Schritt gewählt wird: prepare.yml/side_effect.yml. Die Molecule wird diese Dateien im Szenariobereich erwarten.
Driver
Der Driver ist die Entität, in der Instanzen für Tests erstellt werden.
Die Liste der Standardtreiber, für die Molecule Vorlagen bereitstellt, umfasst: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.
In den meisten Fällen sind Vorlagen Dateien create.yml und destroy.yml im Skriptverzeichnis, die die Erstellung und Löschung einer Instanz beschreiben.
Ausnahmen sind Docker und Vagrant, da die Interaktionen mit deren Modulen auch ohne die genannten Dateien erfolgen können.
Besonders hervorzuheben ist der Treiber Delegated, da bei seiner Verwendung in den Dateien für die Erstellung und Löschung der Instanz nur die Arbeit mit der Instanzkonfiguration beschrieben wird, alles andere muss der Ingenieur festlegen.
Der Standardtreiber ist Docker.
Lassen Sie uns jetzt zur Praxis übergehen und weitere Besonderheiten dort besprechen.
Erste Schritte
Als 'Hello World' testen wir eine einfache Rolle zur Installation von nginx. Als Treiber wählen wir Docker – ich denke, die meisten von Ihnen haben es installiert (und denken Sie daran, dass Docker der Standardtreiber ist).
Lassen Sie uns vorbereiten virtualenv und installieren wir darin molecule:
> pip install virtualenv
> virtualenv -p `which python2` venv
> source venv/bin/activate
> pip install molecule docker # molecule wird ansible als Abhängigkeit installieren; docker für den TreiberDer nächste Schritt ist die Initialisierung einer neuen Rolle.
Die Initialisierung einer neuen Rolle, ebenso wie eines neuen Szenarios, erfolgt über den 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 handelt sich um eine typische Ansible-Rolle. Alle weiteren Interaktionen mit der CLI von Molecule erfolgen aus dem Wurzelverzeichnis der Rolle.
Schauen wir uns an, was im Verzeichnis der Rolle vorhanden ist:
> tree molecule/default/
molecule/default/
├── Dockerfile.j2 # Jinja-Vorlage für Dockerfile
├── INSTALL.rst # Einige Informationen zur Installation der Abhängigkeiten des Szenarios
├── molecule.yml # Konfigurationsdatei
├── playbook.yml # Playbook zum Starten der Rolle
└── tests # Verzeichnis mit Tests zur Verifizierung
└── test_default.py
1 Verzeichnis, 6 DateienLass uns die Konfiguration durchgehen 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 Ihren Anforderungen nicht gerecht werden.
Ich werde nicht lange verweilen, dies ist bereits ausführlich beschrieben in .
driver
Der Name des Treibers. Bei uns ist das docker.
lint
Als Linter verwenden wir yamllint.
Nützliche Optionen in diesem Teil der Konfiguration sind die Möglichkeit, eine Konfigurationsdatei für yamllint anzugeben, Umgebungsvariablen durchzureichen 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 über diesen Abschnitt, und jedes Element der Liste ist verfügbar in Dockerfile.j2 als Variable item.
Im Fall von Treibern, bei denen dies erforderlich ist, create.yml und destroy.yml, ist der Abschnitt in diesen als molecule_yml.platforms, und die Iteration darüber ist bereits in diesen Dateien beschrieben.
Da Molecule die Verwaltung von Instanzen an Ansible-Module übergibt, muss die Liste der möglichen Einstellungen dort gesucht werden. Für Docker wird beispielsweise das Modul . Welche Module in den anderen Treibern verwendet werden, kann in .
Gefunden werden, sowie Beispiele für die Verwendung verschiedener Treiber in .
Hier ersetzen wir centos:7 findet man ubuntu.
provisioner
«Anbieter» ist eine Entität, die Instanzen verwaltet. Im Falle von Molecule ist das ansible, die Unterstützung anderer Lösungen ist nicht geplant, weshalb dieser Abschnitt mit Vorbehalt als erweiterte Konfiguration von ansible bezeichnet werden kann.
Hier können viele Dinge angegeben werden, ich möchte die aus meiner Sicht wichtigsten Punkte hervorheben:
- Playbooks: Hier können die Playbooks angegeben werden, die in bestimmten Phasen verwendet werden sollen.
provisioner:
name: ansible
playbooks:
create: create.yml
destroy: ../default/destroy.yml
converge: playbook.yml
side_effect: side_effect.yml
cleanup: cleanup.yml- Config-Optionen:
provisioner:
name: ansible
config_options:
defaults:
fact_caching: jsonfile
ssh_connection:
scp_if_ssh: true- Verbindungsoptionen: Parameter
provisioner:
name: ansible
connection_options:
ansible_ssh_common_args: "-o 'UserKnownHostsFile=/dev/null' -o 'ForwardAgent=yes'"- Optionen: Ansible-Parameter und Umgebungsvariablen
provisioner:
name: ansible
options:
vvv: true
diff: true
env:
FOO: BARSzenario
Name und Beschreibung von Szenarien.
Die Standardaktionsmatrix eines Kommandos kann geändert werden, indem man den Schlüssel _sequence hinzufügt und als Wert die gewünschte Liste von Schritten definiert.
Nehmen wir an, wir möchten die Reihenfolge der Aktionen beim Ausführen des Playbook-Befehls ändern: molecule converge
# изначально:
# - dependency
# - create
# - prepare
# - converge
scenario:
name: default
converge_sequence:
- create
- convergeÜberprüfer
Einrichten des Frameworks für Tests und den entsprechenden Linter. Standardmäßig wird als Linter verwendet testinfra und flake8. Die möglichen Optionen ähneln den oben beschriebenen:
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: barKehren wir zurück zu unserer Rolle. Lassen Sie uns die Datei tasks/main.yml so gestalten:
---
- name: Installiere nginx
apt:
name: nginx
state: present
- name: Starte nginx
service:
name: nginx
state: started
und fügen wir Tests in 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")
Fertig, es bleibt nur noch, (aus dem Wurzelverzeichnis der Rolle, nur zur Erinnerung) zu starten:
> molecule testLängere Ausgabe im Spoiler:
--> Validierung des Schemas /nginx/molecule/default/molecule.yml.
Validierung erfolgreich abgeschlossen.
--> Testmatrix
└── standard
├── lint
├── destroy
├── dependency
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
└── destroy
--> Szenario: 'standard'
--> Aktion: 'lint'
--> Ausführung von Yamllint für Dateien in /nginx/...
Lint erfolgreich abgeschlossen.
--> Ausführung von Flake8 für Dateien in /nginx/molecule/default/tests/...
Lint erfolgreich abgeschlossen.
--> Ausführung von Ansible Lint für /nginx/molecule/default/playbook.yml...
Lint erfolgreich abgeschlossen.
--> Szenario: 'standard'
--> Aktion: 'destroy'
SPIEL [Zerstören] *****************************************************************
AUFGABE [Molecule-Instanz(en) zerstören] ********************************************
geändert: [localhost] => (item=None)
geändert: [localhost]
AUFGABE [Warten auf Abschluss der Instanzlöschung] *******************************
ok: [localhost] => (item=None)
ok: [localhost]
AUFGABE [Docker-Netzwerk(e) löschen] ************************************************
SPIEL-RÜCKBLICK *********************************************************************
localhost : ok=2 geändert=1 unerreichbar=0 fehlgeschlagen=0
--> Szenario: 'standard'
--> Aktion: 'dependency'
Überspringen, Anforderungen fehlen.
--> Szenario: 'standard'
--> Aktion: 'syntax'
playbook: /nginx/molecule/default/playbook.yml
--> Szenario: 'standard'
--> Aktion: 'create'
SPIEL [Erstellen] ******************************************************************
AUFGABE [In ein Docker-Registry einloggen] **********************************************
überspringen: [localhost] => (item=None)
AUFGABE [Dockerfiles aus Bildnamen erstellen] *************************************
geändert: [localhost] => (item=None)
geändert: [localhost]
AUFGABE [Lokale Docker-Images entdecken] ********************************************
ok: [localhost] => (item=None)
ok: [localhost]
AUFGABE [Ein Ansible-kompatibles Bild erstellen] ***************************************
geändert: [localhost] => (item=None)
geändert: [localhost]
AUFGABE [Docker-Netzwerk(e) erstellen] ************************************************
AUFGABE [Molecule-Instanz(en) erstellen] *********************************************
geändert: [localhost] => (item=None)
geändert: [localhost]
AUFGABE [Warten auf Abschluss der Instanz-erstellung] *******************************
geändert: [localhost] => (item=None)
geändert: [localhost]
SPIEL-RÜCKBLICK *********************************************************************
localhost : ok=5 geändert=4 unerreichbar=0 fehlgeschlagen=0
--> Szenario: 'standard'
--> Aktion: 'prepare'
Überspringen, Vorbereitungs-Playbook nicht konfiguriert.
--> Szenario: 'standard'
--> Aktion: 'converge'
SPIEL [Konvergenz] ****************************************************************
AUFGABE [Fakten sammeln] *********************************************************
ok: [instanz]
AUFGABE [nginx : nginx installieren] ***************************************************
geändert: [instanz]
AUFGABE [nginx : nginx starten] *****************************************************
geändert: [instanz]
SPIEL-RÜCKBLICK *********************************************************************
instanz : ok=3 geändert=2 unerreichbar=0 fehlgeschlagen=0
--> Szenario: 'standard'
--> Aktion: 'idempotence'
Idempotenz erfolgreich abgeschlossen.
--> Szenario: 'standard'
--> Aktion: 'side_effect'
Überspringen, Side-Effect-Playbook nicht konfiguriert.
--> Szenario: 'standard'
--> Aktion: 'verify'
--> Ausführung von Testinfra-Tests in /nginx/molecule/default/tests/...
============================= Test-Session beginnt ==============================
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
4 Elemente gesammelt
tests/test_default.py .... [100%]
========================== 4 bestanden in 27.23 Sekunden ===========================
Verifikator erfolgreich abgeschlossen.
--> Szenario: 'standard'
--> Aktion: 'destroy'
SPIEL [Zerstören] *****************************************************************
AUFGABE [Molecule-Instanz(en) zerstören] ********************************************
geändert: [localhost] => (item=None)
geändert: [localhost]
AUFGABE [Warten auf Abschluss der Instanzlöschung] *******************************
geändert: [localhost] => (item=None)
geändert: [localhost]
AUFGABE [Docker-Netzwerk(e) löschen] ************************************************
SPIEL-RÜCKBLICK *********************************************************************
localhost : ok=2 geändert=2 unerreichbar=0 fehlgeschlagen=0
Unsere einfache Rolle wurde ohne Probleme getestet.
Es ist wichtig zu beachten, dass, wenn Probleme während der Ausführung auftreten, molecule test, wenn Sie die Standardreihenfolge nicht geändert haben, Molecule die Instanz löschen wird.
Für das Debugging sind die folgenden Befehle nützlich:
> molecule --debug # Debug-Informationen. Bei normalem Start verbirgt Molecule die Protokolle.
> molecule converge # Lässt die Instanz nach dem Durchlauf der getesteten Rolle bestehen.
> molecule login # In die erstellte Instanz einloggen.
> molecule --help # Vollständige Befehlsliste.Bestehende 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 können, ist Molecule nicht sehr komplex, und durch die Verwendung eigener Vorlagen kann die Bereitstellung eines neuen Szenarios auf die Bearbeitung von Variablen in den Playbooks zur Erstellung und Löschung von Instanzen reduziert werden. Molecule lässt sich problemlos in CI-Systeme integrieren, was die Entwicklungsgeschwindigkeit durch die Reduzierung der Zeit für manuelles Testen der Playbooks erhöht.
Vielen Dank für Ihre Aufmerksamkeit. Wenn Sie Erfahrung mit dem Testen von Ansible-Rollen haben, die nicht mit Molecule zusammenhängen – teilen Sie Ihre Erfahrungen in den Kommentaren!
Quelle: habr.com
