WikiMiNET

La documentation technique et administrative

Outils pour utilisateurs

Outils du site


wiki:monitoring:zabbix:zabbix3

Zabbix3

Overview

Le serveur zabbix3 est à l'adresse 192.168.102.65 et est séparé de la base de données PostgreSQL, à l'adresse 192.168.102.66. Pour l'instant, l'interface web est disponible derrière un vpn prod, à 192.168.102.65/zabbix.

Pour l'instant, aucune configuration des items, triggers et template n'est faite. Elle sera faite pas à pas, en monitorant les machines de la prod une par une.
La base de données est partitionnée, mais toutes les partitions sont stockées en local sur le noeud. Il sera possible plus tard de séparer physiquement les partitions.

Installation

Zabbix 3.2.4 a été installé en prod fin mars 2017, en suivant la documentation officielle zabbix: https://www.zabbix.com/documentation/3.2/manual/installation.

Plusieurs choses sont à noter pour l'installation:

1. Bien suivre l'étape “Passer par un proxy” de la page des dépôts MiNET.

2. Les CT sont bien synchronisés avec le ntp, mais un date donne l'heure UTC au lieu de CEST. Il fallait donc changer la timezone:

cp /usr/share/zoneinfo/Europe/Paris /etc/localtime

3. L'encodage, le collationnement et le type de caractères de la bdd doivent être en UTF-8 et fr_FR.UTF-8. Il faut le faire à la création de la base de données:

sudo -u zabbix createdb zabbixbdd --encoding=UTF-8 --locale=fr_FR.UTF-8 --template=template0

Lors de la création de la bdd, les commandes psql -U <username> donnent lieu à des peer authentication fail. Pour contourner le problème, on utilise sudo -u <username>.

4. Lors de la modification du fichier zabbix_server.conf, il ne faut pas renseigner le champ DBSchema (même si on a déjà crée un schema pour la bdd), sinon la connexion à la base de données à partir du frontend php échouera systématiquement.

5. Avant la configuration du frontend php, il faut:

apt install php5-pgsql

sinon on ne peut pas choisir une base de données PostgreSQL au cours de la configuration.

Partitionnement de la base de données

Chaque jour, 5 partitions sont créées, pour les tables history, history_uint, history_log, history_text et history_str. Ce sont les tables les plus grosses de la bdd, et leur partitionnement permet d'accélérer les queries, quelle que soit la taille totale de la bdd.

Ces données ne sont gardées que quelques semaines, c'est pourquoi un tel partitionnement facilite le nettoyage de la bdd: il suffit de supprimer les partitions vieilles de plus de X jours. De plus, 2 autres partitions sont crées chaque mois pour les tables trends et trends_uint. Elles permettent de sauvegarder l'évolution des données, et d'avoir ainsi un historique de données de plusieurs mois, sans avoir besoin de stocker les donner exactes des tables history pendant plusieurs mois.

Voici comment la créer la fonction qui va créer les partitions:

sudo -u zabbix psql zabbixbdd
CREATE OR REPLACE FUNCTION trigger_partition()
  RETURNS trigger AS
$BODY$
DECLARE
prefix text := 'partitions.';
timeformat text;
selector text;
_interval interval;
tablename text;
startdate text;
enddate text;
create_table_part text;
create_index_part text;
BEGIN
 
selector = TG_ARGV[0];
 
IF selector = 'day' THEN
timeformat := 'DD_MM_YYYY';
ELSIF selector = 'month' THEN
timeformat := 'MM_YYYY';
END IF;
 
_interval := '1 ' || selector;
tablename :=  TG_TABLE_NAME || '_' || to_char(to_timestamp(NEW.clock), timeformat);
 
EXECUTE 'INSERT INTO ' || prefix || quote_ident(tablename) || ' SELECT ($1).*' USING NEW;
RETURN NULL;
 
EXCEPTION
WHEN undefined_table THEN
 
startdate := extract(epoch FROM date_trunc(selector, to_timestamp(NEW.clock)));
enddate := extract(epoch FROM date_trunc(selector, to_timestamp(NEW.clock) + _interval ));
 
create_table_part:= 'CREATE TABLE IF NOT EXISTS '|| prefix || quote_ident(tablename) || ' (CHECK ((clock >= ' || 
quote_literal(startdate) || ' AND clock < ' || quote_literal(enddate) || '))) INHERITS ('|| TG_TABLE_NAME || ')';
create_index_part:= 'CREATE INDEX '|| quote_ident(tablename) || '_1 on ' || prefix || quote_ident(tablename) || 
'(itemid,clock)';
 
EXECUTE create_table_part;
EXECUTE create_index_part;
 
