Ausgabe von Shell-Befehlen in Ansible Playbooks anzeigen

AnsibleAnsibleIntermediate
Jetzt üben

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

Ansible ist ein leistungsstarkes Open-Source-Automatisierungstool, das von Systemadministratoren und DevOps-Experten weit verbreitet ist. Eine seiner wichtigsten Fähigkeiten ist das Ausführen von Shell-Befehlen auf Remote-Hosts und die Verarbeitung ihrer Ausgabe. In diesem praktischen Tutorial erfahren Sie, wie Sie die Ausgabe von Shell-Befehlen in Ansible-Playbooks effektiv erfassen, anzeigen und verarbeiten können. Diese Fähigkeit ist unerlässlich für die Erstellung robuster Automatisierungs-Workflows, die sich an unterschiedliche Systembedingungen anpassen und nützliches Feedback liefern können.

Dies ist ein Guided Lab, das schrittweise Anweisungen bietet, um Ihnen beim Lernen und Üben zu helfen. Befolgen Sie die Anweisungen sorgfältig, um jeden Schritt abzuschließen und praktische Erfahrungen zu sammeln. Historische Daten zeigen, dass dies ein Labor der Stufe Fortgeschrittener mit einer Abschlussquote von 53% ist. Es hat eine positive Bewertungsrate von 100% von den Lernenden erhalten.

Einrichten Ihres ersten Ansible-Playbooks mit Shell-Befehlen

In diesem Schritt richten wir ein einfaches Ansible-Playbook ein, das Shell-Befehle ausführt und deren Ausgabe erfasst. Dies bildet die Grundlage für fortgeschrittenere Techniken in späteren Schritten.

Installieren von Ansible

Zuerst installieren wir Ansible auf unserem System:

sudo apt update
sudo apt install -y ansible

Überprüfen Sie nun, ob Ansible korrekt installiert wurde:

ansible --version

Sie sollten eine ähnliche Ausgabe wie diese sehen:

ansible [core 2.12.x]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/labex/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /home/labex/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.10.x (default, Mar 15 2022, 12:22:08) [GCC 11.2.0]
  jinja version = 3.0.3
  libyaml = True

Erstellen einer Inventory-Datei

Ansible verwendet eine Inventory-Datei, um zu wissen, welche Hosts verwaltet werden sollen. Für dieses Lab erstellen wir ein einfaches Inventory, das nur die lokale Maschine enthält:

  1. Öffnen Sie Ihre WebIDE und erstellen Sie eine neue Datei namens inventory.ini im Verzeichnis /home/labex/project.
  2. Fügen Sie der Datei den folgenden Inhalt hinzu:
[local]
localhost ansible_connection=local

Dieses Inventory definiert eine Gruppe namens local, die nur den localhost enthält, und weist Ansible an, sich direkt ohne SSH zu verbinden.

Erstellen Ihres ersten Playbooks

Erstellen wir nun ein einfaches Playbook, das Shell-Befehle ausführt:

  1. Erstellen Sie eine neue Datei namens first_playbook.yml im Verzeichnis /home/labex/project.
  2. Fügen Sie der Datei den folgenden YAML-Inhalt hinzu:
---
- name: Shell Command Example
  hosts: local
  gather_facts: no

  tasks:
    - name: Run a simple shell command
      shell: echo "Hello from Ansible shell command"
      register: hello_output

    - name: Display the output
      debug:
        msg: "{{ hello_output.stdout }}"

Dieses Playbook macht Folgendes:

  • Zielt auf die Gruppe local ab, die wir in unserem Inventory definiert haben
  • Überspringt das Sammeln von Fakten (Systeminformationen), um die Dinge einfach zu halten
  • Führt einen Shell-Befehl aus, der eine Grußnachricht ausgibt
  • Speichert die Ausgabe in einer Variable mithilfe des Schlüsselworts register
  • Zeigt die Ausgabe mithilfe des debug-Moduls an

Ausführen Ihres Playbooks

Führen wir nun das Playbook aus:

ansible-playbook -i inventory.ini first_playbook.yml

Sie sollten eine ähnliche Ausgabe wie diese sehen:

PLAY [Shell Command Example] **************************************************

TASK [Run a simple shell command] *********************************************
changed: [localhost]

