ajoute la supervision : règles d'alerte Prometheus, relais ntfy, Uptime Kuma
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
d21c6ef544
commit
aa31b6112f
12 changed files with 432 additions and 0 deletions
|
|
@ -36,6 +36,19 @@ quit
|
|||
"
|
||||
logger -t sauvegarde-dedibackup "Quotidienne envoyée : $ARCHIVE_QUOT"
|
||||
|
||||
# Métrique pour l'alerte Prometheus "SauvegardeAncienne" (étape 7) : écrite
|
||||
# seulement une fois la sauvegarde confirmée envoyée, de façon atomique
|
||||
# (fichier temporaire puis renommage) pour que node_exporter ne lise jamais
|
||||
# un fichier à moitié écrit.
|
||||
DOSSIER_TEXTFILE="{{ sauvegardes_dossier_textfile | default('/var/lib/node_exporter/textfile') }}"
|
||||
mkdir -p "$DOSSIER_TEXTFILE"
|
||||
cat > "$DOSSIER_TEXTFILE/sauvegarde.prom.tmp" << METRIQUE
|
||||
# HELP syllabus_derniere_sauvegarde_reussie_timestamp Horodatage Unix de la dernière sauvegarde Dedibackup réussie.
|
||||
# TYPE syllabus_derniere_sauvegarde_reussie_timestamp gauge
|
||||
syllabus_derniere_sauvegarde_reussie_timestamp $(date +%s)
|
||||
METRIQUE
|
||||
mv "$DOSSIER_TEXTFILE/sauvegarde.prom.tmp" "$DOSSIER_TEXTFILE/sauvegarde.prom"
|
||||
|
||||
if [ "$DOW" = "{{ sauvegardes_jour_hebdomadaire }}" ]; then
|
||||
ARCHIVE_HEBDO="hebdomadaire-${SEMAINE}.tar.zst.age"
|
||||
cp "$WORKDIR/$ARCHIVE_QUOT" "$WORKDIR/$ARCHIVE_HEBDO"
|
||||
|
|
|
|||
5
ansible/roles/supervision/defaults/main.yml
Normal file
5
ansible/roles/supervision/defaults/main.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
supervision_fichier_webhook: /root/.alertes-webhook
|
||||
supervision_port_relais: 9099
|
||||
supervision_port_uptime_kuma: 3001
|
||||
supervision_dossier_textfile: /var/lib/node_exporter/textfile
|
||||
10
ansible/roles/supervision/handlers/main.yml
Normal file
10
ansible/roles/supervision/handlers/main.yml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
- name: recharger systemd
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: redémarrer le relais alertmanager-ntfy
|
||||
ansible.builtin.systemd:
|
||||
name: alertmanager-ntfy
|
||||
state: restarted
|
||||
enabled: true
|
||||
100
ansible/roles/supervision/tasks/main.yml
Normal file
100
ansible/roles/supervision/tasks/main.yml
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
---
|
||||
- name: vérifier la présence du fichier webhook (jamais son contenu)
|
||||
ansible.builtin.stat:
|
||||
path: "{{ supervision_fichier_webhook }}"
|
||||
register: supervision_webhook_stat
|
||||
|
||||
- name: échouer clairement si le webhook est absent
|
||||
ansible.builtin.fail:
|
||||
msg: >
|
||||
{{ supervision_fichier_webhook }} est absent. Adam doit le déposer
|
||||
lui-même (chmod 600) avant de continuer : c'est un secret, Claude ne le
|
||||
demande jamais en clair.
|
||||
when: not supervision_webhook_stat.stat.exists
|
||||
|
||||
- name: vérifier les permissions du fichier webhook
|
||||
ansible.builtin.fail:
|
||||
msg: "{{ supervision_fichier_webhook }} ne devrait pas être lisible par d'autres utilisateurs (mode actuel {{ supervision_webhook_stat.stat.mode }})."
|
||||
when:
|
||||
- supervision_webhook_stat.stat.exists
|
||||
- supervision_webhook_stat.stat.mode != '0600'
|
||||
|
||||
- name: créer le dossier des métriques textfile (node_exporter)
|
||||
ansible.builtin.file:
|
||||
path: "{{ supervision_dossier_textfile }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: créer le dossier de configuration Prometheus personnalisée
|
||||
ansible.builtin.file:
|
||||
path: /etc/kolla/config/prometheus
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
|
||||
- name: déployer les règles d'alerte Prometheus
|
||||
ansible.builtin.copy:
|
||||
src: "{{ playbook_dir }}/../kolla/config/prometheus/alertes.rules"
|
||||
dest: /etc/kolla/config/prometheus/alertes.rules
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0640"
|
||||
register: supervision_regles_alerte
|
||||
|
||||
- name: déployer la configuration Alertmanager
|
||||
ansible.builtin.copy:
|
||||
src: "{{ playbook_dir }}/../kolla/config/prometheus/prometheus-alertmanager.yml"
|
||||
dest: /etc/kolla/config/prometheus/prometheus-alertmanager.yml
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0640"
|
||||
register: supervision_config_alertmanager
|
||||
|
||||
- name: déployer le relais Alertmanager -> ntfy
|
||||
ansible.builtin.template:
|
||||
src: alertmanager-ntfy.py.j2
|
||||
dest: /usr/local/sbin/alertmanager-ntfy.py
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
notify: redémarrer le relais alertmanager-ntfy
|
||||
|
||||
- name: déployer le service systemd du relais
|
||||
ansible.builtin.template:
|
||||
src: alertmanager-ntfy.service.j2
|
||||
dest: /etc/systemd/system/alertmanager-ntfy.service
|
||||
mode: "0644"
|
||||
notify:
|
||||
- recharger systemd
|
||||
- redémarrer le relais alertmanager-ntfy
|
||||
|
||||
- name: appliquer les changements du relais
|
||||
ansible.builtin.meta: flush_handlers
|
||||
|
||||
- name: vérifier que le relais tourne
|
||||
ansible.builtin.systemd:
|
||||
name: alertmanager-ntfy
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: déployer Uptime Kuma
|
||||
community.docker.docker_container:
|
||||
name: uptime-kuma
|
||||
image: louislam/uptime-kuma:1
|
||||
state: started
|
||||
restart_policy: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:{{ supervision_port_uptime_kuma }}:3001"
|
||||
volumes:
|
||||
- uptime-kuma-data:/app/data
|
||||
|
||||
- name: afficher s'il faut relancer Kolla pour les nouvelles règles
|
||||
ansible.builtin.debug:
|
||||
msg: >
|
||||
Config Prometheus/Alertmanager modifiée : lancer
|
||||
`kolla-ansible reconfigure -i /etc/kolla/all-in-one --tags prometheus`
|
||||
sur le serveur (voir docs/runbooks/supervision.md).
|
||||
when: supervision_regles_alerte.changed or supervision_config_alertmanager.changed
|
||||
72
ansible/roles/supervision/templates/alertmanager-ntfy.py.j2
Normal file
72
ansible/roles/supervision/templates/alertmanager-ntfy.py.j2
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Relais local : reçoit le webhook JSON d'Alertmanager, le transforme en
|
||||
notification ntfy. Géré par Ansible (rôle supervision) — ne pas modifier à la
|
||||
main. N'écoute que sur 127.0.0.1 : jamais exposé en dehors du serveur.
|
||||
|
||||
L'URL ntfy (avec le topic) est un secret : lue depuis {{ supervision_fichier_webhook }}
|
||||
à chaque notification, jamais mise en cache, jamais journalisée.
|
||||
"""
|
||||
import http.server
|
||||
import json
|
||||
import urllib.request
|
||||
|
||||
FICHIER_WEBHOOK = "{{ supervision_fichier_webhook }}"
|
||||
ADRESSE = ("127.0.0.1", {{ supervision_port_relais }})
|
||||
|
||||
|
||||
def lire_url_ntfy():
|
||||
with open(FICHIER_WEBHOOK, encoding="utf-8") as f:
|
||||
return f.read().strip()
|
||||
|
||||
|
||||
class Gestionnaire(http.server.BaseHTTPRequestHandler):
|
||||
def do_POST(self):
|
||||
longueur = int(self.headers.get("Content-Length", 0))
|
||||
corps = self.rfile.read(longueur)
|
||||
try:
|
||||
charge = json.loads(corps)
|
||||
except ValueError:
|
||||
self.send_response(400)
|
||||
self.end_headers()
|
||||
return
|
||||
|
||||
try:
|
||||
url_ntfy = lire_url_ntfy()
|
||||
except OSError:
|
||||
self.send_response(500)
|
||||
self.end_headers()
|
||||
return
|
||||
|
||||
for alerte in charge.get("alerts", []):
|
||||
statut = alerte.get("status", "inconnu")
|
||||
nom = alerte.get("labels", {}).get("alertname", "?")
|
||||
resume = (
|
||||
alerte.get("annotations", {}).get("description")
|
||||
or alerte.get("annotations", {}).get("summary")
|
||||
or nom
|
||||
)
|
||||
titre = f"[{statut.upper()}] {nom}"
|
||||
requete = urllib.request.Request(
|
||||
url_ntfy,
|
||||
data=resume.encode("utf-8"),
|
||||
method="POST",
|
||||
headers={
|
||||
"Title": titre,
|
||||
"Priority": "high" if statut == "firing" else "default",
|
||||
},
|
||||
)
|
||||
try:
|
||||
urllib.request.urlopen(requete, timeout=5)
|
||||
except Exception:
|
||||
pass # ne bloque jamais Alertmanager ; vérifier manuellement en cas de doute
|
||||
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
||||
def log_message(self, format, *args): # silencieux : rien de secret à tracer non plus
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
serveur = http.server.HTTPServer(ADRESSE, Gestionnaire)
|
||||
serveur.serve_forever()
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
[Unit]
|
||||
Description=Relais Alertmanager -> ntfy (Syllabus)
|
||||
# Géré par Ansible (rôle supervision) — ne pas modifier à la main.
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/python3 /usr/local/sbin/alertmanager-ntfy.py
|
||||
Restart=on-failure
|
||||
# Durcissement minimal : ce service lit un secret (fichier webhook), pas de
|
||||
# raison qu'il écrive où que ce soit ou touche au reste du système.
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
PrivateTmp=true
|
||||
ReadOnlyPaths=/root/.alertes-webhook
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
8
ansible/supervision.yml
Normal file
8
ansible/supervision.yml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
# Étape 7 du déploiement Syllabus : supervision et alertes.
|
||||
- name: Supervision et alertes
|
||||
hosts: syllabus
|
||||
become: true
|
||||
gather_facts: true
|
||||
roles:
|
||||
- supervision
|
||||
Loading…
Add table
Add a link
Reference in a new issue