EXECUTE 'INSERT INTO ' || prefix || quote_ident(tablename) || ' SELECT ($1).*' USING NEW;
RETURN NULL;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION trigger_partition()
  OWNER TO zabbix;

Ce que la fonction fait (en gros): elle prend en argument l'intervalle que vous lui rentrez (day ou month) et essaye d'INSERT le row qui vient d'arriver dans la bdd dans la partition dont la date correspond à celle du row.
Si la partition est déjà créée, le row y est inséré normalement, sinon la partition nécessaire est créée sous la forme partitions.nom_de_la_table_JOUR_MOIS_ANNEE pour les tables que l'on veut partitionner quotidiennement, ou partitions.nom_de_la_table_MOIS_ANNEE pour celles que l'on veut partitionner mensuellement.
Dans PostgreSQL, les partitions doivent hériter d'une table mère. Ici, chaque partition hérite de la table du même nom (history_log ou trends par exemple), qui est initialisée au cours de l'installation de la base de données.

Ensuite il faut créer les triggers qui vont appeler la fonction précédente, pour chaque table:

CREATE TRIGGER partition_trigger BEFORE INSERT ON history           FOR EACH ROW EXECUTE PROCEDURE trigger_partition('day');
CREATE TRIGGER partition_trigger BEFORE INSERT ON history_uint      FOR EACH ROW EXECUTE PROCEDURE trigger_partition('day');
CREATE TRIGGER partition_trigger BEFORE INSERT ON history_str       FOR EACH ROW EXECUTE PROCEDURE trigger_partition('day');
CREATE TRIGGER partition_trigger BEFORE INSERT ON history_text      FOR EACH ROW EXECUTE PROCEDURE trigger_partition('day');
CREATE TRIGGER partition_trigger BEFORE INSERT ON history_log       FOR EACH ROW EXECUTE PROCEDURE trigger_partition('day');
CREATE TRIGGER partition_trigger BEFORE INSERT ON trends            FOR EACH ROW EXECUTE PROCEDURE trigger_partition('month');
CREATE TRIGGER partition_trigger BEFORE INSERT ON trends_uint       FOR EACH ROW EXECUTE PROCEDURE trigger_partition('month');

Ainsi, la fonction précédente est appelée à chaque row qui est rajouté à la bdd, le partitionnement se fait donc automatiquement.

Pour le désactiver, il faut désactiver tous ces triggers :

DROP TRIGGER partition_trigger ON history;
DROP TRIGGER partition_trigger ON history_uint;
DROP TRIGGER partition_trigger ON history_str;
DROP TRIGGER partition_trigger ON history_text;
DROP TRIGGER partition_trigger ON history_log;
DROP TRIGGER partition_trigger ON trends;
DROP TRIGGER partition_trigger ON trends_uint;

Il reste encore à voir comment supprimer les partitions datant de plus d'un certain temps. On peut utiliser un trigger SQL comme celui qui créée les partitions, ou bien juste créer la fonction SQL, et faire 2 cronjobs qui l'appellent: un qui tourne tous les jours, et l'autre tous les mois.

La procédure pour mettre en place l'auto partitionnement vient du wiki zabbix2: https://www.zabbix.org/wiki/Docs/howto/zabbix2_postgresql_autopartitioning

Comment maintenir la base de données

Lister les partitions

EXPLAIN SELECT * FROM 'parent_table';

On peut par exemple remplacer 'parent_table' par 'history' ou 'history_uint' qui sont les tables les plus remplies.

Sinon on peut lister toutes les partitions d'un coup:

SELECT * FROM pg_tables WHERE schemaname = 'partitions';

Supprimer les partitions trop anciennes

On créée deux fonctions qui permettront de supprimer facilement les partitions trop vieilles. Une pour les partitions créées quotidiennement:

CREATE OR REPLACE FUNCTION public.delete_partitions_daily(intervaltodelete interval)
 RETURNS text
 LANGUAGE plpgsql
AS $function$
DECLARE
result record ;
prefix text := 'partitions.';
table_timestamp timestamp;
delete_before_date date;
tablename text;

BEGIN
    FOR result IN SELECT * FROM pg_tables WHERE schemaname = 'partitions' LOOP

        table_timestamp := to_timestamp(substring(result.tablename from '[0-9_]*$'), '_DD_MM_YYYY');
        --raise notice 'Value: %',table_timestamp;
        --raise notice 'Value %', tablename;
        delete_before_date := date_trunc('day', NOW() - intervalToDelete);
        tablename := result.tablename;

    --Check whether the table name has a day (YYYY_MM_DD) or month (YYYY_MM) format
        IF length(substring(result.tablename from '[0-9_]*$')) = 11 THEN
            --This is a daily partition _DD_MM_YYYY
            --RAISE NOTICE 'Skipping table % when trying to delete "%" partitions (%)', result.tablename, tabletype, length(substring(result.tablename from '[0-9_]*$'));
            IF table_timestamp <= delete_before_date THEN
                RAISE NOTICE 'Deleting table %', quote_ident(tablename);
                EXECUTE 'DROP TABLE ' || prefix || quote_ident(tablename) || ';';
            END IF;
        END IF;

    END LOOP;
