SSD Nodes Learn 🎉 VPS ab $5.50/Monat
Anleitungen Matt ConnorVon Matt Connor · Aktualisiert 2026-08-07

Zabbix auf Ubuntu 24.04 mit Ansible installieren

Dieses idempotente Ansible-Playbook installiert Zabbix 7.0 LTS auf Ubuntu 24.04, nutzt ein Vault-Datenbankpasswort und richtet Agents im gesamten Inventar ein.

Was dieses Ansible-Playbook installiert

Ein Ansible-Playbook zur Installation von Zabbix auf Ubuntu 24.04 besteht aus zwei Plays, zwei Konfigurationsvorlagen, einer verschlüsselten Variablendatei und einem Inventar. zabbix-server.yml fügt das Zabbix-Repository hinzu, installiert die Serverpakete, erstellt die MySQL-Datenbank und schreibt /etc/zabbix/zabbix_server.conf auf einem Host. zabbix-agents.yml installiert den Agent auf allen anderen Hosts im Inventar. Beide Plays sind idempotent. Bei einem zweiten Lauf wird changed=0 gemeldet. Anschließend können Sie --check als Test auf Konfigurationsabweichungen verwenden.

Auf den Zielsystemen benötigen Sie Ubuntu 24.04 und ein sudo-Konto, das Sie über SSH (Secure Shell) erreichen können. Alle folgenden Schritte werden von einem Laptop oder einer Jump-Box mit installiertem ansible-core ausgeführt. Wenn Sie noch nie ein Play geschrieben haben, lesen Sie zuerst Ihr erstes Ansible-Playbook auf einem VPS. Wenn Sie vor der Automatisierung sehen möchten, was diese Pakete tun, beschreibt die manuelle Zabbix-Installation auf Ubuntu 24.04 denselben Ablauf Schritt für Schritt.

Das Playbook verwendet Zabbix 7.0 LTS, also die Long-Term-Support-Version, für die es im August 2026 geschrieben wurde. Zabbix unterstützt 7.0 bis 2029. Deshalb gehört die Version in eine Variable, die Sie bewusst ändern. Sie sollte nicht in einer URL innerhalb eines Tasks verborgen sein.

Das Projektlayout

zabbix/
  ansible.cfg
  inventory.ini
  zabbix-server.yml
  zabbix-agents.yml
  group_vars/
    all.yml
    zabbix_server/
      main.yml
      vault.yml
  templates/
    zabbix_server.conf.j2
    zabbix_agentd.conf.j2

Die Konfigurationsdatei enthält drei Einstellungen. Dadurch müssen Sie sich keine Befehlszeilenoptionen merken.

[defaults]
inventory = inventory.ini
forks = 20
vault_password_file = ~/.zabbix-vault-pass

Prüfen Sie mit ansible-config dump --only-changed, welche Einstellungen aktiv sind. Der Befehl gibt die Datei aus, aus der jeder Wert stammt:

DEFAULT_FORKS(/home/you/zabbix/ansible.cfg) = 20
DEFAULT_HOST_LIST(/home/you/zabbix/ansible.cfg) = ['/home/you/zabbix/inventory.ini']
DEFAULT_VAULT_PASSWORD_FILE(/home/you/zabbix/ansible.cfg) = /home/you/.zabbix-vault-pass

Ansible liest ansible.cfg aus dem aktuellen Arbeitsverzeichnis. Führen Sie denselben Befehl daher aus Ihrem Home-Verzeichnis aus. Dann wird keine dieser drei Zeilen angezeigt. Das ist kein rein kosmetischer Unterschied: ansible-playbook, das aus dem falschen Verzeichnis gestartet wurde, hat kein Vault-Passwort und bricht mit ERROR! Attempting to decrypt but no vault secrets found ab. Führen Sie diese Playbooks jedes Mal aus dem Projektverzeichnis aus.

Die Vault-Passwortdatei enthält eine Zeile, liegt außerhalb des Repositorys und hat den Modus 600: chmod 600 ~/.zabbix-vault-pass. Jeder, der diese Datei lesen kann, kann alle Secrets im Projekt lesen.