TASK [Display the output] *****************************************************
ok: [localhost] => {
    "msg": "Hello from Ansible shell command"
}

PLAY RECAP ********************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Die Ausgabe zeigt, dass unser Playbook erfolgreich ausgeführt wurde, den Shell-Befehl ausführte und dessen Ausgabe anzeigte.

Wichtige Konzepte zum Verständnis

Beachten Sie aus dieser Übung diese wichtigen Konzepte:

  1. Das shell-Modul ermöglicht die Ausführung von Shell-Befehlen
  2. Die register-Direktive erfasst die Ausgabe einer Aufgabe in einer Variable
  3. Das debug-Modul hilft beim Anzeigen von Variablenwerten
  4. Die Ausgabe von Shell-Befehlen enthält sowohl stdout (Standardausgabe) als auch stderr (Fehlerausgabe)

Im nächsten Schritt werden wir untersuchen, wie man die Ausgabe von Shell-Befehlen effektiver verarbeitet und formatiert.

Arbeiten mit strukturierter Shell-Befehlsausgabe

Nachdem Sie die Grundlagen der Ausführung von Shell-Befehlen in Ansible verstanden haben, wollen wir uns damit befassen, wie man mit strukturierter Befehlsausgabe arbeitet und diese in verschiedenen Formaten anzeigt.

Verarbeiten von Systeminformationen

Erstellen wir ein praktischeres Playbook, das Systeminformationen sammelt und strukturiert darstellt:

  1. Erstellen Sie eine neue Datei namens system_info.yml im Verzeichnis /home/labex/project.
  2. Fügen Sie den folgenden Inhalt hinzu:
---
- name: Gather and Display System Information
  hosts: local
  gather_facts: no

  tasks:
    - name: Gather system information
      shell: |
        echo "OS Information: $(cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2)"
        echo "Kernel Version: $(uname -r)"
        echo "CPU Information: $(grep "model name" /proc/cpuinfo | head -1 | cut -d: -f2 | xargs)"
        echo "Memory Information: $(free -h | grep Mem | awk '{print $2}')"
      register: system_info

    - name: Display raw system information
      debug:
        msg: "{{ system_info.stdout }}"

    - name: Display information as a list
      debug:
        msg: "{{ system_info.stdout_lines }}"

Dieses Playbook:

  • Führt ein mehrzeiliges Shell-Skript aus, das verschiedene Systemdetails sammelt
  • Speichert die Ausgabe in der Variable system_info
  • Zeigt die Ausgabe zuerst als rohe Zeichenkette und dann als Liste von Zeilen an

Führen Sie das Playbook aus:

ansible-playbook -i inventory.ini system_info.yml

Sie sollten eine Ausgabe sehen, die so aussieht:

PLAY [Gather and Display System Information] **********************************

TASK [Gather system information] **********************************************
changed: [localhost]

TASK [Display raw system information] *****************************************
ok: [localhost] => {
    "msg": "OS Information: \"Ubuntu 22.04.1 LTS\"\nKernel Version: 5.15.0-1023-azure\nCPU Information: Intel(R) Xeon(R) Platinum 8171M CPU @ 2.60GHz\nMemory Information: 4.0Gi"
}

TASK [Display information as a list] *****************************************
ok: [localhost] => {
    "msg": [
        "OS Information: \"Ubuntu 22.04.1 LTS\"",
        "Kernel Version: 5.15.0-1023-azure",
        "CPU Information: Intel(R) Xeon(R) Platinum 8171M CPU @ 2.60GHz",
        "Memory Information: 4.0Gi"
    ]
}

PLAY RECAP ********************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Beachten Sie, wie die zweite Anzeigaufgabe die Ausgabe als Liste anstelle einer Zeichenkette mit Zeilenumbrüchen anzeigt. Dieses Listenformat erleichtert die Arbeit mit mehrzeiliger Ausgabe.

Arbeiten mit Befehlsergebnissen und Rückgabecodes

Shell-Befehle in Linux geben Exit-Codes zurück, um Erfolg (0) oder Fehler (ungleich 0) anzuzeigen. Ansible erfasst diese im Attribut rc (Return Code) der registrierten Variable.

