Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a62cebc92 |
+1
-264
@@ -69,26 +69,13 @@ grafana: &grafana_config
|
|||||||
path: /api/health
|
path: /api/health
|
||||||
port: 3000
|
port: 3000
|
||||||
|
|
||||||
# Base Grafana en SQLite sur emptyDir → migration complète du schéma à CHAQUE démarrage du
|
|
||||||
# pod, lente sur Raspberry Pi (> 160 s). Sans garde suffisante, la liveness tuait Grafana en
|
|
||||||
# pleine migration → CrashLoop au moindre rollout. startupProbe : ~10 min avant d'armer la
|
|
||||||
# liveness. failureThreshold de liveness relevé aussi (filet de sécurité si le chart n'expose
|
|
||||||
# pas startupProbe). Fix pérenne : DB persistante (PVC) ou externe (postgres) — hors scope ici.
|
|
||||||
startupProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /api/health
|
|
||||||
port: 3000
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 10
|
|
||||||
failureThreshold: 60
|
|
||||||
|
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /api/health
|
path: /api/health
|
||||||
port: 3000
|
port: 3000
|
||||||
initialDelaySeconds: 60
|
initialDelaySeconds: 60
|
||||||
timeoutSeconds: 30
|
timeoutSeconds: 30
|
||||||
failureThreshold: 60
|
failureThreshold: 10
|
||||||
|
|
||||||
## Use an alternate scheduler, e.g. "stork".
|
## Use an alternate scheduler, e.g. "stork".
|
||||||
## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/
|
## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/
|
||||||
@@ -716,14 +703,6 @@ grafana: &grafana_config
|
|||||||
editable: true
|
editable: true
|
||||||
options:
|
options:
|
||||||
path: /var/lib/grafana/dashboards/grafana-dashboards-kubernetes
|
path: /var/lib/grafana/dashboards/grafana-dashboards-kubernetes
|
||||||
- name: 'prospection'
|
|
||||||
orgId: 1
|
|
||||||
folder: 'Prospection'
|
|
||||||
type: file
|
|
||||||
disableDeletion: false
|
|
||||||
editable: true
|
|
||||||
options:
|
|
||||||
path: /var/lib/grafana/dashboards/prospection
|
|
||||||
|
|
||||||
# - name: 'default'
|
# - name: 'default'
|
||||||
# orgId: 1
|
# orgId: 1
|
||||||
@@ -778,248 +757,6 @@ grafana: &grafana_config
|
|||||||
k8s-views-pods:
|
k8s-views-pods:
|
||||||
url: https://raw.githubusercontent.com/dotdc/grafana-dashboards-kubernetes/master/dashboards/k8s-views-pods.json
|
url: https://raw.githubusercontent.com/dotdc/grafana-dashboards-kubernetes/master/dashboards/k8s-views-pods.json
|
||||||
token: ''
|
token: ''
|
||||||
prospection:
|
|
||||||
prospection:
|
|
||||||
json: |
|
|
||||||
{
|
|
||||||
"__inputs": [],
|
|
||||||
"annotations": { "list": [] },
|
|
||||||
"editable": true,
|
|
||||||
"graphTooltip": 1,
|
|
||||||
"schemaVersion": 39,
|
|
||||||
"tags": ["prospection", "arcodange"],
|
|
||||||
"title": "Prospection — pipeline BI missions",
|
|
||||||
"uid": "prospection-pipeline",
|
|
||||||
"time": { "from": "now-7d", "to": "now" },
|
|
||||||
"refresh": "30m",
|
|
||||||
"templating": {
|
|
||||||
"list": [
|
|
||||||
{
|
|
||||||
"name": "DS_PROMETHEUS",
|
|
||||||
"label": "Datasource",
|
|
||||||
"type": "datasource",
|
|
||||||
"query": "prometheus",
|
|
||||||
"current": {},
|
|
||||||
"hide": 0,
|
|
||||||
"refresh": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"panels": [
|
|
||||||
{
|
|
||||||
"type": "row", "title": "Vue d'ensemble du dernier run",
|
|
||||||
"gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 1, "collapsed": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "stat", "title": "Dernier run réussi il y a", "id": 2,
|
|
||||||
"gridPos": { "h": 4, "w": 5, "x": 0, "y": 1 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "time() - prospection_run_timestamp_seconds", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"fieldConfig": { "defaults": {
|
|
||||||
"unit": "s",
|
|
||||||
"thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "yellow", "value": 86400 }, { "color": "red", "value": 90000 } ] }
|
|
||||||
}, "overrides": [] },
|
|
||||||
"options": { "colorMode": "background", "graphMode": "none", "reduceOptions": { "calcs": ["lastNotNull"] }, "textMode": "auto" }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "stat", "title": "Statut du run", "id": 3,
|
|
||||||
"gridPos": { "h": 4, "w": 4, "x": 5, "y": 1 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "prospection_run_success", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"fieldConfig": { "defaults": {
|
|
||||||
"mappings": [ { "type": "value", "options": { "0": { "text": "ÉCHEC", "color": "red", "index": 0 }, "1": { "text": "OK", "color": "green", "index": 1 } } } ],
|
|
||||||
"thresholds": { "mode": "absolute", "steps": [ { "color": "red", "value": null }, { "color": "green", "value": 1 } ] }
|
|
||||||
}, "overrides": [] },
|
|
||||||
"options": { "colorMode": "background", "graphMode": "none", "reduceOptions": { "calcs": ["lastNotNull"] }, "textMode": "auto" }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "stat", "title": "Durée du run", "id": 4,
|
|
||||||
"gridPos": { "h": 4, "w": 5, "x": 9, "y": 1 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "prospection_run_duration_seconds", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"fieldConfig": { "defaults": { "unit": "s", "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "yellow", "value": 1200 }, { "color": "red", "value": 1700 } ] } }, "overrides": [] },
|
|
||||||
"options": { "colorMode": "value", "graphMode": "area", "reduceOptions": { "calcs": ["lastNotNull"] } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "stat", "title": "Étapes en erreur", "id": 5,
|
|
||||||
"gridPos": { "h": 4, "w": 4, "x": 14, "y": 1 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "prospection_run_errors_total", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"fieldConfig": { "defaults": { "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 1 } ] } }, "overrides": [] },
|
|
||||||
"options": { "colorMode": "background", "graphMode": "none", "reduceOptions": { "calcs": ["lastNotNull"] } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "stat", "title": "Missions A qualifiées", "id": 6,
|
|
||||||
"gridPos": { "h": 4, "w": 3, "x": 18, "y": 1 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "prospection_opportunities_total{kind=\"A\"}", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"fieldConfig": { "defaults": { "thresholds": { "mode": "absolute", "steps": [ { "color": "red", "value": null }, { "color": "yellow", "value": 1 }, { "color": "green", "value": 10 } ] } }, "overrides": [] },
|
|
||||||
"options": { "colorMode": "value", "graphMode": "area", "reduceOptions": { "calcs": ["lastNotNull"] } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "gauge", "title": "Meilleur score", "id": 7,
|
|
||||||
"gridPos": { "h": 4, "w": 3, "x": 21, "y": 1 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "prospection_opportunity_top_score", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"fieldConfig": { "defaults": { "min": 0, "max": 100, "thresholds": { "mode": "absolute", "steps": [ { "color": "red", "value": null }, { "color": "yellow", "value": 65 }, { "color": "green", "value": 85 } ] } }, "overrides": [] },
|
|
||||||
"options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "showThresholdLabels": false, "showThresholdMarkers": true }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "row", "title": "Collecte par étape",
|
|
||||||
"gridPos": { "h": 1, "w": 24, "x": 0, "y": 5 }, "id": 8, "collapsed": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "table", "title": "Étapes — dernier run", "id": 9,
|
|
||||||
"gridPos": { "h": 9, "w": 12, "x": 0, "y": 6 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [
|
|
||||||
{ "refId": "Statut", "expr": "prospection_step_status", "instant": true, "format": "table", "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } },
|
|
||||||
{ "refId": "Items", "expr": "prospection_step_items", "instant": true, "format": "table", "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } },
|
|
||||||
{ "refId": "Durée", "expr": "prospection_step_duration_seconds", "instant": true, "format": "table", "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } }
|
|
||||||
],
|
|
||||||
"transformations": [
|
|
||||||
{ "id": "merge", "options": {} },
|
|
||||||
{ "id": "organize", "options": {
|
|
||||||
"excludeByName": { "Time": true, "__name__": true, "job": true, "instance": true },
|
|
||||||
"renameByName": { "step": "Étape", "Value #Statut": "Statut", "Value #Items": "Items", "Value #Durée": "Durée (s)" },
|
|
||||||
"indexByName": { "step": 0, "Value #Statut": 1, "Value #Items": 2, "Value #Durée": 3 }
|
|
||||||
} }
|
|
||||||
],
|
|
||||||
"fieldConfig": { "defaults": { "custom": { "align": "auto" } }, "overrides": [
|
|
||||||
{ "matcher": { "id": "byName", "options": "Statut" }, "properties": [
|
|
||||||
{ "id": "mappings", "value": [ { "type": "value", "options": { "0": { "text": "⛔ erreur", "color": "red", "index": 0 }, "1": { "text": "✅ ok", "color": "green", "index": 1 }, "2": { "text": "⏭️ skip", "color": "blue", "index": 2 } } } ] },
|
|
||||||
{ "id": "custom.cellOptions", "value": { "type": "color-text" } }
|
|
||||||
] },
|
|
||||||
{ "matcher": { "id": "byName", "options": "Durée (s)" }, "properties": [ { "id": "unit", "value": "s" } ] }
|
|
||||||
] },
|
|
||||||
"options": { "showHeader": true, "sortBy": [ { "displayName": "Durée (s)", "desc": true } ] }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "barchart", "title": "Durée par étape — dernier run", "id": 21,
|
|
||||||
"gridPos": { "h": 9, "w": 12, "x": 12, "y": 6 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "prospection_step_duration_seconds", "instant": true, "format": "table", "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"transformations": [ { "id": "organize", "options": { "excludeByName": { "Time": true, "__name__": true, "job": true, "instance": true }, "renameByName": { "step": "Étape", "Value": "Durée (s)" } } } ],
|
|
||||||
"fieldConfig": { "defaults": { "unit": "s", "color": { "mode": "continuous-GrYlRd" }, "custom": { "lineWidth": 1, "fillOpacity": 80 } }, "overrides": [] },
|
|
||||||
"options": { "orientation": "horizontal", "xField": "Étape", "showValue": "auto", "legend": { "showLegend": false }, "tooltip": { "mode": "single", "sort": "none" } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "barchart", "title": "Items par étape — dernier run", "id": 22,
|
|
||||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 15 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "prospection_step_items", "instant": true, "format": "table", "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"transformations": [ { "id": "organize", "options": { "excludeByName": { "Time": true, "__name__": true, "job": true, "instance": true }, "renameByName": { "step": "Étape", "Value": "Items" } } } ],
|
|
||||||
"fieldConfig": { "defaults": { "unit": "short", "color": { "mode": "continuous-BlPu" }, "custom": { "lineWidth": 1, "fillOpacity": 80 } }, "overrides": [] },
|
|
||||||
"options": { "orientation": "horizontal", "xField": "Étape", "showValue": "auto", "legend": { "showLegend": false }, "tooltip": { "mode": "single", "sort": "none" } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "timeseries", "title": "Items collectés par étape (historique)", "id": 10,
|
|
||||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 15 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "prospection_step_items > 0", "legendFormat": "{{step}}", "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"fieldConfig": { "defaults": { "custom": { "drawStyle": "bars", "fillOpacity": 40, "stacking": { "mode": "none" }, "lineWidth": 1 } }, "overrides": [] },
|
|
||||||
"options": { "legend": { "displayMode": "table", "placement": "right", "calcs": ["lastNotNull"] }, "tooltip": { "mode": "multi", "sort": "desc" } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "row", "title": "Modèle de données & scoring",
|
|
||||||
"gridPos": { "h": 1, "w": 24, "x": 0, "y": 23 }, "id": 11, "collapsed": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "timeseries", "title": "Opportunités — missions A / cibles B", "id": 12,
|
|
||||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 24 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [
|
|
||||||
{ "refId": "A", "expr": "prospection_opportunities_total{kind=\"A\"}", "legendFormat": "Missions A", "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } },
|
|
||||||
{ "refId": "B", "expr": "prospection_opportunities_total{kind=\"B\"}", "legendFormat": "Cibles B", "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } }
|
|
||||||
],
|
|
||||||
"fieldConfig": { "defaults": { "custom": { "drawStyle": "line", "fillOpacity": 10, "lineWidth": 2, "pointSize": 5, "showPoints": "always" } }, "overrides": [] },
|
|
||||||
"options": { "legend": { "displayMode": "list", "placement": "bottom" }, "tooltip": { "mode": "multi" } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "timeseries", "title": "Offres & entités collectées (historique)", "id": 13,
|
|
||||||
"gridPos": { "h": 8, "w": 8, "x": 12, "y": 24 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [
|
|
||||||
{ "refId": "A", "expr": "prospection_offers_total", "legendFormat": "Offres (missions)", "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } },
|
|
||||||
{ "refId": "B", "expr": "prospection_entities_total", "legendFormat": "Entités (territoire)", "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } },
|
|
||||||
{ "refId": "C", "expr": "prospection_scores_total", "legendFormat": "Offres scorées", "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } }
|
|
||||||
],
|
|
||||||
"fieldConfig": { "defaults": { "custom": { "drawStyle": "line", "fillOpacity": 10, "lineWidth": 2, "showPoints": "auto" } }, "overrides": [] },
|
|
||||||
"options": { "legend": { "displayMode": "list", "placement": "bottom" }, "tooltip": { "mode": "multi" } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "bargauge", "title": "Modèle — dernier run", "id": 14,
|
|
||||||
"gridPos": { "h": 8, "w": 4, "x": 20, "y": 24 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [
|
|
||||||
{ "refId": "A", "expr": "prospection_signals_total", "legendFormat": "Signaux", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } },
|
|
||||||
{ "refId": "B", "expr": "prospection_entities_total", "legendFormat": "Entités", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } },
|
|
||||||
{ "refId": "C", "expr": "prospection_offers_total", "legendFormat": "Offres", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } },
|
|
||||||
{ "refId": "D", "expr": "prospection_scores_total", "legendFormat": "Scorées", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } }
|
|
||||||
],
|
|
||||||
"fieldConfig": { "defaults": { "thresholds": { "mode": "absolute", "steps": [ { "color": "blue", "value": null } ] } }, "overrides": [] },
|
|
||||||
"options": { "displayMode": "gradient", "orientation": "horizontal", "reduceOptions": { "calcs": ["lastNotNull"] }, "legend": { "showLegend": false } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "row", "title": "Livraison & alertes",
|
|
||||||
"gridPos": { "h": 1, "w": 24, "x": 0, "y": 32 }, "id": 15, "collapsed": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "stat", "title": "Brief rendu", "id": 16,
|
|
||||||
"gridPos": { "h": 5, "w": 3, "x": 0, "y": 33 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "prospection_brief_rendered", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"fieldConfig": { "defaults": { "mappings": [ { "type": "value", "options": { "0": { "text": "non", "color": "red", "index": 0 }, "1": { "text": "oui", "color": "green", "index": 1 } } } ], "thresholds": { "mode": "absolute", "steps": [ { "color": "red", "value": null }, { "color": "green", "value": 1 } ] } }, "overrides": [] },
|
|
||||||
"options": { "colorMode": "background", "graphMode": "none", "reduceOptions": { "calcs": ["lastNotNull"] } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "stat", "title": "Poussé sur Telegram", "id": 17,
|
|
||||||
"gridPos": { "h": 5, "w": 4, "x": 3, "y": 33 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "prospection_brief_telegram_pushed", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"fieldConfig": { "defaults": { "mappings": [ { "type": "value", "options": { "0": { "text": "non", "color": "red", "index": 0 }, "1": { "text": "oui", "color": "green", "index": 1 } } } ], "thresholds": { "mode": "absolute", "steps": [ { "color": "red", "value": null }, { "color": "green", "value": 1 } ] } }, "overrides": [] },
|
|
||||||
"options": { "colorMode": "background", "graphMode": "none", "reduceOptions": { "calcs": ["lastNotNull"] } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "stat", "title": "Messages / mission", "id": 18,
|
|
||||||
"gridPos": { "h": 5, "w": 3, "x": 7, "y": 33 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "prospection_telegram_messages_sent", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"fieldConfig": { "defaults": { "thresholds": { "mode": "absolute", "steps": [ { "color": "blue", "value": null } ] } }, "overrides": [] },
|
|
||||||
"options": { "colorMode": "value", "graphMode": "area", "reduceOptions": { "calcs": ["lastNotNull"] } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "stat", "title": "Offres du brief", "id": 23,
|
|
||||||
"gridPos": { "h": 5, "w": 3, "x": 10, "y": 33 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "prospection_brief_offres", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"fieldConfig": { "defaults": { "thresholds": { "mode": "absolute", "steps": [ { "color": "red", "value": null }, { "color": "green", "value": 1 } ] } }, "overrides": [] },
|
|
||||||
"options": { "colorMode": "value", "graphMode": "none", "reduceOptions": { "calcs": ["lastNotNull"] } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "stat", "title": "Vidéo brief", "id": 19,
|
|
||||||
"gridPos": { "h": 5, "w": 3, "x": 13, "y": 33 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "prospection_brief_video_kb * 1024", "instant": true, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"fieldConfig": { "defaults": { "unit": "bytes", "thresholds": { "mode": "absolute", "steps": [ { "color": "blue", "value": null } ] } }, "overrides": [] },
|
|
||||||
"options": { "colorMode": "value", "graphMode": "none", "reduceOptions": { "calcs": ["lastNotNull"] } }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "table", "title": "Alertes prospection actives", "id": 20,
|
|
||||||
"gridPos": { "h": 5, "w": 8, "x": 16, "y": 33 },
|
|
||||||
"datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
|
|
||||||
"targets": [ { "refId": "A", "expr": "ALERTS{alertname=~\"Prospection.*\", alertstate=\"firing\"}", "instant": true, "format": "table", "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" } } ],
|
|
||||||
"transformations": [
|
|
||||||
{ "id": "organize", "options": {
|
|
||||||
"excludeByName": { "Time": true, "Value": true, "__name__": true, "job": true, "instance": true, "alertstate": true },
|
|
||||||
"renameByName": { "alertname": "Alerte", "severity": "Sévérité" }
|
|
||||||
} }
|
|
||||||
],
|
|
||||||
"fieldConfig": { "defaults": { "custom": { "align": "auto" } }, "overrides": [] },
|
|
||||||
"options": { "showHeader": true, "footer": { "show": false } }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
# default:
|
# default:
|
||||||
# some-dashboard:
|
# some-dashboard:
|
||||||
|
|||||||
@@ -74,36 +74,11 @@ resource "vault_kubernetes_auth_backend_role" "vso" {
|
|||||||
alias_name_source = "serviceaccount_name"
|
alias_name_source = "serviceaccount_name"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Alertmanager (ns tools) doit lire le token du bot Telegram de prospection
|
|
||||||
# pour livrer les alertes. Rôle k8s dédié + policy read-only sur kvv2/prospection/telegram.
|
|
||||||
data "vault_policy_document" "alertmanager_telegram" {
|
|
||||||
rule {
|
|
||||||
path = "kvv2/data/prospection/telegram"
|
|
||||||
capabilities = ["read"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resource "vault_policy" "alertmanager_telegram" {
|
|
||||||
name = "alertmanager-telegram"
|
|
||||||
policy = data.vault_policy_document.alertmanager_telegram.hcl
|
|
||||||
}
|
|
||||||
resource "vault_kubernetes_auth_backend_role" "alertmanager" {
|
|
||||||
backend = vault_auth_backend.kubernetes.path
|
|
||||||
role_name = "alertmanager"
|
|
||||||
bound_service_account_names = ["prometheus-alertmanager"]
|
|
||||||
bound_service_account_namespaces = ["tools"]
|
|
||||||
token_ttl = 3600
|
|
||||||
token_policies = ["default", vault_policy.alertmanager_telegram.name]
|
|
||||||
audience = "vault"
|
|
||||||
alias_name_source = "serviceaccount_name"
|
|
||||||
}
|
|
||||||
|
|
||||||
module "app_policies" {
|
module "app_policies" {
|
||||||
source = "./modules/app_policy"
|
source = "./modules/app_policy"
|
||||||
for_each = { for app in var.applications : app.name => app }
|
for_each = { for app in var.applications : app.name => app }
|
||||||
name = each.value.name
|
name = each.value.name
|
||||||
envs = each.value.envs
|
ops_policies = each.value.policies
|
||||||
ops_policies = each.value.ops_policies
|
|
||||||
kv_read_paths = each.value.kv_read_paths
|
|
||||||
service_account_names = each.value.service_account_names
|
service_account_names = each.value.service_account_names
|
||||||
service_account_namespaces = each.value.service_account_namespaces
|
service_account_namespaces = each.value.service_account_namespaces
|
||||||
gitea_app_id = var.gitea_app_id
|
gitea_app_id = var.gitea_app_id
|
||||||
|
|||||||
@@ -7,15 +7,8 @@
|
|||||||
|
|
||||||
locals {
|
locals {
|
||||||
name = lower(var.name)
|
name = lower(var.name)
|
||||||
envs = [for e in var.envs : lower(e)]
|
bound_service_account_names = concat([var.name], var.service_account_names)
|
||||||
|
bound_service_account_namespaces = concat([var.name], var.service_account_namespaces)
|
||||||
# Elision rule: env=prod → bare name; else <name>-<env>
|
|
||||||
instances = [for e in local.envs : e == "prod" ? local.name : "${local.name}-${e}"]
|
|
||||||
non_prod_instances = [for e in local.envs : "${local.name}-${e}" if e != "prod"]
|
|
||||||
|
|
||||||
# Per-instance SA name/namespace sets used by the CI policy's allowed_parameter blocks.
|
|
||||||
per_instance_sa_names = { for inst in local.instances : inst => concat([inst], var.service_account_names) }
|
|
||||||
per_instance_sa_namespaces = { for inst in local.instances : inst => concat([inst], var.service_account_namespaces) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data "vault_policy_document" "ops" {
|
data "vault_policy_document" "ops" {
|
||||||
@@ -67,62 +60,42 @@ data "vault_policy_document" "ops" {
|
|||||||
}
|
}
|
||||||
allowed_parameter {
|
allowed_parameter {
|
||||||
key = "bound_service_account_names"
|
key = "bound_service_account_names"
|
||||||
value = [for inst in local.instances : jsonencode(local.per_instance_sa_names[inst])]
|
value = [jsonencode(local.bound_service_account_names)]
|
||||||
}
|
}
|
||||||
allowed_parameter {
|
allowed_parameter {
|
||||||
key = "bound_service_account_namespaces"
|
key = "bound_service_account_namespaces"
|
||||||
value = [for inst in local.instances : jsonencode(local.per_instance_sa_namespaces[inst])]
|
value = [jsonencode(local.bound_service_account_namespaces)]
|
||||||
}
|
}
|
||||||
allowed_parameter {
|
allowed_parameter {
|
||||||
key = "token_policies"
|
key = "token_policies"
|
||||||
value = flatten([
|
value = [
|
||||||
for inst in local.instances : [
|
jsonencode(["default", local.name]),
|
||||||
jsonencode(["default", inst]),
|
jsonencode([local.name, "default"])
|
||||||
jsonencode([inst, "default"])
|
|
||||||
]
|
]
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
# allow editing app secrets — one rule per (capability × instance) preserves the
|
# allow editing app secrets
|
||||||
# original rule order (data, delete, undelete, destroy, metadata) so prod-only apps
|
rule {
|
||||||
# render a byte-identical policy document (no Vault state diff). Multi-env apps add
|
path = "kvv2/data/${local.name}/*"
|
||||||
# extra rules per non-prod instance.
|
|
||||||
dynamic "rule" {
|
|
||||||
for_each = local.instances
|
|
||||||
content {
|
|
||||||
path = "kvv2/data/${rule.value}/*"
|
|
||||||
capabilities = ["create", "update", "read", "delete"]
|
capabilities = ["create", "update", "read", "delete"]
|
||||||
}
|
}
|
||||||
}
|
rule {
|
||||||
dynamic "rule" {
|
path = "kvv2/delete/${local.name}/*"
|
||||||
for_each = local.instances
|
|
||||||
content {
|
|
||||||
path = "kvv2/delete/${rule.value}/*"
|
|
||||||
capabilities = ["update"]
|
capabilities = ["update"]
|
||||||
}
|
}
|
||||||
}
|
rule {
|
||||||
dynamic "rule" {
|
path = "kvv2/undelete/${local.name}/*"
|
||||||
for_each = local.instances
|
|
||||||
content {
|
|
||||||
path = "kvv2/undelete/${rule.value}/*"
|
|
||||||
capabilities = ["update"]
|
capabilities = ["update"]
|
||||||
}
|
}
|
||||||
}
|
rule {
|
||||||
dynamic "rule" {
|
path = "kvv2/destroy/${local.name}/*"
|
||||||
for_each = local.instances
|
|
||||||
content {
|
|
||||||
path = "kvv2/destroy/${rule.value}/*"
|
|
||||||
capabilities = ["update"]
|
capabilities = ["update"]
|
||||||
}
|
}
|
||||||
}
|
rule {
|
||||||
dynamic "rule" {
|
path = "kvv2/metadata/${local.name}/*"
|
||||||
for_each = local.instances
|
|
||||||
content {
|
|
||||||
path = "kvv2/metadata/${rule.value}/*"
|
|
||||||
capabilities = ["read", "list", "delete"]
|
capabilities = ["read", "list", "delete"]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
# allow edit vault role (risky ?)
|
# allow edit vault role (risky ?)
|
||||||
}
|
}
|
||||||
resource "vault_policy" "ops" {
|
resource "vault_policy" "ops" {
|
||||||
@@ -166,9 +139,6 @@ resource "vault_jwt_auth_backend_role" "gitea_jwt_cicd" {
|
|||||||
role_type = "jwt"
|
role_type = "jwt"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Runtime policy for the env=prod instance — kept at its single-env address
|
|
||||||
# (data.vault_policy_document.app, vault_policy.app, name = local.name) so existing
|
|
||||||
# state isn't disturbed when this module is upgraded.
|
|
||||||
data "vault_policy_document" "app" {
|
data "vault_policy_document" "app" {
|
||||||
rule {
|
rule {
|
||||||
path = "kvv2/data/${local.name}/*"
|
path = "kvv2/data/${local.name}/*"
|
||||||
@@ -178,35 +148,8 @@ data "vault_policy_document" "app" {
|
|||||||
path = "postgres/creds/${local.name}*"
|
path = "postgres/creds/${local.name}*"
|
||||||
capabilities = ["read"]
|
capabilities = ["read"]
|
||||||
}
|
}
|
||||||
# Extra shared paths this app's prod runtime may read (e.g. backup creds).
|
|
||||||
dynamic "rule" {
|
|
||||||
for_each = var.kv_read_paths
|
|
||||||
content {
|
|
||||||
path = rule.value
|
|
||||||
capabilities = ["read", "list"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
resource "vault_policy" "app" {
|
resource "vault_policy" "app" {
|
||||||
name = local.name
|
name = local.name
|
||||||
policy = data.vault_policy_document.app.hcl
|
policy = data.vault_policy_document.app.hcl
|
||||||
}
|
}
|
||||||
|
|
||||||
# Runtime policies for non-prod envs. Each one is named <name>-<env> and reads
|
|
||||||
# only its own kvv2 + postgres creds paths.
|
|
||||||
data "vault_policy_document" "app_non_prod" {
|
|
||||||
for_each = toset(local.non_prod_instances)
|
|
||||||
rule {
|
|
||||||
path = "kvv2/data/${each.key}/*"
|
|
||||||
capabilities = ["read", "list"]
|
|
||||||
}
|
|
||||||
rule {
|
|
||||||
path = "postgres/creds/${each.key}*"
|
|
||||||
capabilities = ["read"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resource "vault_policy" "app_non_prod" {
|
|
||||||
for_each = toset(local.non_prod_instances)
|
|
||||||
name = each.key
|
|
||||||
policy = data.vault_policy_document.app_non_prod[each.key].hcl
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,6 @@
|
|||||||
variable "name" {
|
variable "name" {
|
||||||
type = string
|
type = string
|
||||||
}
|
}
|
||||||
variable "envs" {
|
|
||||||
type = list(string)
|
|
||||||
default = ["prod"]
|
|
||||||
description = "List of environments this app deploys to. The CI policy + JWT role + identity group are created ONCE per repo regardless. One runtime policy is created per env; the env=prod runtime policy keeps its single-env address for backwards compatibility (no state move)."
|
|
||||||
}
|
|
||||||
variable "gitea_app_id" {
|
variable "gitea_app_id" {
|
||||||
type = string
|
type = string
|
||||||
}
|
}
|
||||||
@@ -23,8 +18,3 @@ variable "service_account_namespaces" {
|
|||||||
default = []
|
default = []
|
||||||
description = "var.name will always be included by default - whitelist service account namespaces that can take this policy"
|
description = "var.name will always be included by default - whitelist service account namespaces that can take this policy"
|
||||||
}
|
}
|
||||||
variable "kv_read_paths" {
|
|
||||||
type = list(string)
|
|
||||||
default = []
|
|
||||||
description = "Extra kvv2 data paths the env=prod runtime policy may read (read,list) — e.g. a shared backup-creds path owned by another app (kvv2/data/longhorn/gcs-backup). Default none."
|
|
||||||
}
|
|
||||||
@@ -4,18 +4,10 @@ data "vault_auth_backend" "kubernetes" {
|
|||||||
|
|
||||||
locals {
|
locals {
|
||||||
name = lower(var.name)
|
name = lower(var.name)
|
||||||
env = lower(var.env)
|
database = var.database == null ? local.name : var.database
|
||||||
database = var.database == null ? local.instance : var.database
|
|
||||||
|
|
||||||
# Elision rule (factory runbook conventions.md):
|
bound_service_account_names = concat([var.name], var.service_account_names)
|
||||||
# env == prod → identical to the single-env baseline (no suffix)
|
bound_service_account_namespaces = concat([var.name], var.service_account_namespaces)
|
||||||
# else → kebab-case "<name>-<env>" for K8s/Vault paths.
|
|
||||||
# Postgres owner role stays snake-case for consistency with the existing "_role" suffix.
|
|
||||||
instance = local.env == "prod" ? local.name : "${local.name}-${local.env}"
|
|
||||||
owner_role = local.env == "prod" ? "${local.name}_role" : "${local.name}_${local.env}_role"
|
|
||||||
|
|
||||||
bound_service_account_names = concat([local.instance], var.service_account_names)
|
|
||||||
bound_service_account_namespaces = concat([local.instance], var.service_account_namespaces)
|
|
||||||
|
|
||||||
vault_mount_postgres = { path = "postgres" }
|
vault_mount_postgres = { path = "postgres" }
|
||||||
vault_mount_kvv2 = { path = "kvv2" }
|
vault_mount_kvv2 = { path = "kvv2" }
|
||||||
@@ -28,14 +20,14 @@ moved {
|
|||||||
resource "vault_database_secret_backend_role" "role" {
|
resource "vault_database_secret_backend_role" "role" {
|
||||||
count = var.disable_database ? 0 : 1
|
count = var.disable_database ? 0 : 1
|
||||||
backend = local.vault_mount_postgres.path
|
backend = local.vault_mount_postgres.path
|
||||||
name = local.instance
|
name = local.name
|
||||||
db_name = "postgres"
|
db_name = "postgres"
|
||||||
creation_statements = [
|
creation_statements = [
|
||||||
"CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';",
|
"CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';",
|
||||||
"GRANT ${local.owner_role} TO \"{{name}}\";",
|
"GRANT ${local.name}_role TO \"{{name}}\";",
|
||||||
]
|
]
|
||||||
revocation_statements = [
|
revocation_statements = [
|
||||||
"REASSIGN OWNED BY \"{{name}}\" TO ${local.owner_role};", # reassign must be executed in the database where the reassgined objects are - TODO (one connection per database/app)
|
"REASSIGN OWNED BY \"{{name}}\" TO ${local.name}_role;", # reassign must be executed in the database where the reassgined objects are - TODO (one connection per database/app)
|
||||||
"REVOKE ALL ON DATABASE ${local.database} FROM \"{{name}}\";", # should we drop the role ? -> YES after fixing reassign
|
"REVOKE ALL ON DATABASE ${local.database} FROM \"{{name}}\";", # should we drop the role ? -> YES after fixing reassign
|
||||||
]
|
]
|
||||||
renew_statements = []
|
renew_statements = []
|
||||||
@@ -44,11 +36,11 @@ resource "vault_database_secret_backend_role" "role" {
|
|||||||
|
|
||||||
resource "vault_kubernetes_auth_backend_role" "role" {
|
resource "vault_kubernetes_auth_backend_role" "role" {
|
||||||
backend = data.vault_auth_backend.kubernetes.path
|
backend = data.vault_auth_backend.kubernetes.path
|
||||||
role_name = local.instance
|
role_name = local.name
|
||||||
bound_service_account_names = local.bound_service_account_names
|
bound_service_account_names = local.bound_service_account_names
|
||||||
bound_service_account_namespaces = local.bound_service_account_namespaces
|
bound_service_account_namespaces = local.bound_service_account_namespaces
|
||||||
token_ttl = 3600
|
token_ttl = 3600
|
||||||
token_policies = ["default", local.instance]
|
token_policies = ["default", local.name]
|
||||||
audience = "vault"
|
audience = "vault"
|
||||||
alias_name_source = "serviceaccount_name"
|
alias_name_source = "serviceaccount_name"
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,6 @@
|
|||||||
output "name" {
|
output "name" {
|
||||||
value = local.name
|
value = local.name
|
||||||
}
|
}
|
||||||
output "env" {
|
|
||||||
value = local.env
|
|
||||||
}
|
|
||||||
output "instance" {
|
|
||||||
value = local.instance
|
|
||||||
description = "Derived id by the elision rule: equals name when env=prod, else <name>-<env>."
|
|
||||||
}
|
|
||||||
output "database" {
|
output "database" {
|
||||||
value = local.database
|
value = local.database
|
||||||
}
|
}
|
||||||
@@ -19,6 +12,5 @@ output "mount_paths" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
output "kvv2_path_prefix" {
|
output "kvv2_path_prefix" {
|
||||||
# Identical to format("%s/", local.name) when env=prod (backwards compat).
|
value = format("%s/", local.name)
|
||||||
value = format("%s/", local.instance)
|
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,6 @@
|
|||||||
variable "name" {
|
variable "name" {
|
||||||
type = string
|
type = string
|
||||||
}
|
}
|
||||||
variable "env" {
|
|
||||||
type = string
|
|
||||||
default = "prod"
|
|
||||||
description = "Deployment environment. By the elision rule (factory runbook conventions.md), env=prod produces names identical to the single-env baseline; non-prod values produce <name>-<env> kebab-case and <name>_<env>_role for the Postgres owner role."
|
|
||||||
}
|
|
||||||
variable "database" {
|
variable "database" {
|
||||||
type = string
|
type = string
|
||||||
nullable = true
|
nullable = true
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
applications = [
|
applications = [
|
||||||
{ name = "webapp" },
|
{ name = "webapp" },
|
||||||
{
|
{ name = "erp" },
|
||||||
name = "erp"
|
|
||||||
envs = ["prod", "sandbox"]
|
|
||||||
kv_read_paths = ["kvv2/data/longhorn/gcs-backup"] # backup CronJob reads the shared GCS creds
|
|
||||||
},
|
|
||||||
{ name = "dance-lessons-coach" },
|
{ name = "dance-lessons-coach" },
|
||||||
{
|
{
|
||||||
name = "cms"
|
name = "cms"
|
||||||
@@ -19,6 +15,4 @@ applications = [
|
|||||||
name = "plausible"
|
name = "plausible"
|
||||||
service_account_namespaces = ["tools"]
|
service_account_namespaces = ["tools"]
|
||||||
},
|
},
|
||||||
{ name = "prospection" },
|
|
||||||
{ name = "kadans" },
|
|
||||||
]
|
]
|
||||||
@@ -12,15 +12,8 @@ variable "POSTGRES_CREDENTIALS_EDITOR_PASSWORD" {
|
|||||||
variable "applications" {
|
variable "applications" {
|
||||||
type = set(object({
|
type = set(object({
|
||||||
name = string
|
name = string
|
||||||
ops_policies = optional(list(string), [])
|
policies = optional(list(string), [])
|
||||||
service_account_names = optional(list(string), [])
|
service_account_names = optional(list(string), [])
|
||||||
service_account_namespaces = optional(list(string), [])
|
service_account_namespaces = optional(list(string), [])
|
||||||
# Multi-env extension: list of envs this app deploys to. Defaults to ["prod"] for
|
|
||||||
# every existing app — backwards compatible by the elision rule. Non-prod envs
|
|
||||||
# produce additional runtime policies named "<name>-<env>".
|
|
||||||
envs = optional(list(string), ["prod"])
|
|
||||||
# Extra kvv2 data paths the app's prod runtime policy may read (read,list) —
|
|
||||||
# e.g. a shared backup-creds path owned by another app. Default none.
|
|
||||||
kv_read_paths = optional(list(string), [])
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
# Livraison des alertes Prometheus vers Telegram (bot prospection).
|
|
||||||
#
|
|
||||||
# Alertmanager tourne dans le namespace `tools`, mais le token du bot vit dans Vault
|
|
||||||
# (kvv2/prospection/telegram). Le Secret `prospection-telegram` synchronisé par VSO est
|
|
||||||
# namespace-scoped (prospection) et non réutilisable ici. On resynchronise donc le même
|
|
||||||
# chemin kvv2 vers un Secret `alertmanager-telegram` dans `tools`, via un VaultAuth dédié
|
|
||||||
# (rôle k8s `alertmanager`, provisionné par hashicorp-vault/iac).
|
|
||||||
#
|
|
||||||
# NB: ce chart prometheus est en mode `tool.kind: SubChart`, donc les templates
|
|
||||||
# helm-chart*.yaml ne rendent rien ; ce fichier, lui, est rendu tel quel et appliqué par
|
|
||||||
# ArgoCD (app `prometheus`, destination namespace `tools`).
|
|
||||||
apiVersion: secrets.hashicorp.com/v1beta1
|
|
||||||
kind: VaultAuth
|
|
||||||
metadata:
|
|
||||||
name: alertmanager-telegram
|
|
||||||
namespace: tools
|
|
||||||
spec:
|
|
||||||
# Dans le ns tools, VSO exige un vaultConnectionRef explicite (contrairement au ns
|
|
||||||
# prospection qui hérite d'une connexion par défaut). On pointe la VaultConnection
|
|
||||||
# `default` déjà présente dans tools (http://hashicorp-vault.tools.svc:8200).
|
|
||||||
vaultConnectionRef: default
|
|
||||||
method: kubernetes
|
|
||||||
mount: kubernetes
|
|
||||||
kubernetes:
|
|
||||||
role: alertmanager
|
|
||||||
serviceAccount: prometheus-alertmanager
|
|
||||||
audiences:
|
|
||||||
- vault
|
|
||||||
---
|
|
||||||
apiVersion: secrets.hashicorp.com/v1beta1
|
|
||||||
kind: VaultStaticSecret
|
|
||||||
metadata:
|
|
||||||
name: alertmanager-telegram
|
|
||||||
namespace: tools
|
|
||||||
spec:
|
|
||||||
type: kv-v2
|
|
||||||
mount: kvv2
|
|
||||||
path: prospection/telegram
|
|
||||||
destination:
|
|
||||||
name: alertmanager-telegram
|
|
||||||
create: true
|
|
||||||
refreshAfter: 1h
|
|
||||||
vaultAuthRef: alertmanager-telegram
|
|
||||||
# Alertmanager lit le token depuis un fichier monté au démarrage et ne recharge pas à
|
|
||||||
# chaud un secret monté : on redémarre le StatefulSet quand le token change dans Vault.
|
|
||||||
rolloutRestartTargets:
|
|
||||||
- kind: StatefulSet
|
|
||||||
name: prometheus-alertmanager
|
|
||||||
+3
-88
@@ -612,11 +612,8 @@ prometheus: &prometheus_config
|
|||||||
podLabels: {}
|
podLabels: {}
|
||||||
|
|
||||||
## Prometheus AlertManager configuration
|
## Prometheus AlertManager configuration
|
||||||
## Lien Prometheus -> Alertmanager (service du sous-chart, ns tools).
|
##
|
||||||
alertmanagers:
|
alertmanagers: []
|
||||||
- static_configs:
|
|
||||||
- targets:
|
|
||||||
- prometheus-alertmanager:9093
|
|
||||||
|
|
||||||
## Use a StatefulSet if replicaCount needs to be greater than 1 (see below)
|
## Use a StatefulSet if replicaCount needs to be greater than 1 (see below)
|
||||||
##
|
##
|
||||||
@@ -1118,59 +1115,7 @@ prometheus: &prometheus_config
|
|||||||
serverFiles:
|
serverFiles:
|
||||||
## Alerts configuration
|
## Alerts configuration
|
||||||
## Ref: https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/
|
## Ref: https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/
|
||||||
alerting_rules.yml:
|
alerting_rules.yml: {}
|
||||||
groups:
|
|
||||||
# Pipeline prospection (métriques poussées au Pushgateway job=prospection en fin de
|
|
||||||
# run). NB : la LIVRAISON des alertes (Alertmanager → Telegram/…) n'est pas encore
|
|
||||||
# câblée dans ce cluster — ces règles s'évaluent et sont visibles dans Prometheus
|
|
||||||
# /alerts + le dashboard Grafana « Prospection » (panneau Alertes actives).
|
|
||||||
- name: prospection
|
|
||||||
rules:
|
|
||||||
- alert: ProspectionRunStale
|
|
||||||
expr: time() - prospection_run_timestamp_seconds > 90000 # > 25 h (cron quotidien)
|
|
||||||
for: 10m
|
|
||||||
labels:
|
|
||||||
severity: warning
|
|
||||||
app: prospection
|
|
||||||
annotations:
|
|
||||||
summary: "Prospection — aucun run réussi depuis plus de 25 h"
|
|
||||||
description: "Dernier run réussi il y a {{ $value | humanizeDuration }} ; le CronJob quotidien (~06:30 UTC) n'a pas abouti."
|
|
||||||
- alert: ProspectionRunFailed
|
|
||||||
expr: prospection_run_success == 0
|
|
||||||
for: 5m
|
|
||||||
labels:
|
|
||||||
severity: warning
|
|
||||||
app: prospection
|
|
||||||
annotations:
|
|
||||||
summary: "Prospection — le dernier run a échoué"
|
|
||||||
description: "prospection_run_success=0 : toutes les collectes ont échoué au dernier run."
|
|
||||||
- alert: ProspectionStepError
|
|
||||||
expr: prospection_step_status == 0
|
|
||||||
for: 5m
|
|
||||||
labels:
|
|
||||||
severity: info
|
|
||||||
app: prospection
|
|
||||||
annotations:
|
|
||||||
summary: "Prospection — étape {{ $labels.step }} en erreur"
|
|
||||||
description: "L'étape {{ $labels.step }} du pipeline a fini en erreur au dernier run."
|
|
||||||
- alert: ProspectionNoOffers
|
|
||||||
expr: prospection_offers_total == 0
|
|
||||||
for: 15m
|
|
||||||
labels:
|
|
||||||
severity: warning
|
|
||||||
app: prospection
|
|
||||||
annotations:
|
|
||||||
summary: "Prospection — 0 offre (mission) collectée"
|
|
||||||
description: "Aucune offre au dernier run : collecte France Travail / Free-Work potentiellement cassée."
|
|
||||||
- alert: ProspectionBriefNotSent
|
|
||||||
expr: prospection_brief_telegram_pushed == 0
|
|
||||||
for: 15m
|
|
||||||
labels:
|
|
||||||
severity: info
|
|
||||||
app: prospection
|
|
||||||
annotations:
|
|
||||||
summary: "Prospection — brief non poussé sur Telegram"
|
|
||||||
description: "Le brief vidéo n'a pas été diffusé sur Telegram au dernier run."
|
|
||||||
# groups:
|
# groups:
|
||||||
# - name: Instances
|
# - name: Instances
|
||||||
# rules:
|
# rules:
|
||||||
@@ -1236,36 +1181,6 @@ prometheus: &prometheus_config
|
|||||||
##
|
##
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
## Configuration Alertmanager : livraison native Telegram (bot prospection).
|
|
||||||
## Le token est lu depuis le fichier monté via extraSecretMounts (Secret
|
|
||||||
## alertmanager-telegram, synchronisé par VSO — cf. templates/vault-telegram.yaml).
|
|
||||||
## chat_id est public (non sensible), donc inline.
|
|
||||||
config:
|
|
||||||
enabled: true
|
|
||||||
global: {}
|
|
||||||
templates:
|
|
||||||
- /etc/alertmanager/*.tmpl
|
|
||||||
route:
|
|
||||||
group_by: ["alertname", "app"]
|
|
||||||
group_wait: 30s
|
|
||||||
group_interval: 5m
|
|
||||||
repeat_interval: 3h
|
|
||||||
receiver: telegram
|
|
||||||
receivers:
|
|
||||||
- name: telegram
|
|
||||||
telegram_configs:
|
|
||||||
- bot_token_file: /etc/alertmanager/telegram/BOT_TOKEN
|
|
||||||
chat_id: 7497777082
|
|
||||||
parse_mode: HTML
|
|
||||||
send_resolved: true
|
|
||||||
|
|
||||||
## Montage du token du bot dans le pod Alertmanager (fichier BOT_TOKEN).
|
|
||||||
extraSecretMounts:
|
|
||||||
- name: telegram
|
|
||||||
mountPath: /etc/alertmanager/telegram
|
|
||||||
secretName: alertmanager-telegram
|
|
||||||
readOnly: true
|
|
||||||
|
|
||||||
persistence:
|
persistence:
|
||||||
## If true, storage will create or use Persistence Volume
|
## If true, storage will create or use Persistence Volume
|
||||||
## If false, storage will use emptyDir
|
## If false, storage will use emptyDir
|
||||||
|
|||||||
Reference in New Issue
Block a user