[zabbix_server]
zbx01 ansible_host=203.0.113.10

[zabbix_agents]
web01 ansible_host=203.0.113.21
db01 ansible_host=203.0.113.22

[zabbix_agents:vars]
zabbix_server_ip=203.0.113.10

Führen Sie ansible-inventory --list vor dem ersten Playbook aus. Der Befehl gibt die Gruppen und Hosts aus, die Ansible tatsächlich eingelesen hat. So erkennen Sie, ob ein Host in keiner Gruppe gelandet ist.

Warum Ihr in Vault abgelegtes Zabbix-Passwort ignoriert wird

Das Datenbankpasswort ist hier das einzige echte Geheimnis. Genau dieses Passwort wird in den meisten Zabbix-Playbooks falsch behandelt. Legen Sie die Standardwerte in group_vars/all.yml ab. Verwenden Sie dabei ein Platzhalterpasswort, das eindeutig kein echtes Passwort ist:

zabbix_version: "7.0"
zabbix_db_host: localhost
zabbix_db_name: zabbix
zabbix_db_user: zabbix
zabbix_db_password: CHANGE-ME-IN-VAULT
zabbix_start_pollers: 5
zabbix_cache_size: 32M

Hosts mit einer Datenbank erhalten den echten Wert aus einer Datei, die nur diese Hosts einlesen. group_vars/zabbix_server/main.yml ordnet den Namen aus dem Vault dem Namen zu, den die Tasks verwenden:

zabbix_db_password: "{{ vault_zabbix_db_password }}"

Generieren Sie mit openssl rand -base64 24 ein Passwort. Erstellen Sie anschließend die verschlüsselte Datei:

ansible-vault create group_vars/zabbix_server/vault.yml
ansible-vault edit group_vars/zabbix_server/vault.yml

Die Datei enthält eine Zeile, vault_zabbix_db_password: the-string-you-generated, und beginnt auf dem Datenträger mit $ANSIBLE_VAULT;1.1;AES256. Wenn Sie stattdessen eine lesbare Datei mit einem verschlüsselten Wert bevorzugen, gibt ansible-vault encrypt_string --name vault_zabbix_db_password 'the-string-you-generated' einen Block aus, den Sie in eine beliebige Vars-Datei einfügen können. Das Präfix vault_ sollten Sie beibehalten. grep -r vault_ . listet anschließend alle Secrets auf, die das Projekt erwartet.

Jetzt kommt die Falle. Ansible priorisiert Variablenquellen. Dabei spielt es keine Rolle, welche Quelle für Sie spezifischer aussieht. Die hier relevanten Quellen haben folgende Reihenfolge, von der niedrigsten zur höchsten Priorität: group_vars/all, dann group_vars/<group>, dann host_vars, danach ein vars:-Block direkt im Play und zuletzt -e in der Befehlszeile. -e hat Vorrang vor allen anderen Quellen. Ein Standardwert auf Play-Ebene setzt sich daher stillschweigend gegen die Vault-Datei durch:

- name: How a play level var silently wins
  hosts: zabbix_server
  vars:
    zabbix_db_password: CHANGE-ME-IN-VAULT
  tasks:
    - name: Print the sha1 of the password that won
      ansible.builtin.debug:
        msg: "zabbix_db_password sha1 = {{ zabbix_db_password | hash('sha1') }}"
      tags: [verify]

Speichern Sie das als precedence-demo.yml und führen Sie ansible-playbook precedence-demo.yml --tags verify aus. Es wird keine Warnung ausgegeben. Die Vault-Datei wird erfolgreich entschlüsselt, vault_zabbix_db_password wird korrekt geladen, und das Play verwendet trotzdem den Platzhalter. MySQL erhält dadurch einen Benutzer, dessen Passwort aus der Zeichenfolge CHANGE-ME-IN-VAULT besteht, und Zabbix startet problemlos mit diesem Passwort.

Die Lösung besteht darin, den vars:-Block zu löschen. Das folgende Server-Playbook enthält keinen solchen Block. Der Platzhalter steht in group_vars/all.yml, der echte Wert in group_vars/zabbix_server/. Die Gruppendatei setzt sich durch, weil eine benannte Gruppe Vorrang vor all hat.