Erstellen wir ein Playbook, das zeigt, wie man mit Rückgabecodes arbeitet:

  1. Erstellen Sie eine neue Datei namens command_results.yml im Verzeichnis /home/labex/project.
  2. Fügen Sie den folgenden Inhalt hinzu:
---
- name: Working with Command Results
  hosts: local
  gather_facts: no

  tasks:
    - name: Check if a file exists
      shell: test -f /etc/hosts
      register: file_check
      ignore_errors: yes

    - name: Show command result details
      debug:
        msg: |
          Return code: {{ file_check.rc }}
          Succeeded: {{ file_check.rc == 0 }}
          Failed: {{ file_check.rc != 0 }}

    - name: Check if a non-existent file exists
      shell: test -f /file/does/not/exist
      register: missing_file
      ignore_errors: yes

    - name: Show command result for missing file
      debug:
        msg: |
          Return code: {{ missing_file.rc }}
          Succeeded: {{ missing_file.rc == 0 }}
          Failed: {{ missing_file.rc != 0 }}

Dieses Playbook:

  • Führt zwei Testbefehle aus, um zu überprüfen, ob Dateien existieren
  • Verwendet ignore_errors: yes, um zu verhindern, dass das Playbook anhält, wenn ein Befehl fehlschlägt
  • Zeigt detaillierte Informationen zum Befehlsergebnis an, einschließlich des Rückgabecodes und des Erfolgs-/Fehlerstatus

Führen Sie das Playbook aus:

ansible-playbook -i inventory.ini command_results.yml

Sie sollten eine ähnliche Ausgabe wie diese sehen:

PLAY [Working with Command Results] *******************************************

TASK [Check if a file exists] *************************************************
changed: [localhost]

TASK [Show command result details] ********************************************
ok: [localhost] => {
    "msg": "Return code: 0\nSucceeded: True\nFailed: False\n"
}

TASK [Check if a non-existent file exists] ************************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": "test -f /file/does/not/exist", "delta": "0:00:00.003183", "end": "2023-07-14 15:24:33.931406", "msg": "non-zero return code", "rc": 1, "start": "2023-07-14 15:24:33.928223", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
...ignoring

TASK [Show command result for missing file] ***********************************
ok: [localhost] => {
    "msg": "Return code: 1\nSucceeded: False\nFailed: True\n"
}

PLAY RECAP ********************************************************************
localhost                  : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=1

Beachten Sie, dass der Rückgabecode 0 für die vorhandene Datei und 1 für die nicht vorhandene Datei ist. Dies zeigt, wie Sie Rückgabecodes verwenden können, um Entscheidungen in Ihren Playbooks zu treffen.

Verstehen der Struktur der registrierten Variable

Die Variable, die von einem Shell-Befehl registriert wird, enthält mehrere nützliche Attribute:

  • stdout: Die Standardausgabe als einzelne Zeichenkette
  • stdout_lines: Die Standardausgabe aufgeteilt in eine Liste von Zeilen
  • stderr: Die Standardfehlerausgabe als einzelne Zeichenkette
  • stderr_lines: Die Standardfehlerausgabe aufgeteilt in eine Liste von Zeilen
  • rc: Der Rückgabecode (0 für Erfolg, ungleich 0 für Fehler)
  • cmd: Der Befehl, der ausgeführt wurde
  • start und end: Zeitstempel für den Beginn und das Ende des Befehls
  • delta: Die Dauer der Befehlsausführung

Das Verständnis dieser Struktur ist entscheidend für die effektive Arbeit mit der Ausgabe von Shell-Befehlen in Ansible.

Bedingte Ausführung basierend auf Shell-Ausgabe

Eine der leistungsstärksten Fähigkeiten in Ansible ist die Möglichkeit, Entscheidungen basierend auf der Ausgabe von Shell-Befehlen zu treffen. In diesem Schritt lernen wir, wie man Bedingungen und Filter verwendet, um die Ausgabe von Shell-Befehlen zu verarbeiten und Playbooks dynamischer zu gestalten.

Verwenden von Bedingungen mit Shell-Ausgabe

Erstellen wir ein Playbook, das Entscheidungen basierend auf der Ausgabe von Shell-Befehlen trifft:

  1. Erstellen Sie eine neue Datei namens conditional_playbook.yml im Verzeichnis /home/labex/project.
  2. Fügen Sie den folgenden Inhalt hinzu:
---
- name: Conditional Tasks Based on Command Output
  hosts: local
  gather_facts: no

  tasks:
    - name: Check disk space
      shell: df -h / | grep -v Filesystem | awk '{print $5}' | sed 's/%//'
      register: disk_usage

    - name: Display disk usage
      debug:
        msg: "Current disk usage: {{ disk_usage.stdout }}%"

    - name: Disk usage warning
      debug:
        msg: "WARNING: Disk usage is high"
      when: disk_usage.stdout|int > 50

    - name: Disk usage normal
      debug:
        msg: "Disk usage is normal"
      when: disk_usage.stdout|int <= 50

Dieses Playbook:

  • Führt einen Shell-Befehl aus, um den Festplattenauslastungsprozentsatz auf dem Root-Dateisystem zu überprüfen
  • Verwendet die Bedingung when basierend auf der Befehlsausgabe
  • Verwendet den int-Filter, um die Zeichenkettenausgabe zur Vergleichszwecken in eine Ganzzahl umzuwandeln

Führen Sie das Playbook aus:

ansible-playbook -i inventory.ini conditional_playbook.yml

Die Ausgabe variiert je nach Ihrer tatsächlichen Festplattenauslastung, sieht aber in etwa so aus:

PLAY [Conditional Tasks Based on Command Output] ******************************

TASK [Check disk space] *******************************************************
changed: [localhost]

TASK [Display disk usage] *****************************************************
ok: [localhost] => {
    "msg": "Current disk usage: 38%"
}

TASK [Disk usage warning] *****************************************************
skipped: [localhost]

TASK [Disk usage normal] ******************************************************
ok: [localhost] => {
    "msg": "Disk usage is normal"
}

PLAY RECAP ********************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

Beachten Sie, wie Ansible nur eine der bedingten Aufgaben basierend auf dem tatsächlichen Festplattenauslastungswert ausgeführt hat.

Umgang mit JSON-Ausgabe von Befehlen

Viele moderne CLI-Tools geben Daten im JSON-Format zurück. Ansible verfügt über integrierte Funktionen zur Verarbeitung von JSON-Ausgaben:

  1. Erstellen Sie eine neue Datei namens json_output.yml im Verzeichnis /home/labex/project.
  2. Fügen Sie den folgenden Inhalt hinzu:
---
- name: Handling JSON Output
  hosts: local
  gather_facts: no

  tasks:
    - name: Create a JSON file for testing
      copy:
        dest: /tmp/services.json
        content: |
          {
            "services": [
              {
                "name": "web",
                "status": "running",
                "port": 80
              },
              {
                "name": "database",
                "status": "stopped",
                "port": 5432
              },
              {
                "name": "cache",
                "status": "running",
                "port": 6379
              }
            ]
          }

    - name: Read JSON file with shell
      shell: cat /tmp/services.json
      register: json_output

    - name: Parse and display JSON content
      debug:
        msg: "{{ json_output.stdout | from_json }}"

    - name: Extract and display service information
      debug:
        msg: "Service: {{ item.name }}, Status: {{ item.status }}, Port: {{ item.port }}"
      loop: "{{ (json_output.stdout | from_json).services }}"

    - name: Show only running services
      debug:
        msg: "Running service: {{ item.name }} on port {{ item.port }}"
      loop: "{{ (json_output.stdout | from_json).services }}"
      when: item.status == "running"

Dieses Playbook:

  • Erstellt eine Beispiel-JSON-Datei zur Demonstration
  • Liest die JSON-Datei mit einem Shell-Befehl
  • Verwendet den from_json-Filter, um die JSON-Zeichenkette in eine Datenstruktur zu parsen
  • Durchläuft die Datenstruktur, um Informationen anzuzeigen
  • Verwendet bedingte Logik, um nur laufende Dienste zu filtern

Führen Sie das Playbook aus:

ansible-playbook -i inventory.ini json_output.yml

Sie sollten eine ähnliche Ausgabe wie diese sehen:

PLAY [Handling JSON Output] ***************************************************

TASK [Create a JSON file for testing] *****************************************
changed: [localhost]

TASK [Read JSON file with shell] **********************************************
changed: [localhost]

