====== 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 [[wiki:cluster:depot|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 donnent lieu à des peer authentication fail. Pour contourner le problème, on utilise sudo -u .\\ 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.