RETURN 'OK';

END;

$function$

Et une pour les partitions créées mensuellement:

CREATE OR REPLACE FUNCTION public.delete_partitions_monthly(intervaltodelete interval)
 RETURNS text
 LANGUAGE plpgsql
AS $function$
DECLARE
result record ;
prefix text := 'partitions.';
table_timestamp timestamp;
delete_before_date date;
tablename text;

BEGIN
    FOR result IN SELECT * FROM pg_tables WHERE schemaname = 'partitions' LOOP

        table_timestamp := to_timestamp(substring(result.tablename from '[0-9_]*$'), '_DD_MM_YYYY');
        --raise notice 'Value: %',table_timestamp;
        --raise notice 'Value %', tablename;
        delete_before_date := date_trunc('day', NOW() - intervalToDelete);
        tablename := result.tablename;

    --Check whether the table name has a day (YYYY_MM_DD) or month (YYYY_MM) format
        IF length(substring(result.tablename from '[0-9_]*$')) = 7 THEN
            --This is a daily partition _DD_MM_YYYY
            --RAISE NOTICE 'Skipping table % when trying to delete "%" partitions (%)', result.tablename, tabletype, length(substring(result.tablename from '[0-9_]*$'));
            IF table_timestamp <= delete_before_date THEN
                RAISE NOTICE 'Deleting table %', quote_ident(tablename);
                EXECUTE 'DROP TABLE ' || prefix || quote_ident(tablename) || ';';
            END IF;
        END IF;

    END LOOP;
RETURN 'OK';

END;

$function$

Deux cronjob run les deux fonctions précédentes avec 7 jours et 3 mois de délai avant suppression. Les gros gros scripts sql très complexes utilisés sont dans /opt. S'il faut un jour run les fonctions manuellement, voici les commandes à rentrer dans postgresql:

SELECT delete_partitions_daily('7 days');
SELECT delete_partitions_monthly('12 months');

Les trucs et astuces de configuration

Les discovery rules

Vous l'aurez vite remarqué, ajouter des hosts à Zabbix peut être extrêmement laborieux. Mais heureusement, une fonctionnalité existe pour vous simplifier la vie : les discovery rules.

Comment ça marche ? Il faut aller dans l'onglet configuration de Zabbix, puis dans le sous-onglet “Discovery”. Là vous devriez déjà voir listés “Local Network” et “Production & Développement”. Pour faire court, si tout ce qui vous intéresse est de faire monitorer votre machine sur les cluster de production et de développement par Zabbix, il vous suffit de rentrer l'adresse IP du serveur Zabbix 3 dans le fichier de configuration de votre agent Zabbix et…c'est tout. En effet la discovery rule ordonne à Zabbix de scanner les plages d'IPs données à la recherche d'un agent Zabbix, en les discriminant sur la base de leur IP.

Si vous voulez monitorer une plage d'IP différente, vous pouvez l'ajouter à la règle, en la séparant d'une virgule.

Et si vous avez besoin, pour des raisons spécifiques, d'en créer une nouvelle, cliquez sur le bouton en haut à droite. Vous aurez besoin d'inclure un “check”. Une idée de check simple est de choisir la clé “system.uname” qui vérifiera le nom de l'OS de la machine monitorée (fonctionne avec les VMs).

Les Actions

Zabbix ne permet pas que de vous spammer de mails à longueur de journée. Il a aussi des fonctionnalités utiles qui permettent d'automatiser certains processus, les actions.

Qu'est-ce qu'une action ? C'est l'association de plusieurs conditions et de plusieurs opérations. Tout d'abord, avant même de créer votre action, choisissez le type de votre action dans le sous-onglet “Actions” de l'onglet “Configuration”. En effet, vous devez être dans la bonne catégorie de listing des actions pour créer une action en rapport (cf. menu déroulant “Event source” en haut à droite).

Une fois sur la bonne catégorie, vous pouvez créer une nouvelle action. Les conditions et opérations disponibles changent selon la catégorie d'action dans laquelle vous êtes. Vous n'avez plus qu'à remplir les deux onglets, et voilà, Zabbix fait tout pour vous.

wiki/monitoring/zabbix/zabbix3.txt · Dernière modification: 2020/06/27 18:16 (modification externe)