L'Infrastructure as Code (IaC) a révolutionné la gestion des environnements cloud. Cette même philosophie s'applique naturellement au monitoring.
Le Monitoring as Code transforme la configuration d'alertes et de dashboards en code versionnable et auditable.
Qu'est-ce que le Monitoring Infrastructure as Code ?
Le Monitoring IaC désigne la pratique de définir et gérer toute la configuration de monitoring sous forme de code déclaratif, versionné et déployé automatiquement.
Composants gérés comme code
Cette approche couvre plusieurs aspects :
| Composant | Outils | Exemple |
|---|---|---|
| Alertes | Terraform + Datadog Provider | datadog_monitor |
| Dashboards | Grafonnet, Terraform | JSON/HCL versionné |
| Collecteurs | Ansible, Puppet | Configuration agents |
| SLO/SLI | Nobl9, Terraform | Définitions déclaratives |
Exemple avec Terraform et Datadog
# monitoring/main.tf
terraform {
required_providers {
datadog = {
source = "DataDog/datadog"
version = "~> 3.0"
}
}
}
resource "datadog_monitor" "high_error_rate" {
name = "High Error Rate - ${var.service_name}"
type = "metric alert"
message = <<-EOF
Taux d'erreur élevé détecté sur ${var.service_name}
@slack-${var.team_channel}
@pagerduty-${var.pagerduty_service}
Runbook: ${var.runbook_url}
EOF
query = <<-EOF
sum(last_5m):sum:http.requests{service:${var.service_name},status:5xx}.as_count()
/
sum:http.requests{service:${var.service_name}}.as_count() > 0.01
EOF
monitor_thresholds {
critical = 0.01
warning = 0.005
}
tags = [
"team:${var.team}",
"service:${var.service_name}",
"env:${var.environment}"
]
}
Pourquoi Adopter le Monitoring as Code
Consistance entre environnements
Les mêmes définitions s'appliquent partout :
# modules/service-monitoring/main.tf
variable "environment" {
type = string
}
variable "alert_thresholds" {
type = map(number)
default = {
dev = 0.05 # 5% erreurs tolérées en dev
staging = 0.02 # 2% en staging
prod = 0.01 # 1% en production
}
}
resource "datadog_monitor" "error_rate" {
# ...
monitor_thresholds {
critical = var.alert_thresholds[var.environment]
}
}
Fini les surprises en production dues à un monitoring différent.
Auditabilité native
L'historique Git répond aux questions critiques :
# Qui a modifié cette alerte?
git log --oneline monitoring/alerts/payment-service.tf
# Diff entre versions
git diff HEAD~5 monitoring/alerts/payment-service.tf
# Quand le seuil a-t-il changé?
git log -p --all -S '0.01' -- monitoring/
Collaboration via Pull Requests
Les changements passent par un processus de revue :
## PR #234: Ajuster monitoring service payment
### Changements
- Augmenter seuil latence p99: 200ms → 350ms
- Ajouter alerte sur taux de timeout
- Supprimer alerte deprecated `payment_legacy_errors`
### Justification
- Latence naturellement plus élevée après migration vers nouvelle API bancaire
- Timeouts non monitorés causaient des angles morts
### Tests
- [x] `terraform validate` passé
- [x] `terraform plan` vérifié
- [x] Déployé en staging 24h sans faux positif
Résilience et disaster recovery
Si votre infrastructure de monitoring est détruite :
# Reconstruction complète depuis le code
terraform init
terraform apply
# Monitoring restauré identique en quelques minutes
Évolutivité simplifiée
Ajouter du monitoring pour un nouveau service :
# Instancier le module avec les paramètres du service
module "user_service_monitoring" {
source = "./modules/service-monitoring"
service_name = "user-service"
team = "platform"
environment = "production"
latency_threshold = 0.2 # 200ms
error_rate_threshold = 0.005 # 0.5%
}
Comment Implémenter le Monitoring as Code
Avec Terraform
Utilisez les providers officiels des plateformes :
# providers.tf
terraform {
required_providers {
datadog = {
source = "DataDog/datadog"
version = "~> 3.0"
}
pagerduty = {
source = "PagerDuty/pagerduty"
version = "~> 2.0"
}
grafana = {
source = "grafana/grafana"
version = "~> 2.0"
}
}
}
# Dashboard Grafana
resource "grafana_dashboard" "service_overview" {
config_json = file("${path.module}/dashboards/service-overview.json")
folder = grafana_folder.services.id
}
# Escalation PagerDuty
resource "pagerduty_escalation_policy" "platform_team" {
name = "Platform Team Escalation"
num_loops = 2
rule {
escalation_delay_in_minutes = 15
target {
type = "schedule_reference"
id = pagerduty_schedule.platform_oncall.id
}
}
rule {
escalation_delay_in_minutes = 30
target {
type = "user_reference"
id = pagerduty_user.team_lead.id
}
}
}
Avec Pulumi
Bénéficiez de la puissance des langages de programmation :
# monitoring.py
import pulumi
from pulumi_datadog import Monitor, Dashboard
def create_service_monitoring(service_name: str, config: dict):
"""Crée le monitoring complet pour un service."""
# Alerte latence
latency_alert = Monitor(
f"{service_name}-latency",
name=f"High Latency - {service_name}",
type="metric alert",
query=f"""
avg(last_5m):avg:trace.http.request.duration{{service:{service_name}}}
by {{resource_name}} > {config['latency_threshold']}
""",
message=f"""
Latence élevée sur {service_name}
@slack-{config['team_channel']}
""",
tags=[f"service:{service_name}", f"team:{config['team']}"]
)
# Alerte erreurs
error_alert = Monitor(
f"{service_name}-errors",
name=f"High Error Rate - {service_name}",
type="metric alert",
query=f"""
sum(last_5m):sum:trace.http.request.errors{{service:{service_name}}}.as_count()
/ sum:trace.http.request.hits{{service:{service_name}}}.as_count()
> {config['error_threshold']}
""",
tags=[f"service:{service_name}"]
)
return {
'latency_alert': latency_alert,
'error_alert': error_alert
}
# Utilisation
for service in ['api-gateway', 'user-service', 'payment-service']:
create_service_monitoring(service, services_config[service])
Avec Ansible
Gérez la configuration des agents et collecteurs :
# playbooks/monitoring-agents.yml
---
- name: Configure monitoring agents
hosts: all
become: true
roles:
- role: prometheus_node_exporter
vars:
node_exporter_version: "1.7.0"
node_exporter_web_listen_address: "0.0.0.0:9100"
- role: filebeat
vars:
filebeat_inputs:
- type: log
paths:
- /var/log/application/*.log
json.keys_under_root: true
json.add_error_key: true
tasks:
- name: Deploy custom metrics script
template:
src: templates/custom-metrics.py.j2
dest: /opt/monitoring/custom-metrics.py
mode: '0755'
- name: Configure cron for custom metrics
cron:
name: "Push custom metrics"
minute: "*/5"
job: "/opt/monitoring/custom-metrics.py"
Pour Kubernetes
Utilisez les CRDs du Prometheus Operator :
# Définir comme manifestes Kubernetes standard
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: service-alerts
namespace: monitoring
spec:
groups:
- name: service.rules
interval: 30s
rules:
- alert: ServiceDown
expr: up{job="kubernetes-services"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Service {{ $labels.instance }} down"
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: api-services
spec:
selector:
matchLabels:
monitoring: enabled
endpoints:
- port: metrics
interval: 15s
path: /metrics
Bonnes Pratiques du Monitoring as Code
Structure de code claire
Organisez vos fichiers de manière logique :
monitoring/
├── modules/
│ ├── service-monitoring/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ └── infrastructure-monitoring/
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ └── terraform.tfvars
│ ├── staging/
│ └── production/
├── dashboards/
│ └── templates/
└── alerts/
└── templates/
Tests pour le monitoring
Validez vos configurations :
# Validation syntaxique
terraform validate
# Tests avec Terratest
func TestServiceMonitoring(t *testing.T) {
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: "../modules/service-monitoring",
Vars: map[string]interface{}{
"service_name": "test-service",
"environment": "test",
},
})
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
// Vérifier que les ressources sont créées correctement
monitorId := terraform.Output(t, terraformOptions, "monitor_id")
assert.NotEmpty(t, monitorId)
}
Gestion des secrets
Ne jamais exposer les credentials :
# Mauvais: secret en dur
provider "datadog" {
api_key = "abc123..." # ❌ JAMAIS
}
# Bon: depuis les variables d'environnement
provider "datadog" {
api_key = var.datadog_api_key
}
# Encore mieux: depuis un gestionnaire de secrets
data "vault_generic_secret" "datadog" {
path = "secret/monitoring/datadog"
}
provider "datadog" {
api_key = data.vault_generic_secret.datadog.data["api_key"]
}
Documentation des conventions
Créez un CONTRIBUTING.md :
# Guide de contribution au Monitoring as Code
## Ajouter un nouveau service
1. Créer un fichier dans `services/{service-name}.tf`
2. Utiliser le module `service-monitoring`
3. Configurer les seuils appropriés
4. Ouvrir une PR avec les tests passing
## Conventions de nommage
- Monitors: `{service}-{metric}-{environment}`
- Dashboards: `{team}-{service}-overview`
- Tags obligatoires: `team`, `service`, `environment`
## Review checklist
- [ ] terraform validate passé
- [ ] terraform plan sans erreur
- [ ] Seuils justifiés
- [ ] Runbook URL définie
Nettoyage régulier
Supprimez les ressources obsolètes :
# Identifier les ressources orphelines
terraform state list | grep legacy
# Supprimer proprement
terraform destroy -target=module.legacy_service_monitoring
Conclusion
Le Monitoring as Code représente la maturation naturelle des pratiques d'observabilité dans un monde cloud-native.
En appliquant les mêmes principes qui ont fait le succès de l'Infrastructure as Code, vous gagnez en consistance, traçabilité et résilience.
Cette approche demande un investissement initial mais génère des bénéfices durables en maintenabilité et fiabilité opérationnelle.
Commencez par codifier vos alertes les plus critiques, puis étendez progressivement le périmètre.
WizStatus expose des APIs permettant d'intégrer la configuration de monitoring dans vos workflows IaC existants, facilitant l'adoption du Monitoring as Code.