TASK [Parse and display JSON content] *****************************************
ok: [localhost] => {
    "msg": {
        "services": [
            {
                "name": "web",
                "port": 80,
                "status": "running"
            },
            {
                "name": "database",
                "port": 5432,
                "status": "stopped"
            },
            {
                "name": "cache",
                "port": 6379,
                "status": "running"
            }
        ]
    }
}

TASK [Extract and display service information] ********************************
ok: [localhost] => (item={'name': 'web', 'status': 'running', 'port': 80}) => {
    "msg": "Service: web, Status: running, Port: 80"
}
ok: [localhost] => (item={'name': 'database', 'status': 'stopped', 'port': 5432}) => {
    "msg": "Service: database, Status: stopped, Port: 5432"
}
ok: [localhost] => (item={'name': 'cache', 'status': 'running', 'port': 6379}) => {
    "msg": "Service: cache, Status: running, Port: 6379"
}

TASK [Show only running services] *********************************************
ok: [localhost] => (item={'name': 'web', 'status': 'running', 'port': 80}) => {
    "msg": "Running service: web on port 80"
}
skipped: [localhost] => (item={'name': 'database', 'status': 'stopped', 'port': 5432})
ok: [localhost] => (item={'name': 'cache', 'status': 'running', 'port': 6379}) => {
    "msg": "Running service: cache on port 6379"
}

PLAY RECAP ********************************************************************
localhost                  : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Beachten Sie, wie das Playbook das JSON parst, bestimmte Informationen extrahiert und die Daten basierend auf Bedingungen filtert.

Fehlerbehandlung mit Shell-Befehlen

Bei der Ausführung von Shell-Befehlen ist es wichtig, potenzielle Fehler ordnungsgemäß zu behandeln:

  1. Erstellen Sie eine neue Datei namens error_handling.yml im Verzeichnis /home/labex/project.
  2. Fügen Sie den folgenden Inhalt hinzu:
---
- name: Error Handling with Shell Commands
  hosts: local
  gather_facts: no

  tasks:
    - name: Run a potentially failing command
      shell: grep "nonexistent_pattern" /etc/passwd
      register: command_result
      ignore_errors: yes

    - name: Display success or failure
      debug:
        msg: "Command {{ 'succeeded' if command_result.rc == 0 else 'failed with return code ' + command_result.rc|string }}"

    - name: Run a custom failing command
      shell: exit 3
      register: exit_command
      ignore_errors: yes

    - name: Display detailed error information
      debug:
        msg: |
          Return code: {{ exit_command.rc }}
          Error message: {{ exit_command.stderr if exit_command.stderr else 'No error message' }}

Dieses Playbook:

  • Führt Befehle aus, von denen erwartet wird, dass sie fehlschlagen
  • Verwendet ignore_errors: yes, um die Ausführung des Playbooks fortzusetzen, auch wenn Befehle fehlschlagen
  • Zeigt verschiedene Methoden zur Behandlung und Anzeige von Fehlerinformationen

Führen Sie das Playbook aus:

ansible-playbook -i inventory.ini error_handling.yml

Sie sollten eine ähnliche Ausgabe wie diese sehen:

PLAY [Error Handling with Shell Commands] *************************************

TASK [Run a potentially failing command] **************************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": "grep \"nonexistent_pattern\" /etc/passwd", "delta": "0:00:00.002916", "end": "2023-07-14 16:10:23.671519", "msg": "non-zero return code", "rc": 1, "start": "2023-07-14 16:10:23.668603", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
...ignoring

TASK [Display success or failure] *********************************************
ok: [localhost] => {
    "msg": "Command failed with return code 1"
}

TASK [Run a custom failing command] *******************************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": "exit 3", "delta": "0:00:00.002447", "end": "2023-07-14 16:10:23.906121", "msg": "non-zero return code", "rc": 3, "start": "2023-07-14 16:10:23.903674", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
...ignoring

TASK [Display detailed error information] *************************************
ok: [localhost] => {
    "msg": "Return code: 3\nError message: No error message\n"
}

PLAY RECAP ********************************************************************
localhost                  : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=2

Dies zeigt, wie man auf verschiedene Fehlerbedingungen reagieren kann, wenn Shell-Befehle ausgeführt werden.

Erstellen eines praktischen Tools zur Verarbeitung der Shell-Ausgabe

