Wenn ein Task eines Ansible Playbooks Probleme macht oder es zu einem unerwarteten Fehlverhalten kommt ist es oft nicht ganz nachvollziehbar was da passiert ist, bzw. ob z.B. auch die entsprechenden Variablen richtig gesetzt wurden.
Nachfolgend ein paar Schritte, welche bei der Fehlersuche helfen können…
Verbosity
Um zusätzliche Informationen während eines Playbook-Laufes angezeigt zu bekommen erhöht man einfach die verbosity.
Dies geschieht mit der dafür üblichen Option -v
. Hierbei gibt die Anzahl der v
’s das Level der Ausführlichkeit an. -vvvv
ist die Stufe mit den ausführlichsten Informationen.
Auszug aus ansible --help
:
-v, --verbose
verbose mode (-vvv for more, -vvvv to enable connection debugging)
Anwendung
Man kann beim Aufruf des Playbooks im Terminal die Option angeben:
ansible-playbook -vvvv -i hosts playbook.yml
oder man kann auch mit Umgebungsvariablen arbeiten:
ANSIBLE_VERBOSITY=4 ansible-playbook -i hosts playbook.yml
Nun wird aus der regulären Ausgabe:
PLAY [127.0.0.1] ***********************************************************************************
TASK [Hello World!] ********************************************************************************
ok: [localhost] => {
"msg": "Hello world!"
}
PLAY RECAP *****************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0
folgende:
ansible-playbook 2.7.8
config file = None
configured module search path = [u'/home/rasputin/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /home/rasputin/Ansible/ansible_2.7.8/lib/python2.7/site-packages/ansible
executable location = /home/rasputin/Ansible/ansible_2.7.8/bin/ansible-playbook
python version = 2.7.15 (default, Oct 15 2018, 15:26:09) [GCC 8.2.1 20180801 (Red Hat 8.2.1-2)]
No config file found; using defaults
setting up inventory plugins
/home/rasputin/github/ansible_helloworld/hosts did not meet host_list requirements, check plugin documentation if this is unexpected
/home/rasputin/github/ansible_helloworld/hosts did not meet script requirements, check plugin documentation if this is unexpected
Set default localhost to localhost
Parsed /home/rasputin/github/ansible_helloworld/hosts inventory source with ini plugin
Loading callback plugin default of type stdout, v2.0 from /home/rasputin/Ansible/ansible_2.7.8/lib/python2.7/site-packages/ansible/plugins/callback/default.pyc
PLAYBOOK: playbook.yml *****************************************************************************
1 plays in playbook.yml
PLAY [127.0.0.1] ***********************************************************************************
META: ran handlers
TASK [Hello World!] ********************************************************************************
task path: /home/rasputin/github/ansible_helloworld/playbook.yml:6
ok: [localhost] => {
"msg": "Hello world!"
}
META: ran handlers
META: ran handlers
PLAY RECAP *****************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0
Debug Strategy
Strategies regeln in Ansible die Ausführung der Tasks.
Der Standardwert ist hierbei linear
, d.h. es wird je immer ein Task auf allen entsprechenden Hosts durchgeführt und wenn dieser auf allen Systemen abgeschloßen ist geht es zum nächsten Task.
Eine weitere strategy die man hier verwenden kann lautet debug
.
Hierbei erscheint bei einem fehlgeschlagenen Task eine Debug-Konsole, in der man prüfen kann welche Variablen gerade mit welchen Werten gesetzt sind und man kann diese dann ggf. entsprechend ändern.
Dazu gibt man entweder auf der Play
-Ebene des Playbooks den String: strategy: debug
an:
---
- hosts: all
strategy: debug
...
oder arbeitet wieder mit Umgebungsvariablen:
export ANSIBLE_STRATEGY=debug
Nachfolgend mal ein Beispiel, bei dem die Pfadangabe für eine Datei einen Tippfehler enthält und deshalb nicht erstellt werden kann.
$ ansible-playbook -i hosts playbook.yml
PLAY [all] ***********************************************************************************************************
TASK [Write my file] *************************************************************************************************
fatal: [testserver01]: FAILED! => {"changed": false, "checksum": "0ded82d0b36ca79392d9bd97debb6fbbb7bb93ef", "msg": "Destination directory /tmpp does not exist"}
[testserver01] TASK: Write my file (debug)> task.args
{u'dest': u'/tmpp/testfile', u'content': u'Hello World!\nThis is my file.\n'}
[testserver01] TASK: Write my file (debug)> task.args['dest']='/tmp/testfile'
[testserver01] TASK: Write my file (debug)> task.args
{u'dest': '/tmp/testfile', u'content': u'Hello World!\nThis is my file.\n'}
[testserver01] TASK: Write my file (debug)> redo
changed: [testserver01]
PLAY RECAP ***********************************************************************************************************
testserver01 : ok=1 changed=1 unreachable=0 failed=0
Einzeln erklärt:
fatal: [testserver01]: FAILED! => {"changed": false, "checksum": "0ded82d0b36ca79392d9bd97debb6fbbb7bb93ef", "msg": "Destination directory /tmpp does not exist"}
—> Die Fehlermeldung, welche meldet das es das entsprechende Verzeichnis nicht gibt.
[testserver01] TASK: Write my file (debug)> task.args
{u'dest': u'/tmpp/testfile', u'content': u'Hello World!\nThis is my file.\n'}
—> Es öffnet sich diedebug
-Konsole und mit dem Befehltask.args
kann man sich die an dieser Stelle verwendeten Variablen anzeigen lassen.
Alternativ:(debug)> p task.args
—> Hier sieht man auch, dass ein falscher Wert für dendest
Parameter gesetzt ist.
[testserver01] TASK: Write my file (debug)> task.args['dest']='/tmp/testfile'
—> Der entsprechende Eintrag im Array der Variablen wird neu gesetzt.
[testserver01] TASK: Write my file (debug)> task.args
{u'dest': '/tmp/testfile', u'content': u'Hello World!\nThis is my file.\n'}
—> Die erneute Prüfung der variablen lässt erkennen, dass nun der richtige Pfad gesetzt ist
[testserver01] TASK: Write my file (debug)> redo
—> Der letzte (fehlgeschlagene) Task wird mit den neuen Werten wiederholt.
Mithilfe des Debuggers lassen sich aber nicht nur bestehende Werte ändern, sondern auch neue setzen oder bestehende löschen:
(debug) task.args['owner']='myuser'
(debug) task.args
{u'dest': '/tmp/my/myfile', u'content': u'Hello World!\nThis is my file.\n', 'owner': 'myuser'}
(debug) del(task.args['owner'])
(debug) task.args
{u'dest': '/tmp/my/myfile', u'content': u'Hello World!\nThis is my file.\n'}
Die vom debugger verstandenen Befehle:
Documented commands (type help <topic>):
========================================
EOF c continue h help p pprint q quit r redo
Das debugger Keyword
Das Debugger Keyword kann in jedem Block verwendet werden, welches ein name
Attribut bereitstellt, z.B.: eine Rolle, ein Task, etc..
Somit kann es zielgerichteter eingesetzt werden und es muss nicht die gesamte strategy geändert werden.
debugger
Keyword akzeptiert folgende Werte:
- always – der debugger wird immer mit ausgeführt, egal was das Ergebnis ist
- never – der debugger wird niemals ausgeführt
- on_failed der debugger wird nur aufgerufen wenn ein Task fehlschlägt
- on_unreachable – der debugger wird aufgerufen wenn ein Host nicht erreichbar ist
- on_skipped – der debugger wird immer dann aufgerufen wenn ein Task übersprungen wird
In einem Task:
- name: Execute a command
command: false
debugger: on_failed
In einem Play:
- name: Play
hosts: all
debugger: never
tasks:
- name: Execute a command
command: false
debugger: on_failed
Hierbei sieht man gut, dass der Wert des debugger
in einem Task den globalen Wert aus dem Play überschreibt.
Auch hierbei kann eine entsprechende Umgebungsvariable vor den Aufruf des Playbooks gesetzt werden:
ANSIBLE_ENABLE_TASK_DEBUGGER=True ansible-playbook -i hosts playbook.yml
Das debug
Modul
Ansible hat die Möglichkeit bestimmte Ausgaben von Befehlen in eigene Variablen schreiben zu lassen. Dies geschieht mit dem Keyword register
.
Um sich diese und auch andere Variablen ausgeben zu lassen kann man das debug
Modul verwenden.
Hierbei kann man die Fülle der Ausgabe zusätzlich auch mit verbosity
regeln:
Beispiel:
- name: Write file
copy:
dest: /tmp/my
content: |
Hello World!
This is my file.
register: write_file_result
- name: Debug write_file_result
debug:
var: write_file_result
verbosity: 2
Die entsprechende Ausgabe:
TASK [Debug write_file_result] **********************************************
ok: [localhost] => {
"write_file_result": {
"changed": true,
"checksum": "0ded82d0b36ca79392d9bd97debb6fbbb7bb93ef",
"dest": "/tmp/my/myfile",
"gid": 20,
"group": "staff",
"md5sum": "f52e35e56cd4087de0948890215f7de0",
"mode": "0644",
"owner": "myuser",
"size": 30,
"src": "/Users/myuser/.ansible/tmp/ansible-tmp-1498165508.64-230947914847899/source",
"state": "file",
"uid": 501
}
}