Prüfen Sie, welcher Wert verwendet wurde, ohne ihn auszugeben:

ansible-playbook zabbix-server.yml --tags verify
printf %s 'the-string-you-generated' | sha1sum

Die beiden Hashes stimmen überein, wenn der Wert aus dem Vault verwendet wurde. Ein Hash von CHANGE-ME-IN-VAULT bedeutet, dass etwas mit höherer Priorität als group_vars/zabbix_server den Wert überschreibt. Die Ausgabe eines Hashes ist sicher. Die Ausgabe des Passworts ist es nicht. Deshalb hasht der Verify-Task den Wert.

Die Befehlszeile ist die bewusste Ausnahme. ansible-playbook zabbix-server.yml --tags verify -e zabbix_db_password=CHANGE-ME-IN-VAULT gibt den Hash des Platzhalters aus, weil -e Vorrang vor allen Dateien auf dem Datenträger hat. Das ist die Ausweichmöglichkeit für einen einmaligen Lauf. Gleichzeitig lässt sich damit ein funktionierendes Passwort am schnellsten versehentlich überschreiben.

Ein Nebeneffekt ist wichtig: group_vars/all.yml gilt auch für die Agents. Daher zeigt ansible-inventory --host web01 auf web01 den Wert CHANGE-ME-IN-VAULT an. Das Agent-Playbook liest diese Variable an keiner Stelle ein. Sie bleibt deshalb ein Platzhalter. Die Hosts mit einem echten Secret sind genau die Hosts in group_vars/zabbix_server/.

Das Ansible-Playbook, das den Zabbix-Server installiert

- name: Install and configure the Zabbix server
  hosts: zabbix_server
  become: true

  handlers:
    - name: Restart zabbix-server
      ansible.builtin.systemd_service:
        name: zabbix-server
        state: restarted

  tasks:
    - name: Show which zabbix_db_password won, without printing it
      ansible.builtin.debug:
        msg: "zabbix_db_password sha1 = {{ zabbix_db_password | hash('sha1') }}"
      tags: [verify]

    - name: Install the Zabbix repository package
      ansible.builtin.apt:
        deb: "https://repo.zabbix.com/zabbix/{{ zabbix_version }}/ubuntu/pool/main/z/zabbix-release/zabbix-release_latest_{{ zabbix_version }}+ubuntu{{ ansible_distribution_version }}_all.deb"
      register: zabbix_repo

    - name: Install the Zabbix server packages
      ansible.builtin.apt:
        name:
          - zabbix-server-mysql
          - zabbix-sql-scripts
        state: present
        update_cache: "{{ zabbix_repo is changed }}"

    - name: Install MySQL and the Python client library
      ansible.builtin.apt:
        name:
          - mysql-server
          - python3-pymysql
        state: present

    - name: Enable and start MySQL
      ansible.builtin.systemd_service:
        name: mysql
        enabled: true
        state: started

    - name: Create the Zabbix database
      community.mysql.mysql_db:
        name: "{{ zabbix_db_name }}"
        encoding: utf8mb4
        collation: utf8mb4_bin
        state: present
        login_unix_socket: /run/mysqld/mysqld.sock

    - name: Create the Zabbix database user
      community.mysql.mysql_user:
        name: "{{ zabbix_db_user }}"
        host: localhost
        password: "{{ zabbix_db_password }}"
        priv: "{{ zabbix_db_name }}.*:ALL"
        update_password: on_create
        state: present
        login_unix_socket: /run/mysqld/mysqld.sock
      no_log: true

Das Repository stammt aus dem Paket zabbix-release und nicht aus einer manuell erstellten Sources-Datei. Dieses Paket enthält sowohl den Signaturschlüssel als auch den Sources-Eintrag. Außerdem überspringt apt das Paket, wenn bereits dieselbe Version installiert ist. Die URL wird aus Facts zusammengesetzt. Daher trägt ansible_distribution_version den Wert in 24.04 ein, und zabbix_version den Wert in 7.0. Zabbix 8.0 legt dieselbe Datei unter /zabbix/8.0/release/ubuntu/ ab. Wenn Sie die Versionsvariable erhöhen, müssen Sie daher auch den Pfad prüfen.