In diesem letzten Schritt werden wir alles, was wir gelernt haben, zusammenführen, um ein praktisches Ansible-Playbook zu erstellen, das Systeminformationen sammelt, verarbeitet und einen Bericht generiert. Dies stellt ein reales Szenario dar, in dem die Fähigkeiten von Ansible zur Verarbeitung von Shell-Befehlen äußerst nützlich sein können.

Erstellen eines Tools zur Erstellung von Systeminformationsberichten

Erstellen wir ein umfassendes Tool zur Erfassung von Systeminformationen:

  1. Erstellen Sie eine neue Datei namens system_report.yml im Verzeichnis /home/labex/project.
  2. Fügen Sie den folgenden Inhalt hinzu:
---
- name: Comprehensive System Report
  hosts: local
  gather_facts: no

  vars:
    report_file: /tmp/system_report.txt

  tasks:
    - name: Collect basic system information
      shell: |
        echo "SYSTEM REPORT" > {{ report_file }}
        echo "=============" >> {{ report_file }}
        echo "" >> {{ report_file }}

        echo "HOSTNAME: $(hostname)" >> {{ report_file }}
        echo "TIMESTAMP: $(date)" >> {{ report_file }}
        echo "" >> {{ report_file }}

        echo "SYSTEM INFORMATION" >> {{ report_file }}
        echo "------------------" >> {{ report_file }}
        echo "OS: $(cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2)" >> {{ report_file }}
        echo "KERNEL: $(uname -r)" >> {{ report_file }}
        echo "UPTIME: $(uptime -p)" >> {{ report_file }}
        echo "" >> {{ report_file }}

        echo "RESOURCE UTILIZATION" >> {{ report_file }}
        echo "-------------------" >> {{ report_file }}
        echo "CPU LOAD: $(uptime | awk -F'load average:' '{print $2}')" >> {{ report_file }}
        echo "MEMORY USAGE:" >> {{ report_file }}
        free -h >> {{ report_file }}
        echo "" >> {{ report_file }}
        echo "DISK USAGE:" >> {{ report_file }}
        df -h >> {{ report_file }}
        echo "" >> {{ report_file }}

        echo "NETWORK INFORMATION" >> {{ report_file }}
        echo "-------------------" >> {{ report_file }}
        echo "IP ADDRESSES:" >> {{ report_file }}
        ip addr | grep "inet " | awk '{print $2}' >> {{ report_file }}
        echo "" >> {{ report_file }}

        echo "PROCESS INFORMATION" >> {{ report_file }}
        echo "-------------------" >> {{ report_file }}
        echo "TOP 5 CPU CONSUMING PROCESSES:" >> {{ report_file }}
        ps aux --sort=-%cpu | head -6 >> {{ report_file }}
        echo "" >> {{ report_file }}
        echo "TOP 5 MEMORY CONSUMING PROCESSES:" >> {{ report_file }}
        ps aux --sort=-%mem | head -6 >> {{ report_file }}
      register: report_generation

    - name: Check if report was generated successfully
      stat:
        path: "{{ report_file }}"
      register: report_stat

    - name: Display report generation status
      debug:
        msg: "Report generated successfully at {{ report_file }}"
      when: report_stat.stat.exists

    - name: Display report content
      shell: cat {{ report_file }}
      register: report_content
      when: report_stat.stat.exists

    - name: Show report content
      debug:
        msg: "{{ report_content.stdout_lines }}"
      when: report_stat.stat.exists

    - name: Analyze disk usage
      shell: df -h / | grep -v Filesystem | awk '{print $5}' | sed 's/%//'
      register: disk_usage
      when: report_stat.stat.exists

    - name: Generate disk usage alert if needed
      debug:
        msg: "ALERT: Disk usage on / is {{ disk_usage.stdout }}% which exceeds the 80% threshold!"
      when:
        - report_stat.stat.exists
        - disk_usage.stdout|int > 80

    - name: Generate disk usage warning if needed
      debug:
        msg: "WARNING: Disk usage on / is {{ disk_usage.stdout }}% which exceeds the 60% threshold."
      when:
        - report_stat.stat.exists
        - disk_usage.stdout|int > 60
        - disk_usage.stdout|int <= 80

    - name: Confirm normal disk usage
      debug:
        msg: "Disk usage on / is normal at {{ disk_usage.stdout }}%."
      when:
        - report_stat.stat.exists
        - disk_usage.stdout|int <= 60

