Grafana¶
Rôle¶
Visualisation et tableaux de bord pour la supervision de l'infrastructure Indio, en complément de Zabbix. Installé et configuré sur INDSUPV003 via un rôle Ansible dédié (/root/grafana/ansible/).
Correction par rapport à une version précédente de cette page
Une version antérieure de cette page affirmait qu'« aucune configuration Ansible n'existe dans ce dépôt » et que les datasources n'étaient « pas confirmées ». C'était faux/obsolète : /root/grafana/ansible/site.yml existe et déploie réellement le paquet, le TLS, la datasource Zabbix et des dashboards — détaillé ci-dessous.
Architecture¶
- 1 nœud Rocky Linux 9.6 (clone de
tmpl-rocky96-hardened) :INDSUPV003, IP10.100.2.228, segmentseg-supervision(10.100.2.224/28). - 1 vCPU / 2048 Mo RAM — dimensionnement minimal, cohérent avec un rôle de frontend de visualisation sans stockage de métriques propre (toutes les données viennent de l'API Zabbix).
- Grafana sert HTTPS directement via son serveur intégré, sur le port 3000 — pas de reverse proxy local (httpd/nginx) ni de VIP HAProxy partagée, même exception que Kibana (voir HAProxy) pour un service qui gère déjà nativement son propre TLS.
- Certificat TLS émis par la CA interne Vault PKI (
pki_int/roles/infra-indio, voir Vault PKI), CNdashboards.infra.indio— fichiers statiques déployés depuis le dépôt (pas d'appel Vault dynamique dans ce rôle, même pattern que Zabbix/GLPI/ELK). Ce nom a nécessité un enregistrement DNS créé manuellement (voir Procédure manuelle). - Datasource Zabbix branchée : plugin communautaire
alexanderzobnin-zabbix-app(v6.5.0, signé « Grafana Labs »), authentification par token API contre un compte de service Zabbix dédié en lecture seule (svc-grafana), interrogeanthttps://supervision.infra.indio/zabbix/api_jsonrpc.php. - Dashboards provisionnés (fichiers, dossier « Zabbix ») : le dashboard Zabbix Server par défaut du plugin (adapté à
INDSUPV001), sa variante dupliquée pourINDSUPV002, et un template Linux Server générique. Le 3ᵉ dashboard livré par le plugin (« Zabbix System Status ») est explicitement retiré du provisioning — structurellement incompatible avec la version du plugin installée (voir Points d'attention).
Structure du dépôt¶
| Fichier/Dossier | Contenu |
|---|---|
main.tf |
VM unique (pas de for_each, contrairement aux autres projets), clone du template, IP via NetBox |
variables.tf |
vsphere_network = "seg-supervision", vm_name = "INDSUPV003", vm_cpu = 1, vm_ram = 2048 |
versions.tf |
Providers hashicorp/vsphere (>= 2.6.0), e-breuninger/netbox (>= 5.0.0, < 6.0.0) |
ansible/site.yml |
Play unique, rôle grafana, hôte INDSUPV003 |
ansible/group_vars/grafana.yml |
grafana_fqdn, grafana_admin_password et zabbix_api_token (vaultés) |
ansible/roles/grafana/tasks/main.yml |
Dépôt Nexus, paquet, TLS, grafana.ini, plugin Zabbix, correctif permissions, datasource, dashboards, vérifications post-déploiement |
ansible/roles/grafana/files/tls/ |
Certificat/clé/CA Vault PKI (statiques) |
ansible/roles/grafana/files/dashboards/ |
4 JSON (3 provisionnés + zabbix_system_status.json gardé pour référence, non déployé) |
flowchart LR
subgraph SEGSUPV["seg-supervision — 10.100.2.224/28"]
GRAF["INDSUPV003<br/>10.100.2.228<br/>grafana-server :3000 (TLS intégré)"]
end
USER["Utilisateur<br/>https://dashboards.infra.indio:3000"] --> GRAF
GRAF -- "API JSON-RPC<br/>token svc-grafana (lecture seule)" --> ZBXAPI["Zabbix API<br/>supervision.infra.indio<br/>(INDSUPV004)"]
ZBXAPI --> ZBXSRV["Zabbix Server HA<br/>INDSUPV001 / INDSUPV002"]
ZBXSRV --> PGVIP(("VIP PostgreSQL<br/>10.100.2.195"))
VAULT["Vault PKI<br/>pki_int/roles/infra-indio"] -. "certificat statique<br/>CN=dashboards.infra.indio" .-> GRAF
Approvisionnement du paquet Grafana — rpm.grafana.com (Fastly, 151.101.0.0/16) est bloqué en sortie sur ce réseau, contournement retenu :
sequenceDiagram
participant CTRL as Nœud de contrôle (10.15.100.61)
participant API as grafana.com/api
participant DL as dl.grafana.com (Fastly, autre plage)
participant NEXUS as Nexus — dépôt grafana-rpm (hosted)
participant VM as INDSUPV003
Note over CTRL,VM: rpm.grafana.com résout en 151.101.0.0/16 -- bloqué en sortie, cause exacte non identifiée
CTRL->>API: GET /api/grafana/versions/stable
API-->>CTRL: URL du RPM + sha256
CTRL->>DL: téléchargement direct (plage Fastly différente, non bloquée)
DL-->>CTRL: grafana_<version>_linux_amd64.rpm
CTRL->>NEXUS: POST /service/rest/v1/components?repository=grafana-rpm
Note over NEXUS: régénération repodata plus lente que d'habitude (~30-40s, paquet volumineux)
VM->>NEXUS: dnf install grafana (grafana.repo, proxy=_none_)
NEXUS-->>VM: paquet grafana
Provisioning Terraform¶
/root/grafana/main.tf: VM unique (pas defor_each, contrairement aux autres projets de l'infra), clone dedata.vsphere_virtual_machine.template, IP assignée par NetBox (data.netbox_ip_addresses.vm, filtredns_nameen minuscules).lifecycle.ignore_changessurannotation,clone[0].template_uuid,clone[0].customize,disk[0].io_share_count(pattern standard de l'infra).- Variables (
variables.tf) :vsphere_network = "seg-supervision",vm_gateway = "10.100.2.238",vm_netmask = 28,vm_name = "INDSUPV003",vm_cpu = 1,vm_ram = 2048. - Providers :
hashicorp/vsphere(>= 2.6.0),e-breuninger/netbox(>= 5.0.0, < 6.0.0). - Identifiants vCenter/NetBox via
terraform.tfvars(non versionné) ; modèle dansterraform.tfvars.example. output "grafana_ip"expose l'IP effective attribuée.
Configuration Ansible¶
Playbook /root/grafana/ansible/site.yml, rôle unique grafana appliqué à l'hôte INDSUPV003. Étapes réelles de roles/grafana/tasks/main.yml, dans l'ordre :
- Déploie
/etc/yum.repos.d/grafana.repodepuisgrafana.repo.j2— pointe vers le dépôt Nexus hébergégrafana-rpm(https://repo.infra.infra/repository/grafana-rpm/,proxy=_none_), pas versrpm.grafana.com(voir schéma d'approvisionnement ci-dessus, cf. aussi Nexus). - Installe le paquet
grafana. - Crée
/etc/grafana/certs(grafana:grafana, mode0750). - Déploie le certificat/clé/CA Vault PKI (fichiers statiques de
files/tls/) — notifie un restart. - Déploie
grafana.inidepuisgrafana.ini.j2:[server] protocol=https,http_port=3000,domain/root_urlsur{{ grafana_fqdn }}, chemins des certificats ;[security] admin_user=admin,admin_password={{ grafana_admin_password }}(vaulté) — notifie un restart. - Ouvre le port
3000/tcpdans firewalld. - Installe le plugin communautaire
alexanderzobnin-zabbix-appviagrafana-cli --homepath /usr/share/grafana plugins install ...(idempotent viacreates:). - Tâche corrective dédiée : reprend la propriété de
/var/lib/grafana/plugins(grafana:grafana,recurse: true) —grafana-cliexécuté ensudolaisse ce dossier enroot:root, invisible au processgrafana-server(voir Points d'attention) — notifie un restart. - Déploie le provisioning de la datasource Zabbix (
zabbix-datasource.yaml.j2→/etc/grafana/provisioning/datasources/zabbix.yaml) : typealexanderzobnin-zabbix-datasource,access: proxy, URL de l'API Zabbix,authType: token,apiTokenvaulté ({{ zabbix_api_token }}),trends: true. - Crée le dossier de provisioning des dashboards (
/etc/grafana/provisioning/dashboards/zabbix). - Déploie 3 dashboards JSON (
zabbix_server_dashboard.json,zabbix_server_dashboard_indsupv002.json,template_linux_server.json). - Retire explicitement
zabbix_system_status.jsondu dossier de provisioning (state: absent) — dashboard structurellement incompatible avec le backend du plugin (voir Points d'attention) ; le JSON reste dansfiles/dashboards/pour référence uniquement. - Déploie le provider de dashboards (
dashboards-provider.yaml.j2→/etc/grafana/provisioning/dashboards/zabbix.yaml) : provider de typefile, dossier Grafana « Zabbix »,foldersFromFilesStructure: false. - Active et démarre
grafana-server, puis force l'application des handlers (meta: flush_handlers). - Attend que
GET /api/healthréponde 200 en HTTPS (12 tentatives, 5s d'intervalle). - Vérifie que la datasource Zabbix est fonctionnelle :
GET /api/datasources/uid/zabbix-datasource/healthen Basic Auth admin, avecforce_basic_auth: trueexplicite (voir Points d'attention), jusqu'àstatus == "OK".
Procédure manuelle¶
Plusieurs prérequis, réalisés une fois, ne sont couverts par aucune ressource Terraform/Ansible de ce dépôt :
- Approvisionnement du RPM Grafana dans Nexus (à refaire à chaque montée de version,
rpm.grafana.comrestant bloqué) :- Depuis un poste avec accès Internet réel (nœud de contrôle) : interroger
https://grafana.com/api/grafana/versions/stablepour obtenir l'URL du RPM et son sha256. - Télécharger depuis
dl.grafana.com(pasrpm.grafana.com) et vérifier le sha256. - Uploader dans Nexus :
POST /service/rest/v1/components?repository=grafana-rpm(assetyum.asset, dépôt yum hosted dédié — pas mélangé à un dépôt générique). - Attendre la régénération des métadonnées yum (
repodata/repomd.xml) avant de tester le dépôt — plus lente que pour un petit paquet (~30-40s observés pour ce RPM de l'ordre de 227 Mo). - Aucune modification Ansible nécessaire ensuite si
grafana.repo.j2reste inchangé :dnfprend automatiquement la dernière version présente dans le dépôt.
- Depuis un poste avec accès Internet réel (nœud de contrôle) : interroger
- Émission du certificat TLS Vault PKI :
vault write pki_int/issue/infra-indio common_name=dashboards.infra.indio ..., en récupérant le champca_chaincomplet (intermédiaire + racine), pasissuing_caseul — sinonCERTIFICATE_VERIFY_FAILED: unable to get issuer certificatecôté clients stricts. Le résultat (cert/clé/CA) est ensuite copié tel quel dansroles/grafana/files/tls/— un renouvellement futur est donc une réémission manuelle suivie d'un remplacement de fichiers, pas un mécanisme dynamique. - Enregistrement DNS : le nom
dashboards.infra.indioa été créé manuellement comme enregistrement A sur le contrôleur de domaine (INDIDEN008) — toute création de nom fonctionnel*.infra.indiosuit ce même geste manuel côté AD. - Compte de service Zabbix
svc-grafana(consommé par la datasource, étape 9 de la Configuration Ansible), créé via l'API Zabbix — aucune ressource IaC ne gère les objets Zabbix (comptes, groupes de droits, tokens) :- Groupe d'utilisateurs dédié (
hostgroup_rightsen lecture seule,permission=2, sur tous les host groups existants). - Utilisateur
svc-grafanadans ce groupe, rôle stock « User role » (pas Admin). - Génération du token API en 2 appels distincts (
token.createpuistoken.generate— le premier ne renvoie que l'identifiant, pas la valeur secrète). - La valeur obtenue est ensuite chiffrée dans
group_vars/grafana.yml(zabbix_api_token) — jamais en clair dans le dépôt.
- Groupe d'utilisateurs dédié (
Procédure de déploiement¶
terraform apply(dans/root/grafana) : cloneINDSUPV003et lui attribue son IP via NetBox.- Prérequis manuels à satisfaire avant le premier
ansible-playbook(voir Procédure manuelle ci-dessus) : RPM déjà présent dans le dépôt Nexusgrafana-rpm, certificat TLS déjà émis et placé dansroles/grafana/files/tls/, DNSdashboards.infra.indiodéjà créé, comptesvc-grafana+ token déjà générés côté Zabbix (sinon la vérification finale de l'étape 16 échoue). ansible-playbook site.yml(dans/root/grafana/ansible) : installe et configure Grafana de bout en bout. Playbook idempotent — un second run n'entraîne aucun changement.
Contrôle de santé / Vérification¶
curl -k https://dashboards.infra.indio:3000/api/health→ code 200, JSON incluant"database":"ok"et la version installée.curl -k -u admin:<mot_de_passe> https://dashboards.infra.indio:3000/api/datasources/uid/zabbix-datasource/health→{"status":"OK","message":"Zabbix API version 8.0.0"}attendu.openssl s_client -connect 10.100.2.228:3000: vérifierCN=dashboards.infra.indioet la chaîne jusqu'à l'Intermediate CA Vault.stat -c '%U:%G' /var/lib/grafana/plugins(et son contenu) : doit renvoyergrafana:grafana— régression classique après une réinstallation manuelle du plugin (voir Points d'attention).ls /etc/grafana/provisioning/dashboards/zabbix/: ne doit pas contenirzabbix_system_status.json(retiré intentionnellement).
Points d'attention¶
rpm.grafana.com bloqué en sortie — spécifique à la plage Fastly 151.101.0.0/16
Le dépôt Nexus proxy classique (remoteUrl=https://rpm.grafana.com, même pattern que Zabbix/PostgreSQL) ne fonctionne pas : la whitelist Squid est acceptée (CONNECT passe), mais la connexion time-out ensuite, car rpm.grafana.com résout uniquement sur des IP 151.101.0.0/16 (Fastly), bloquées en sortie quelque part entre la zone admin et Internet (cause exacte non identifiée, probablement le FortiGate). Ce n'est pas un blocage Fastly générique : dl.grafana.com (autre plage Fastly) répond normalement depuis le même nœud. Pour tout futur service dont le dépôt officiel est servi par Fastly, tester la plage IP réelle avant de supposer qu'une whitelist Squid suffit.
- Permissions du dossier plugins :
grafana-cliexécuté ensudocrée/var/lib/grafana/plugins/enroot:roots'il n'existait pas encore — le processgrafana-server(utilisateurgrafana) ne peut alors même pas lister ce dossier (open ... permission denieddansjournalctl -u grafana-server), le plugin reste invisible (404 Plugin not found) sans aucune erreur explicite côté installation. Le correctif doit porter sur le dossier parent/var/lib/grafana/pluginsavecrecurse: true, pas seulement le sous-dossier du plugin — tâche Ansible dédiée, idempotente, toujours suivie d'un restart. force_basic_auth: truerequis pour tout healthcheckuricontre l'API Grafana : sans cette option, le moduleansible.builtin.urin'envoieurl_username/url_passwordqu'après un défi HTTP 401 explicite du serveur — Grafana ne renvoie pas ce défi sur l'endpoint/api/datasources/.../health, donc la requête part sans authentification et boucle en 401 malgré des identifiants corrects.- Émission de certificat Vault PKI : utiliser le champ
ca_chain(intermédiaire + racine), jamaisissuing_caseul — piège potentiellement présent (non vérifié) sur les autres rôles*_tlsde l'infra (Zabbix, GLPI, ELK) qui utilisent des certificats émis manuellement en amont ; personne n'a testévalidate_certs: truedessus à ce jour. - Dashboard « Zabbix System Status » retiré, pas un faux négatif : confirmé au niveau du code source du plugin (
alexanderzobnin/grafana-zabbix, pipelinequeryData) — seuls les modesMODE_METRICSetMODE_ITEMIDsont supportés par les panels Grafana core (table/stat) ; le modeMODE_TRIGGERSutilisé par ce dashboard tombe systématiquement en erreur (ErrNonMetricQueryNotSupported). Incompatibilité structurelle avec la version du plugin installée (6.5.0), pas un problème de configuration réparable sans le panel custom dédié du plugin (non utilisé par ce dashboard exporté). - 3 panels vides sur le dashboard dédié à INDSUPV002 (Queue, NVPS, processus Zabbix) : leurs items de self-monitoring (
Zabbix server: Queue, etc.) n'existent que surINDSUPV001— le template applicatif Zabbix correspondant n'a jamais été attaché au 2ᵉ nœud lors du bootstrap du cluster HA. Correction possible (attacher le bon template côté Zabbix) mais non faite — décision de supervision hors périmètre d'un provisioning de dashboard. - Dimensionnement minimal (1 vCPU / 2 Go) : Grafana ne stocke aucune métrique lui-même (tout vient de l'API Zabbix) — à revoir si le nombre de dashboards/utilisateurs simultanés augmente sensiblement.
- Secrets :
grafana_admin_passwordetzabbix_api_tokenchiffrés via Ansible Vault dansgroup_vars/grafana.yml; clé privée TLS statique dansroles/grafana/files/tls/(pas d'émission dynamique au déploiement) — voir gestion des secrets.