Hallo allerseits!
Ich arbeite als DevOps-Ingenieur im Hotelreservierungsservice Ostrovok.ru . In diesem Artikel möchte ich ĂŒber unsere Erfahrungen beim Testen ansibler Rollen sprechen.
Bei Ostrovok.ru verwenden wir ansible als Konfigurationsmanager. Wir hatten kĂŒrzlich die Notwendigkeit, Rollen zu testen, aber wie sich herausstellte, gibt es dafĂŒr nicht viele Tools - das Molecule-Framework ist vielleicht das beliebteste, deshalb haben wir uns fĂŒr die Verwendung entschieden. Es stellte sich jedoch heraus, dass seine Dokumentation ĂŒber viele Fallstricke schweigte. Wir konnten keinen ausreichend detaillierten Leitfaden auf Russisch finden und haben uns daher entschlossen, diesen Artikel zu schreiben.

MolekĂŒl
MolekĂŒl - ein Framework zum Testen ansibler Rollen.
Vereinfachte Beschreibung: Das MolekĂŒl erstellt eine Instanz auf der von Ihnen angegebenen Plattform (Cloud, virtuelle Maschine, Container; weitere Einzelheiten finden Sie im Abschnitt Treiber ), fĂŒhrt Ihre Rolle darauf aus, fĂŒhrt dann die Tests aus und löscht die Instanz. Im Falle eines Fehlers bei einem der Schritte informiert Sie das MolekĂŒl darĂŒber.
Jetzt genauer.
Ein bisschen Theorie
Betrachten Sie die beiden SchlĂŒsselelemente des MolekĂŒls: Szenario und Treiber.
Szenario
Das Skript enthĂ€lt eine Beschreibung dessen, was, wo, wie und in welcher Reihenfolge ausgefĂŒhrt wird. Eine Rolle kann mehrere Skripte enthalten, und jedes ist ein Verzeichnis entlang des Pfads <role>/molecule/<scenario>
, das Beschreibungen der fĂŒr den Test erforderlichen Aktionen enthĂ€lt. Das default
muss vorhanden sein, das automatisch erstellt wird, wenn Sie die Rolle mit dem MolekĂŒl initialisieren. Die Namen der folgenden Szenarien werden nach Ihrem Ermessen ausgewĂ€hlt.
Die Testsequenz im Skript wird als Matrix bezeichnet und ist standardmĂ€Ăig wie folgt:
(Schritte markiert mit ?
standardmĂ€Ăig ĂŒbersprungen, wenn sie nicht vom Benutzer beschrieben wurden.)
lint
Linter. StandardmĂ€Ăig werden yamllint
und flake8
.destroy
- Instanzen vom letzten MolekĂŒlstart entfernen (falls ĂŒbrig),dependency
? - Installation der Ansible-AbhÀngigkeit der getesteten Rolle,syntax
- ĂberprĂŒfen Sie die Syntax einer Rolle mit ansible-playbook --syntax-check
,create
- Instanz erstellen,prepare
? - Vorbereitung der Instanz; B. Python2 ĂŒberprĂŒfen / installierenconverge
- Start des getesteten Playbooks,idempotence
- Starten Sie das Playbook fĂŒr den Idempotenztest neu.side_effect
? - MaĂnahmen, die nicht direkt mit der Rolle zusammenhĂ€ngen, aber fĂŒr Tests erforderlich sind,verify
- FĂŒhren Sie Tests der resultierenden Konfiguration mit testinfra
(Standard) / goss
/ inspec
.cleanup
? - (in neuen Versionen) - grob gesagt die âReinigungâ der vom MolekĂŒl betroffenen externen Infrastruktur,destroy
- lösche eine Instanz.
Diese Sequenz deckt die meisten FÀlle ab, kann jedoch bei Bedarf geÀndert werden.
Jeder der obigen Schritte kann separat mit dem molecule <command>
. Es lohnt sich jedoch zu verstehen, dass fĂŒr jeden solchen cli-Befehl eine eigene Abfolge von Aktionen existieren kann, die durch AusfĂŒhren der molecule matrix <command>
erkannt werden kann. Wenn Sie beispielsweise den Befehl converge
ausfĂŒhren (das getestete Playbook ausfĂŒhren), werden die folgenden Aktionen ausgefĂŒhrt:
$ molecule matrix converge ... âââ default
Die Reihenfolge dieser Aktionen kann bearbeitet werden. Wenn etwas aus der Liste bereits abgeschlossen ist, wird es ĂŒbersprungen. Der aktuelle Status sowie die $TMPDIR/molecule/<role>/<scenario>
werden im $TMPDIR/molecule/<role>/<scenario>
gespeichert.
Schritte hinzufĂŒgen mit ?
Nachdem Sie die gewĂŒnschten Aktionen im Format von Ansible-Playbook beschrieben haben, können Sie den Dateinamen gemÀà dem folgenden Schritt prepare.yml
: prepare.yml
/ side_effect.yml
. Das MolekĂŒl wartet im Skriptordner auf diese Dateien.
Fahrer
Ein Treiber ist eine EntitÀt, in der Testinstanzen erstellt werden.
Die Liste der Standardtreiber, fĂŒr die das MolekĂŒl Vorlagen bereit hĂ€lt, lautet wie folgt: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegiert.
In den meisten FĂ€llen handelt es sich bei den Vorlagen um die destroy.yml
create.yml
und destroy.yml
im destroy.yml
, die das Erstellen bzw. Löschen der Instanz beschreiben.
Ausnahmen sind Docker und Vagrant, da Interaktionen mit ihren Modulen ohne die oben genannten Dateien auftreten können.
Es lohnt sich, den delegierten Treiber hervorzuheben, da, wenn er in den Dateien zum Erstellen und Löschen einer Instanz verwendet wird, nur die Arbeit mit der Konfiguration von Instanzen beschrieben wird, der Rest vom Techniker beschrieben werden sollte.
Der Standardtreiber ist Docker.
Nun wenden wir uns der Praxis zu und betrachten dort weitere Funktionen.
Erste Schritte
Als "Hallo Welt" testen wir die einfache Rolle der Installation von Nginx. Wir wÀhlen Docker als Treiber - ich denke, es ist auf den meisten von Ihnen installiert (und denken Sie daran, dass Docker der Standardtreiber ist).
virtualenv
Sie virtualenv
und installieren Sie das molecule
darin:
> pip install virtualenv > virtualenv -p `which python2` venv > source venv/bin/activate > pip install molecule docker
Der nÀchste Schritt besteht darin, eine neue Rolle zu initialisieren.
Die Initialisierung einer neuen Rolle sowie eines neuen Szenarios erfolgt mit dem molecule init <params>
:
> molecule init role -r nginx --> Initializing new role nginx... Initialized role in <path>/nginx successfully. > cd nginx > tree -L 1 . âââ README.md âââ defaults âââ handlers âââ meta âââ molecule âââ tasks âââ vars 6 directories, 1 file
Das Ergebnis war eine typische ansible Rolle. Ferner werden alle Wechselwirkungen mit den CLI-MolekĂŒlen von der Wurzel der Rolle aus hergestellt.
Mal sehen, was sich im Rollenverzeichnis befindet:
> tree molecule/default/ molecule/default/ âââ Dockerfile.j2
Lassen Sie uns die molecule/default/molecule.yml
Konfiguration analysieren (wir ersetzen nur das Docker-Bild):
--- 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
AbhÀngigkeit
Dieser Abschnitt beschreibt die Quelle der AbhÀngigkeiten.
Mögliche Optionen: Galaxie , vergoldet , Muschel.
Shell ist nur eine Befehls-Shell, die verwendet wird, wenn Galaxie und Gold Ihre Anforderungen nicht erfĂŒllen.
Ich werde hier nicht lange aufhören, es ist in der Dokumentation ausreichend beschrieben.
Fahrer
Der Name des Fahrers. Wir haben diesen Docker.
Flusen
Als Linter wird Yamllint verwendet.
NĂŒtzliche Optionen in diesem Teil der Konfiguration sind die Möglichkeit, eine Konfigurationsdatei fĂŒr yamllint anzugeben, Umgebungsvariablen weiterzuleiten oder den Linter zu deaktivieren:
lint: name: yamllint options: config-file: foo/bar env: FOO: bar enabled: False
Beschreibt die Konfiguration von Instanzen.
Im Fall des Dockers als Treiber wird das MolekĂŒl ĂŒber diesen Abschnitt iteriert, und jedes Listenelement ist in Dockerfile.j2
als Dockerfile.j2
verfĂŒgbar.
Im Fall des Treibers, in dem create.yml
und destroy.yml
, ist der Abschnitt create.yml
als destroy.yml
verfĂŒgbar, und die Iterationen darauf sind bereits in diesen Dateien beschrieben.
Da das MolekĂŒl eine Instanzsteuerung fĂŒr ansible Module bietet, muss dort eine Liste möglicher Einstellungen gesucht werden. FĂŒr Docker wird beispielsweise das Modul docker_container_module verwendet . Welche Module in anderen Treibern verwendet werden, finden Sie in der Dokumentation .
Beispiele fĂŒr die Verwendung verschiedener Treiber finden sich in Tests des MolekĂŒls selbst .
Ersetzen Sie Centos: 7 auf Ubuntu hier .
Provisioner
"Provider" ist die EntitĂ€t, die die Instanzen steuert. Im Fall des MolekĂŒls ist dies ansible, UnterstĂŒtzung fĂŒr andere ist nicht geplant, so dass dieser Abschnitt mit Vorbehalt die erweiterte Konfiguration von ansible genannt werden kann.
Hier können Sie viele Dinge spezifizieren, ich werde die wichtigsten Momente meiner Meinung nach hervorheben:
- Playbooks : Sie können festlegen, welche Playbooks 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
- Verbindungsoptionen : Verbindungsparameter
provisioner: name: ansible connection_options: ansible_ssh_common_args: "-o 'UserKnownHostsFile=/dev/null' -o 'ForwardAgent=yes'"
- Optionen : Ansible Optionen und Umgebungsvariablen
provisioner: name: ansible options: vvv: true diff: true env: FOO: BAR
Szenario
Der Name und die Beschreibung der Skriptsequenzen.
Sie können die Standardaktionsmatrix eines <command>_sequence
Ă€ndern, indem Sie den <command>_sequence
und die Liste der Schritte festlegen, die wir als Wert dafĂŒr benötigen.
Angenommen, wir möchten die Reihenfolge der Aktionen Ă€ndern, wenn wir den Befehl playbook run ausfĂŒhren: molecule converge
# : # - dependency # - create # - prepare # - converge scenario: name: default converge_sequence: - create - converge
PrĂŒfer
Einrichten des Frameworks fĂŒr Tests und des Linter dazu. StandardmĂ€Ăig werden testinfra
und flake8
testinfra
flake8
. Mögliche Optionen sind Àhnlich wie oben:
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
Kommen wir zurĂŒck zu unserer Rolle. Bearbeiten Sie die tasks/main.yml
:
--- - name: Install nginx apt: name: nginx state: present - name: Start nginx service: name: nginx state: started
Und fĂŒgen Sie die Tests zu 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 zu laufen (von der Wurzel der Rolle, erinnere ich Sie):
> molecule test
Langer Auspuff unter dem Spoiler: --> Validating schema <path>/nginx/molecule/default/molecule.yml. Validation completed successfully. --> Test matrix âââ default âââ lint âââ destroy âââ dependency âââ syntax âââ create âââ prepare âââ converge âââ idempotence âââ side_effect âââ verify âââ destroy --> Scenario: 'default' --> Action: 'lint' --> Executing Yamllint on files found in <path>/nginx/... Lint completed successfully. --> Executing Flake8 on files found in <path>/nginx/molecule/default/tests/... Lint completed successfully. --> Executing Ansible Lint on <path>/nginx/molecule/default/playbook.yml... Lint completed successfully. --> Scenario: 'default' --> Action: 'destroy' PLAY [Destroy] ***************************************************************** TASK [Destroy molecule instance(s)] ******************************************** changed: [localhost] => (item=None) changed: [localhost] TASK [Wait for instance(s) deletion to complete] ******************************* ok: [localhost] => (item=None) ok: [localhost] TASK [Delete docker network(s)] ************************************************ PLAY RECAP ********************************************************************* localhost : ok=2 changed=1 unreachable=0 failed=0 --> Scenario: 'default' --> Action: 'dependency' Skipping, missing the requirements file. --> Scenario: 'default' --> Action: 'syntax' playbook: <path>/nginx/molecule/default/playbook.yml --> Scenario: 'default' --> Action: 'create' PLAY [Create] ****************************************************************** TASK [Log into a Docker registry] ********************************************** skipping: [localhost] => (item=None) TASK [Create Dockerfiles from image names] ************************************* changed: [localhost] => (item=None) changed: [localhost] TASK [Discover local Docker images] ******************************************** ok: [localhost] => (item=None) ok: [localhost] TASK [Build an Ansible compatible image] *************************************** changed: [localhost] => (item=None) changed: [localhost] TASK [Create docker network(s)] ************************************************ TASK [Create molecule instance(s)] ********************************************* changed: [localhost] => (item=None) changed: [localhost] TASK [Wait for instance(s) creation to complete] ******************************* changed: [localhost] => (item=None) changed: [localhost] PLAY RECAP ********************************************************************* localhost : ok=5 changed=4 unreachable=0 failed=0 --> Scenario: 'default' --> Action: 'prepare' Skipping, prepare playbook not configured. --> Scenario: 'default' --> Action: 'converge' PLAY [Converge] **************************************************************** TASK [Gathering Facts] ********************************************************* ok: [instance] TASK [nginx : Install nginx] *************************************************** changed: [instance] TASK [nginx : Start nginx] ***************************************************** changed: [instance] PLAY RECAP ********************************************************************* instance : ok=3 changed=2 unreachable=0 failed=0 --> Scenario: 'default' --> Action: 'idempotence' Idempotence completed successfully. --> Scenario: 'default' --> Action: 'side_effect' Skipping, side effect playbook not configured. --> Scenario: 'default' --> Action: 'verify' --> Executing Testinfra tests found in <path>/nginx/molecule/default/tests/... ============================= test session starts ============================== platform 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 collected 4 items tests/test_default.py .... [100%] ========================== 4 passed in 27.23 seconds =========================== Verifier completed successfully. --> Scenario: 'default' --> Action: 'destroy' PLAY [Destroy] ***************************************************************** TASK [Destroy molecule instance(s)] ******************************************** changed: [localhost] => (item=None) changed: [localhost] TASK [Wait for instance(s) deletion to complete] ******************************* changed: [localhost] => (item=None) changed: [localhost] TASK [Delete docker network(s)] ************************************************ PLAY RECAP ********************************************************************* localhost : ok=2 changed=2 unreachable=0 failed=0
Unsere einfache Rolle wurde ohne Probleme getestet.
Es ist zu beachten, dass das molecule test
die Instanz löscht, wenn wÀhrend des Betriebs des molecule test
Probleme molecule test
sind. Wenn Sie die Standardsequenz nicht geÀndert haben.
Die folgenden Befehle sind zum Debuggen hilfreich:
> molecule --debug <command>
Bestehende Rolle
Das HinzufĂŒgen eines neuen Skripts zu einer vorhandenen Rolle erfolgt aus dem Rollenverzeichnis mit den folgenden Befehlen:
Falls dies das erste Skript in der Rolle ist, kann die -s
weggelassen werden, da das default
erstellt wird.
Fazit
Wie Sie sehen, ist das MolekĂŒl nicht sehr kompliziert. Wenn Sie Ihre eigenen Vorlagen verwenden, können Sie die Bereitstellung eines neuen Skripts auf das Bearbeiten von Variablen in Playbooks zum Erstellen und Löschen von Instanzen reduzieren. Das MolekĂŒl lĂ€sst sich nahtlos in CI-Systeme integrieren, wodurch Sie die Entwicklungsgeschwindigkeit erhöhen können, indem Sie weniger Zeit fĂŒr das manuelle Testen von Playbooks benötigen.
Vielen Dank fĂŒr Ihre Aufmerksamkeit. Wenn Sie Erfahrung mit dem Testen ansibler Rollen haben und diese nicht mit dem MolekĂŒl zusammenhĂ€ngen, teilen Sie uns dies in den Kommentaren mit!