Die Paketaufgabe aktualisiert den apt-Cache nur, wenn die Repository-Aufgabe Änderungen vorgenommen hat. Eine separate apt-Aufgabe, die ausschließlich den Cache aktualisiert, meldet bei jedem Lauf eine Änderung. Dadurch funktioniert die weiter unten beschriebene Drift-Prüfung nicht mehr. Die Aktualisierung vollständig zu überspringen, wäre noch schlechter: apt weiß dann nicht, dass das neue Repository vorhanden ist, und die Installation schlägt fehl.

python3-pymysql ist erforderlich. Die community.mysql-Module werden auf dem Zielhost ausgeführt und importieren dort PyMySQL. Ohne dieses Paket schlägt jede Datenbankaufgabe mit A MySQL module is required: for Python 3.x, PyMySQL fehl. Eine Installation auf Ihrem Laptop ändert daran nichts.

Die Datenbankaufgaben verbinden sich über den Unix-Socket. Unter Ubuntu verwendet das MySQL-root-Konto das Plugin auth_socket. Daher gibt es kein root-Passwort. Die Identität wird anhand des verbindenden Systembenutzers ermittelt. Deshalb benötigt das Playbook become: true und login_unix_socket gemeinsam. Wenn eines davon fehlt, antwortet MySQL mit Access denied for user 'root'@'localhost'.

update_password: on_create macht den Unterschied zwischen einem Playbook aus, das Sie zweimal ausführen können, und einem, bei dem das nicht möglich ist. MySQL speichert einen gesalzenen Hash. Das Modul kann daher Ihr Klartextpasswort nicht mit dem gespeicherten Wert vergleichen. Mit der Standardeinstellung schreibt es das Passwort bei jedem einzelnen Lauf erneut und meldet changed. Mit on_create wird das Passwort einmalig gesetzt, wenn der Benutzer angelegt wird.

Das Schema laden, ohne es zweimal zu laden

    - name: Look up whether the schema is already loaded
      community.mysql.mysql_query:
        login_db: "{{ zabbix_db_name }}"
        login_unix_socket: /run/mysqld/mysqld.sock
        query: "SELECT COUNT(*) AS n FROM information_schema.tables WHERE table_schema = %s AND table_name = 'users'"
        positional_args:
          - "{{ zabbix_db_name }}"
      register: zabbix_schema
      changed_when: false
      check_mode: false

    - name: Record whether the schema still has to be imported
      ansible.builtin.set_fact:
        zabbix_schema_missing: "{{ zabbix_schema.query_result[0][0].n == 0 }}"

    - name: Allow function creation for the import
      community.mysql.mysql_variables:
        variable: log_bin_trust_function_creators
        value: 1
        login_unix_socket: /run/mysqld/mysqld.sock
      when: zabbix_schema_missing | bool
      check_mode: false

    - name: Import the Zabbix schema
      ansible.builtin.shell:
        cmd: "set -o pipefail && zcat /usr/share/zabbix-sql-scripts/mysql/server.sql.gz | mysql --default-character-set=utf8mb4 --user={{ zabbix_db_user }} --database={{ zabbix_db_name }}"
        executable: /bin/bash
      environment:
        MYSQL_PWD: "{{ zabbix_db_password }}"
      when: zabbix_schema_missing | bool
      no_log: true

    - name: Stop allowing function creation
      community.mysql.mysql_variables:
        variable: log_bin_trust_function_creators
        value: 0
        login_unix_socket: /run/mysqld/mysqld.sock
      when: zabbix_schema_missing | bool
      check_mode: false

    - name: Write /etc/zabbix/zabbix_server.conf
      ansible.builtin.template:
        src: zabbix_server.conf.j2
        dest: /etc/zabbix/zabbix_server.conf
        owner: root
        group: zabbix
        mode: "0640"
      no_log: true
      notify: Restart zabbix-server

    - name: Enable and start the Zabbix server
      ansible.builtin.systemd_service:
        name: zabbix-server
        enabled: true
        state: started

