Anleitung: So testen Sie Ansible-Rollen und erkennen Probleme vor der Produktionsumgebung.

Hallo zusammen!

Ich arbeite als DevOps-Ingenieur bei einem Hotelbuchungsdienst. Ostrovok.ru. 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.

Anleitung: So testen Sie Ansible-Rollen und erkennen Probleme vor der Produktionsumgebung.

Molecule

Molekül – 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 Driver), 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: yamllint und flake8,
  • destroy — Löschung von Instanzen aus dem vorherigen Molecule-Lauf (sofern vorhanden),
  • dependency? — установка ansible-зависимости тестируемой роли,
  • syntax — Überprüfung der Syntax der Rolle mit ansible-playbook --syntax-check,
  • create — Erstellung einer Instanz,
  • prepare? — подготовка инстанса; например, проверка / установка python2
  • converge — 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 mit testinfra(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ühren

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

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

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

Lass 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: flake8

dependency

Dieser Abschnitt beschreibt die Quelle der Abhängigkeiten.

Mögliche Optionen: galaxy, gilt, 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 Dokumentation..

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

platforms

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 docker_container_module. Welche Module in den anderen Treibern verwendet werden, kann in Dokumentation..

Gefunden werden, sowie Beispiele für die Verwendung verschiedener Treiber in den Tests von Molecule selbst..

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

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

  • Optionen: Ansible-Parameter und Umgebungsvariablen

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

Szenario

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

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

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

Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz, VPS VDS-Server 🔥 Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz, VPS VDS-Server | ProHoster