Dieses Playbook:

  • Sammelt umfassende Systeminformationen mithilfe einer Reihe von Shell-Befehlen
  • Schreibt die Informationen in eine Berichtdatei
  • Überprüft, ob der Bericht erfolgreich erstellt wurde
  • Zeigt den Inhalt des Berichts an
  • Analysiert die Festplattenauslastungsdaten
  • Generiert entsprechende Warnungen basierend auf der Analyse

Führen Sie das Playbook aus:

ansible-playbook -i inventory.ini system_report.yml

Sie sehen eine umfassende Ausgabe, die die Ausführung des Playbooks und den vollständigen Systembericht zeigt. Die Ausgabe ist recht lang, daher hier nur ein Beispiel dafür, was Sie sehen könnten:

PLAY [Comprehensive System Report] ********************************************

TASK [Collect basic system information] ***************************************
changed: [localhost]

TASK [Check if report was generated successfully] *****************************
ok: [localhost]

TASK [Display report generation status] ***************************************
ok: [localhost] => {
    "msg": "Report generated successfully at /tmp/system_report.txt"
}

TASK [Display report content] *************************************************
changed: [localhost]

TASK [Show report content] ****************************************************
ok: [localhost] => {
    "msg": [
        "SYSTEM REPORT",
        "=============",
        "",
        "HOSTNAME: ubuntu-vm",
        "TIMESTAMP: Fri Jul 14 16:35:42 UTC 2023",
        "",
        "SYSTEM INFORMATION",
        "------------------",
        "OS: \"Ubuntu 22.04.1 LTS\"",
        "KERNEL: 5.15.0-1023-azure",
        "UPTIME: up 3 hours, 25 minutes",
        ...

Untersuchen des Berichts

Untersuchen wir den von uns generierten Systembericht:

cat /tmp/system_report.txt

Dadurch wird der vollständige Bericht angezeigt, der von unserem Playbook generiert wurde.

Erstellen eines benutzerdefinierten Shell-Skripts und Aufrufen aus Ansible

Für komplexere Operationen ist es manchmal einfacher, ein dediziertes Shell-Skript zu erstellen und es von Ansible aus aufzurufen:

  1. Erstellen Sie eine neue Datei namens disk_analyzer.sh im Verzeichnis /home/labex/project.
  2. Fügen Sie den folgenden Inhalt hinzu:
#!/bin/bash

## disk_analyzer.sh - A simple script to analyze disk usage

echo "DISK USAGE ANALYSIS"
echo "------------------"

## Get overall disk usage
ROOT_USAGE=$(df -h / | grep -v Filesystem | awk '{print $5}' | sed 's/%//')
echo "Root filesystem usage: ${ROOT_USAGE}%"

## Categorize the usage
if [ $ROOT_USAGE -gt 80 ]; then
  echo "STATUS: CRITICAL - Immediate action required"
elif [ $ROOT_USAGE -gt 60 ]; then
  echo "STATUS: WARNING - Consider cleaning up disk space"
else
  echo "STATUS: OK - Disk usage is within normal parameters"
fi

echo ""

## Find largest directories
echo "Top 5 largest directories in /var:"
du -h /var --max-depth=1 2> /dev/null | sort -hr | head -5

echo ""

## Find largest files
echo "Top 5 largest files in /var/log:"
find /var/log -type f -exec du -h {} \; 2> /dev/null | sort -hr | head -5

exit 0
  1. Machen Sie das Skript ausführbar:
chmod +x /home/labex/project/disk_analyzer.sh
  1. Erstellen Sie ein neues Playbook, um dieses Skript aufzurufen:
touch /home/labex/project/call_script.yml
  1. Fügen Sie den folgenden Inhalt zum Playbook hinzu:
---
- name: Call Custom Shell Script
  hosts: local
  gather_facts: no

  tasks:
    - name: Run disk analyzer script
      shell: /home/labex/project/disk_analyzer.sh
      register: script_output

    - name: Display script output
      debug:
        msg: "{{ script_output.stdout_lines }}"

    - name: Check for critical status
      debug:
        msg: "CRITICAL DISK USAGE DETECTED! Immediate action required."
      when: script_output.stdout is search("STATUS: CRITICAL")
  1. Führen Sie das Playbook aus:
ansible-playbook -i inventory.ini call_script.yml

Sie sollten eine ähnliche Ausgabe wie diese sehen:

PLAY [Call Custom Shell Script] ***********************************************

TASK [Run disk analyzer script] ***********************************************
changed: [localhost]

TASK [Display script output] **************************************************
ok: [localhost] => {
    "msg": [
        "DISK USAGE ANALYSIS",
        "------------------",
        "Root filesystem usage: 38%",
        "STATUS: OK - Disk usage is within normal parameters",
        "",
        "Top 5 largest directories in /var:",
        "60M\t/var/lib",
        "60M\t/var/cache",
        "12M\t/var/log",
        "4.0K\t/var/tmp",
        "4.0K\t/var/mail",
        "",
        "Top 5 largest files in /var/log:",
        "4.0M\t/var/log/journal/c75af53674ce472fb9654a1d5cf8cc37/system.journal",
        "2.3M\t/var/log/auth.log",
        "1.3M\t/var/log/syslog",
        "724K\t/var/log/kern.log",
        "428K\t/var/log/cloud-init.log"
    ]
}

TASK [Check for critical status] **********************************************
skipped: [localhost]

PLAY RECAP ********************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

Dieser Ansatz kombiniert die Leistungsfähigkeit von Shell-Skripten mit den Automatisierungsfunktionen von Ansible. Das Shell-Skript übernimmt die komplexe Logik für die Festplattenanalyse, während Ansible die Ausführung und weitere Verarbeitung der Ergebnisse verwaltet.

Wichtige Erkenntnisse

Durch dieses Lab haben Sie mehrere wichtige Techniken für die Arbeit mit der Ausgabe von Shell-Befehlen in Ansible gelernt:

  1. Wie man Shell-Befehle ausführt und deren Ausgabe erfasst
  2. Wie man die Befehlsausgabe zur Anzeige verarbeitet und formatiert
  3. Wie man die bedingte Ausführung basierend auf Befehlsergebnissen verwendet
  4. Wie man JSON-Ausgabe und Fehlerbedingungen behandelt
  5. Wie man praktische Tools erstellt, die Shell-Befehle mit den Automatisierungsfunktionen von Ansible kombinieren

Diese Fähigkeiten sind von unschätzbarem Wert, wenn Sie komplexere Automatisierungslösungen mit Ansible erstellen.

Zusammenfassung

In diesem Lab haben Sie gelernt, wie man effektiv mit der Ausgabe von Shell-Befehlen in Ansible-Playbooks arbeitet. Beginnend mit den Grundlagen der Ausführung von Shell-Befehlen und der Erfassung ihrer Ausgabe, sind Sie zu fortgeschritteneren Techniken wie bedingter Ausführung, Fehlerbehandlung und der Verarbeitung strukturierter Datenformate wie JSON übergegangen.

Sie haben mehrere wichtige Fähigkeiten gemeistert:

  • Ausführen von Shell-Befehlen in Ansible-Playbooks mit dem shell-Modul
  • Erfassen der Befehlsausgabe mit der Direktive register
  • Anzeigen der Ausgabe mit dem debug-Modul
  • Verarbeiten der Ausgabe mit Jinja2-Filtern und -Bedingungen
  • Erstellen praktischer Automatisierungstools, die Ansible mit Shell-Skripten kombinieren

Diese Techniken ermöglichen es Ihnen, dynamischere und reaktionsfähigere Automatisierungs-Workflows zu erstellen, die sich an unterschiedliche Systembedingungen anpassen und nützliches Feedback über die durchgeführten Operationen geben können.

Denken Sie auf Ihrer Ansible-Reise daran, dass die integrierten Module von Ansible oft eine robustere und portablere Lösung für gängige Aufgaben darstellen, auch wenn Shell-Befehle eine große Flexibilität bieten. Verwenden Sie Shell-Befehle, wenn Sie vorhandene Shell-Skripte nutzen oder komplexe Operationen durchführen müssen, die nicht einfach von Ansible-Modulen gehandhabt werden können.