Die Prüfung fragt die Datenbank ab, nicht das Dateisystem. Eine Markerdatei unter /etc wäre sofort falsch, sobald jemand die Datenbank löscht oder ein älteres Backup wiederherstellt. information_schema liefert dagegen immer die korrekte Information darüber, ob die Tabelle users vorhanden ist. positional_args bindet den Datenbanknamen, statt ihn in die SQL-Zeichenkette einzusetzen.

MySQL 8.0 auf Ubuntu 24.04 aktiviert standardmäßig die Binärprotokollierung. Das Zabbix-Schema erstellt gespeicherte Funktionen. Ohne die temporäre Umschaltung bricht der Import bei ERROR 1419 (HY000) mit der Meldung ab, dass bei aktivierter Binärprotokollierung das Privileg SUPER fehlt. Die zweite Umschaltung setzt die Einstellung zurück. Die gelockerte Regel gilt damit nur für die Dauer des Imports.

Der Import ist der eine Fall, in dem shell einem Modul überlegen ist. Kein Modul leitet eine komprimierte SQL-Datei an einen Client weiter. command kann überhaupt keine Weiterleitung ausführen. set -o pipefail mit executable: /bin/bash sorgt dafür, dass ein Fehler in zcat den Task fehlschlagen lässt, statt hinter dem Exit-Code von mysql verborgen zu bleiben. Auf einer kleinen VPS dauert der Vorgang ein bis zwei Minuten. Währenddessen wird keine Ausgabe angezeigt.

Das Passwort erreicht den Client über MYSQL_PWD und nicht über -p. Ein Passwort nach -p veranlasst den Client, Warning: Using a password on the command line interface can be insecure auszugeben. Außerdem wird das Geheimnis in die Befehlszeichenkette aufgenommen, die Ansible erstellt. no_log: true hält den gesamten Vorgang aus der Ausgabe heraus.

templates/zabbix_server.conf.j2 ist die Datei, die die Konfiguration reproduzierbar macht:

# Managed by Ansible. Local edits are overwritten on the next run.
LogFile=/var/log/zabbix/zabbix_server.log
PidFile=/run/zabbix/zabbix_server.pid
SocketDir=/run/zabbix
DBHost={{ zabbix_db_host }}
DBName={{ zabbix_db_name }}
DBUser={{ zabbix_db_user }}
DBPassword={{ zabbix_db_password }}
StartPollers={{ zabbix_start_pollers }}
CacheSize={{ zabbix_cache_size }}
Timeout=4

template ist hier besser als lineinfile, weil die gesamte Datei zu Ansible gehört: eine Quelle, ein Verantwortlicher und keine Abweichungen durch Änderungen, die jemand um 2am vorgenommen hat. DBHost=localhost weist den MySQL-Client an, den Unix-Socket zu verwenden. Schreiben Sie stattdessen 127.0.0.1, verwendet der Client das Netzwerk. Dieses muss dann geöffnet und freigegeben sein.

Playbook sicher mit --check --diff ausführen

ansible-playbook zabbix-server.yml --check --diff ist ein Driftbericht: Er zeigt, was seit dem letzten Lauf auf dem Host geändert wurde. Das ist nur dann brauchbar, wenn ein konvergierter Host changed=0 meldet, denn einen Lauf, der immer vier Änderungen anzeigt, liest niemand. Die drei Details in den obigen Tasks dienen genau diesem Zweck.

changed_when: false bei der Schemaabfrage verhindert, dass ein Lesevorgang in die Änderungsanzahl einfließt, da ein SELECT nichts ändert. check_mode: false beim selben Task sorgt dafür, dass er auch unter --check ausgeführt wird. Das ist erforderlich, weil der nächste Task zabbix_schema.query_result liest. Ohne diese Einstellung überspringt der Check-Modus die Abfrage, und das Play schlägt bei 'dict object' has no attribute 'query_result' fehl.

Die beiden log_bin_trust_function_creators-Schalter tragen gemeinsam check_mode: false. Sie bilden die Vorbereitung und den Abbau rund um einen Import. Eine Simulation würde daher zwei Änderungen melden, die der Lauf nie ausführt, und auf einem Host ohne Drift eine Änderung anzeigen.

Der Template-Task enthält no_log: true. Das hat einen wichtigen Nachteil: --diff gibt die Datei aus, die geschrieben würde, und diese Datei enthält DBPassword. Ohne no_log würde jede Driftprüfung das Datenbankpasswort in Ihrem Terminal und in jedem System ausgeben, das die Ausgabe sammelt. Sie verzichten bei dieser einen Datei auf den Diff und behalten das Secret geschützt.

Der Check-Modus ist eine Driftprüfung und keine Simulation einer Erstinstallation. Auf einem Host, auf dem das Playbook noch nie ausgeführt wurde, schlägt er fehl, weil die Datenbank-Tasks keine Datenbank abfragen können, die apt noch nicht installiert hat. Führen Sie zuerst eine Konvergenz des Hosts durch und prüfen Sie ihn anschließend.

Installieren des Zabbix-Agenten im gesamten Inventar

- name: Install the Zabbix agent
  hosts: zabbix_agents
  become: true

  handlers:
    - name: Restart zabbix-agent
      ansible.builtin.systemd_service:
        name: zabbix-agent
        state: restarted

  tasks:
    - name: Install the Zabbix repository package
      ansible.builtin.apt:
        deb: "https://repo.zabbix.com/zabbix/{{ zabbix_version }}/ubuntu/pool/main/z/zabbix-release/zabbix-release_latest_{{ zabbix_version }}+ubuntu{{ ansible_distribution_version }}_all.deb"
      register: zabbix_repo

    - name: Install the agent package
      ansible.builtin.apt:
        name: zabbix-agent
        state: present
        update_cache: "{{ zabbix_repo is changed }}"

    - name: Write /etc/zabbix/zabbix_agentd.conf
      ansible.builtin.template:
        src: zabbix_agentd.conf.j2
        dest: /etc/zabbix/zabbix_agentd.conf
        owner: root
        group: root
        mode: "0644"
      notify: Restart zabbix-agent

    - name: Enable and start the agent
      ansible.builtin.systemd_service:
        name: zabbix-agent
        enabled: true
        state: started

templates/zabbix_agentd.conf.j2 benötigt die Serveradresse und einen Hostnamen, der mit der Konfiguration im Zabbix-Frontend übereinstimmt:

# Managed by Ansible. Local edits are overwritten on the next run.
PidFile=/run/zabbix/zabbix_agentd.pid
LogFile=/var/log/zabbix/zabbix_agentd.log
LogFileSize=0
Server={{ zabbix_server_ip }}
ServerActive={{ zabbix_server_ip }}
Hostname={{ inventory_hostname }}

Server ist eine Zugriffsliste: Der Agent beantwortet passive Prüfungen nur von den dort aufgeführten Adressen. ServerActive ist die Adresse, zu der der Agent für aktive Prüfungen ausgehende Verbindungen herstellt. inventory_hostname gibt jedem Agenten den Namen, unter dem Ansible ihn bereits kennt. Dadurch bleibt das Inventar die zentrale Liste aller Rechner. Einen Host zur Überwachung hinzuzufügen, erfordert dann eine Zeile in inventory.ini und einen Playbook-Lauf.

Die beiden Plays wiederholen die Repository-Aufgabe. Diese Wiederholung zeigt, dass aus diesem Aufgabenpaar eine Rolle entstanden ist. Der Unterschied zwischen einem Playbook und einer Rolle erläutert, wann Sie diesen Schritt durchführen sollten und welche Kosten damit verbunden sind.

Die Agenten lauschen auf Port 10050, und der Server lauscht auf Port 10051. Beide verwenden TCP (transmission control protocol). Öffnen Sie Port 10050 auf jedem Agenten ausschließlich für die Serveradresse und Port 10051 auf dem Server ausschließlich für die Adressen der Agenten. Grundlegende ufw-Regeln auf einem VPS enthält die entsprechende Syntax.

Playbooks ausführen und die erwartete Ausgabe prüfen

ansible-playbook zabbix-server.yml --syntax-check
ansible-playbook zabbix-server.yml
ansible-playbook zabbix-agents.yml

Führen Sie ansible-playbook zabbix-server.yml ein zweites Mal aus. Die Play-Zusammenfassung sollte mit changed=0 enden. Wenn eine Aufgabe weiterhin eine Änderung meldet, müssen Sie diese Aufgabe korrigieren. Im Abschnitt oben sind die üblichen Ursachen aufgeführt.

Prüfen Sie auf dem Server den Daemon und sein Log:

systemctl is-active zabbix-server
sudo tail -n 20 /var/log/zabbix/zabbix_server.log
sudo ss -lntp | grep 10051

Ein fehlerfreies Log beginnt mit Starting Zabbix Server. Zabbix 7.0.x, führt die aktivierten Funktionen auf und bleibt anschließend ruhig. ss sollte einen Prozess anzeigen, der auf 10051 lauscht. Fragen Sie vom Server aus einen Agenten, ob er erreichbar ist:

sudo apt install -y zabbix-get
zabbix_get -s 203.0.113.21 -k agent.ping

Die Antwort lautet 1. Die Antwort zabbix_get [1234]: Check access restrictions in Zabbix agent configuration bedeutet, dass der Agent ausgeführt wird und erreichbar ist. In seiner Zeile Server= ist jedoch nicht die Adresse aufgeführt, von der Ihre Anfrage stammt.

Fehlerbilder und die genauen Fehlermeldungen

No package matching 'zabbix-server-mysql' is available. Der apt-Cache kennt das Zabbix-Repository nicht. So sieht ein fehlender Cache-Refresh aus. Mit no-refresh-demo.yml können Sie dies absichtlich auslösen und beobachten:

- name: Show what a missing cache refresh looks like
  hosts: zabbix_server
  become: true
  tasks:
    - name: Install the server package without refreshing the cache
      ansible.builtin.apt:
        name: zabbix-server-mysql
        state: present
        update_cache: false

Das passiert auch im normalen Betrieb, wenn jemand zabbix-release manuell installiert hat: Die Repository-Aufgabe meldet dann ok, der Cache wird nicht aktualisiert, und die nächste Aufgabe findet das Paket nicht. Führen Sie auf dem Host sudo apt update aus und versuchen Sie es erneut.

Eine fehlende python3-debian-Bibliothek. ansible.builtin.deb822_repository ist die moderne Methode zum Erstellen einer Sources-Datei. Dafür wird das Paket python3-debian auf dem Zielhost benötigt. Ein frisches Ubuntu-24.04-Image enthält es nicht. Installieren Sie das Paket entweder in einer früheren Aufgabe oder verwenden Sie wie oben das zabbix-release-Deb-Paket. Dafür wird außer apt nichts benötigt.

A MySQL module is required: for Python 3.x, PyMySQL. Die Sammlung community.mysql befindet sich auf Ihrem Controller. Die von ihr importierte Bibliothek muss jedoch auf dem Zielhost installiert sein. Installieren Sie python3-pymysql auf dem Zielhost.

ERROR 1419 (HY000) beim Import. Das binäre Logging ist aktiviert, und das Schema erstellt Funktionen. Die Aufgabe zum Deaktivieren wurde nicht ausgeführt. Meist wurde der Import aus dem Playbook entfernt und manuell ausgeführt.

[Z3005] query failed: [1146] Table 'zabbix.config' doesn't exist in zabbix_server.log. Die Datenbank ist vorhanden, das Schema wurde jedoch nie importiert. Prüfen Sie die Guard-Bedingung manuell: SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='zabbix' sollte einige hundert zurückgeben. Wenn zcat stattdessen No such file or directory meldet, fehlt das Paket zabbix-sql-scripts. dpkg -L zabbix-sql-scripts | grep server.sql gibt den Pfad aus, den Ihre Version verwendet.

[Z3001] connection to database 'zabbix' failed: [1045] Access denied for user 'zabbix'@'localhost' (using password: YES). Das Passwort in der Vorlage stimmt nicht mit dem von MySQL gespeicherten Passwort überein. Wegen update_password: on_create ändert eine Änderung des Vault-Werts den vorhandenen MySQL-Benutzer absichtlich nicht. Ändern Sie das Passwort an beiden Stellen: sudo mysql -e "ALTER USER 'zabbix'@'localhost' IDENTIFIED BY 'the new password';". Führen Sie anschließend das Playbook erneut aus, damit die Vorlage aktualisiert wird.

ERROR! Decryption failed (no vault secrets would found that could decrypt) on /home/you/zabbix/group_vars/zabbix_server/vault.yml. Die Vault-Passwortdatei ist vorhanden, enthält jedoch das falsche Passwort. ERROR! Attempting to decrypt but no vault secrets found ist der andere Fall: Es gibt überhaupt kein Vault-Passwort. Das bedeutet fast immer, dass Sie das Playbook außerhalb des Projektverzeichnisses ausgeführt haben.

FAQ

Warum ignoriert Ansible mein verschlüsseltes zabbix_db_password?

Weil eine Variable mit höherer Priorität denselben Variablennamen definiert. Ein vars:-Block im Play hat Vorrang vor jeder group_vars-Datei. -e in der Befehlszeile hat wiederum Vorrang davor. Ein an einer dieser Stellen verbliebener Platzhalter überschreibt daher die Vault-Datei, ohne dass eine Warnung ausgegeben wird. Legen Sie den Standardwert in group_vars/all.yml und den tatsächlichen Wert in group_vars/<group>/vault.yml ab. Im Play darf kein vars:-Block vorhanden sein. Prüfen Sie den Wert mit einer Task, die {{ zabbix_db_password | hash('sha1') }} ausgibt, und vergleichen Sie ihn mit printf %s 'your-password' | sha1sum.

Warum meldet das Playbook Änderungen auf einem Host, der bereits eingerichtet ist?

Dafür sind fast immer drei Tasks verantwortlich. Eine community.mysql.mysql_user-Task ohne update_password: on_create setzt das Passwort bei jedem Durchlauf erneut, weil MySQL einen gesalzenen Hash speichert, den das Modul nicht mit Ihrem Klartextpasswort vergleichen kann. Eine eigenständige apt-Task, die nur den Cache aktualisiert, meldet bei jedem Durchlauf eine Änderung. Führen Sie die Aktualisierung daher in die Installations-Task ein und machen Sie sie von der Repository-Task abhängig. Eine Abfrage-Task meldet ebenfalls eine Änderung, sofern Sie changed_when: false nicht hinzufügen, weil Ansible einen SELECT nicht von einem UPDATE unterscheiden kann.

Wo befindet sich das Zabbix-Webfrontend in diesem Playbook?

Es wurde absichtlich nicht aufgenommen. Dieses Playbook installiert zabbix-server-mysql und zabbix-sql-scripts. Dabei handelt es sich um den Server-Daemon und sein Schema. Das Frontend besteht aus einem separaten Paketsatz sowie einem Webserver und einem PHP-Pool. Es sollte üblicherweise auf einem eigenen Host oder hinter einem eigenen Reverse Proxy betrieben werden. Fügen Sie in einem dritten Play zabbix-frontend-php mit zabbix-nginx-conf hinzu, sobald der Server auf Port 10051 lauscht.

Wie ändere ich das Zabbix-Datenbankpasswort später?

An zwei Stellen, weil update_password: on_create einen vorhandenen Benutzer absichtlich nicht verändert. Führen Sie ansible-vault edit group_vars/zabbix_server/vault.yml aus und setzen Sie den neuen Wert. Führen Sie anschließend sudo mysql -e "ALTER USER 'zabbix'@'localhost' IDENTIFIED BY 'the new password';" auf dem Datenbankhost aus. Führen Sie das Playbook erneut aus. Die Vorlage schreibt den neuen DBPassword, und der Handler startet den Server neu. Überwachen Sie anschließend /var/log/zabbix/zabbix_server.log auf [Z3001]. Dabei handelt es sich um die Zeile, die erscheint, wenn die beiden Werte nicht mehr übereinstimmen.