Des recyclages IIS fréquents et des pauses GC peuvent ruiner l’expérience utilisateur sur une API .NET. Voici un plan d’action concret pour stabiliser Windows Server 2016 : piloter la déduplication, isoler VSS et affiner le GC .NET afin d’éliminer les pointes CPU/E/S et les redémarrages inopinés.
Contexte et symptômes
Votre API .NET s’exécute sur six hôtes Windows Server 2016 derrière un load‑balancer. Les processus IIS (w3wp.exe
) atteignent un seuil mémoire d’environ 15 Go et sont recyclés, ce qui provoque des déconnexions et des indisponibilités ponctuelles. Les analyses montrent des cycles de Garbage Collection (GC) très fréquents, mais la source du GC n’est pas toujours claire : s’agit‑il du GC du CLR (.NET) ou du GC du service de déduplication/VSS (stockage) ?
Vue d’ensemble des mesures recommandées
Axe | Mesures proposées | Effet recherché |
---|---|---|
Service de déduplication | • Laisser la GC « normale » hebdomadaire (paramètre par défaut). • Lancer la Full GC uniquement à la demande : Start-DedupJob <volume> -Type GarbageCollection -Full • Bloquer l’exécution automatique de la Full GC : DeepGCInterval = 0xffffffff dans :HKLM\SYSTEM\CurrentControlSet\Services\ddpsvc\Settings (ou HKLM\CLUSTER\Dedup en cluster). | Éviter les pics d’E/S et de CPU dus à la Full GC, qui déclenchent ralentissements et recyclages. |
VSS (Volume Shadow Copy) | Déplacer la zone de diff (« shadow storage ») sur un volume dédié via vssadmin. | Limiter les suppressions de snapshots qui entraînent du ménage intensif et aggravent fragmentation/mémoire. |
Diagnostic : identifier la source des pauses
Avant toute action, distinguez les trois familles d’événements :
- GC .NET : cycles du CLR qui compactent/collectent les générations 0/1/2 et le LOH (Large Object Heap).
- GC Déduplication : tâches de maintenance du service Data Deduplication (dont la Full GC ou Deep GC), très intensives en CPU/E/S.
- Activité VSS : création/suppression de snapshots qui déclenchent du nettoyage sur le stockage.
Compteurs PerfMon à surveiller
Catégorie | Compteurs clés | Interprétation |
---|---|---|
.NET CLR Memory (instance w3wp ) | % Time in GC, Gen 2 Collections, Allocated Bytes/sec, # Bytes in all Heaps, LOH size | Si % Time in GC et Gen 2 s’envolent, la pression mémoire applicative est réelle. |
Process (w3wp ) & Memory | Private Bytes, Working Set, Committed Bytes, Available MBytes | Corréler la montée des Private Bytes au timing des recyclages IIS. |
Data Deduplication | Compteurs Jobs, Chunk Store, Garbage Collection (file d’attente, débit) | Un pic côté déduplication pendant les incidents oriente vers la Full GC du stockage. |
VSS | Événements de création/suppression de clichés, latence disque | Suppression massive de clichés = ménage et E/S soutenues. |
Plan d’action priorisé
Bloquer et piloter la Full GC de déduplication
Sur des volumes applicatifs très sollicités, la Full GC du service de déduplication (ddpsvc
) peut provoquer des tempêtes d’E/S et un CPU élevé. Le principe est de :
- Empêcher son déclenchement automatique.
- La lancer manuellement et nœud par nœud pendant une fenêtre de maintenance (avec drainage du trafic par le load‑balancer).
Désactivation de l’exécution automatique
Définissez la valeur DeepGCInterval
à 0xffffffff
(DWORD) pour bloquer la Full GC automatique :
reg add HKLM\SYSTEM\CurrentControlSet\Services\ddpsvc\Settings /v DeepGCInterval /t REG_DWORD /d 0xffffffff /f
En environnement cluster :
reg add HKLM\CLUSTER\Dedup /v DeepGCInterval /t REG_DWORD /d 0xffffffff /f
Bonnes pratiques : consigner le changement (Change Advisory), redémarrer le service ddpsvc
si nécessaire et documenter le rollback (revenir à l’intervalle par défaut).
Lancer une Full GC manuelle et contrôlée
Sur un seul nœud à la fois, drainé du load‑balancer :
# Exemple : lancer une Full GC sur le volume E:
Start-DedupJob E: -Type GarbageCollection -Full
# Pour suivre la progression
Get-DedupJob
Attendez la fin, effectuez une vérification métier basique, puis réadmettez le nœud dans le pool et passez au nœud suivant. Cette orchestration évite un impact global.
Isoler la zone de diff VSS sur un volume dédié
Placez le « shadow storage » sur un disque distinct (idéalement rapide) pour éviter que les suppressions de clichés ne perturbent les volumes applicatifs.
# Visualiser la configuration actuelle
vssadmin list shadowstorage
# Définir une zone dédiée (ex. : clichés du volume E: stockés sur V:)
vssadmin add shadowstorage /for=E: /on=V: /maxsize=200GB
# Adapter la taille si nécessaire
vssadmin resize shadowstorage /for=E: /on=V: /maxsize=300GB
Dimensionnez la zone de diff selon la fréquence des sauvegardes, la volumétrie et la fenêtre de rétention des clichés.
Optimiser le GC .NET et les recyclages IIS
L’objectif est de réduire la pression GC applicative tout en évitant les recyclages par seuil mémoire trop agressif.
Activer le GC serveur et la GC en arrière‑plan
Dans web.config
ou app.config
:
<configuration>
<runtime>
<gcServer enabled="true"/>
<gcConcurrent enabled="true"/>
</runtime>
</configuration>
Pourquoi : le GC serveur parallélise les collectes et exploite mieux les cœurs, avantageux sous charge.
Réduire l’impact des pauses pendant les pics
Autour d’événements critiques (pics d’appels, batchs synchrones), utilisez un mode de latence adapté :
using System;
using System.Runtime;
using (new SustainedLowLatencyScope())
{
// Code critique (ex. : traitement temps réel)
// ...
}
public sealed class SustainedLowLatencyScope : IDisposable
{
private readonly GCLatencyMode _previous = GCSettings.LatencyMode;
public SustainedLowLatencyScope()
=> GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency;
public void Dispose()
=> GCSettings.LatencyMode = _previous;
}
Réservez ce mode aux fenêtres ciblées ; en continu, il peut augmenter la pression mémoire.
Gérer le Large Object Heap (LOH) avec parcimonie
Les allocations > 85 Ko vont au LOH. Si la fragmentation devient un problème, vous pouvez, à froid (fenêtre de maintenance), demander une compaction :
using System.Runtime;
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();
À éviter en pleine charge, car la compaction peut rallonger une pause.
Repenser les seuils de recyclage IIS
Paramètre App Pool | Recommandation | Commande/appcmd |
---|---|---|
Recyclage par mémoire privée | Éviter 15 Go arbitraires ; commencer par 0 (illimité) ou relever le seuil après observation. | appcmd set apppool "ApiPool" /recycling.periodicRestart.privateMemory:0 |
Recyclage périodique (minutes) | Désactiver le timer par défaut, planifier des heures creuses spécifiques. | appcmd set apppool "ApiPool" /recycling.periodicRestart.time:00:00:00 |
Plages horaires ciblées | Un recyclage contrôlé, 1 fois/jour max, hors charge, après vidange des sessions. | appcmd set apppool "ApiPool" /+recycling.periodicRestart.schedule.[value='02:30:00'] |
Mode de démarrage | AlwaysRunning + preloadEnabled pour réduire l’impact au redémarrage. | appcmd set apppool "ApiPool" /startMode:AlwaysRunning appcmd set app "Default Web Site/Api" /preloadEnabled:true |
Astuce : testez l’élévation du seuil mémoire par paliers (ex. 22 Go → 28 Go) et corrélez au % Time in GC et au Gen 2.
Mettre à jour le système et le runtime
- Appliquer les derniers Cumulative Updates Windows Server 2016.
- Utiliser un runtime récent (.NET Framework 4.8 ou .NET 6/7 LTS) lorsque possible ; les optimisations GC y sont plus efficientes (compaction, background GC, LOH).
Désactiver la déduplication si le gain est marginal
Sur un volume applicatif avec peu de fichiers redondants, la déduplication apporte peu mais coûte en maintenance. Mesurez le taux de gain ; s’il est faible, envisagez la désactivation :
# Vérifier l’état
Get-DedupVolume -Volume E:
# Désactiver (après validation et sauvegarde)
Disable-DedupVolume -Volume E:
Dans certains contextes, retirer la fonctionnalité FS‑Data‑Deduplication du rôle de fichiers peut simplifier l’exploitation (décision à cadrer selon vos exigences de stockage).
Méthodologie de déploiement sans interruption
- Pré‑production : reproduire la charge (JMeter, k6…), capturer latence p95/p99, % GC, CPU, E/S.
- Canari : appliquer le changement sur 1 nœud (drainé LB), valider fonctionnel, remettre en pool.
- Montée en charge : étendre à 2 → 3 → 6 nœuds en surveillant l’APM et les histogrammes de latence.
Observabilité et corrélations utiles
Signal | Où regarder | Ce que cela indique |
---|---|---|
Spikes CPU + disques saturés | PerfMon, logs déduplication | Tâche GarbageCollection de dédup en cours. |
% Time in GC > 20–30 % soutenu | .NET CLR Memory (w3wp) | Pression mémoire applicative, allocations massives/LOH. |
Suppression en rafale de clichés | VSS + monitoring sauvegarde | Zone de diff trop petite ou co‑localisée sur un volume chaud. |
Recyclages corrélés aux pics | Logs IIS, Event Viewer (App Pool) | Seuil mémoire/recycle timer trop agressif. |
Runbook d’incident : quoi faire pendant la crise
- Isoler : sortir 1 nœud du pool LB (drain), vérifier CPU/disque et % Time in GC.
- Confirmer la cause : si dédup en Full GC, laisser terminer sur ce nœud drainé ; si GC .NET, inspecter Allocated Bytes/sec et rechigner aux recyclages immédiats sauf fuite manifeste.
- Stabiliser : élever temporairement le seuil mémoire de l’App Pool ou le mettre à 0, désactiver les recyclages périodiques.
- Remédiation : déplacer le shadow storage VSS, bloquer la Full GC automatique, planifier une maintenance.
- Retour à la normale : réadmettre le nœud, supervision renforcée 24–48 h.
Exemples de scripts PowerShell prêts à l’emploi
Désactiver l’exécution automatique de la Full GC (déduplication)
$path = "HKLM:\SYSTEM\CurrentControlSet\Services\ddpsvc\Settings"
New-Item -Path $path -Force | Out-Null
New-ItemProperty -Path $path -Name "DeepGCInterval" -Value 0xffffffff -PropertyType DWord -Force | Out-Null
Restart-Service ddpsvc -Force
Write-Host "Full GC automatique désactivée."
Lancer une Full GC manuelle pendant une fenêtre
param(
[Parameter(Mandatory=$true)][string]$Volume = "E:"
)
Write-Host "Démarrage Full GC sur $Volume"
Start-DedupJob $Volume -Type GarbageCollection -Full
do {
$job = Get-DedupJob | Where-Object { $_.Type -eq "GarbageCollection" -and $_.Volume -eq $Volume }
Start-Sleep -Seconds 15
Write-Host ("Progression : {0} %" -f $job.Progress)
} while ($job -and $job.Status -eq "Running")
Write-Host "Full GC terminée."
Déplacer la zone de diff VSS vers un volume dédié
param(
[string]$For = "E:",
[string]$On = "V:",
[string]$Max = "200GB"
)
vssadmin add shadowstorage /for=$For /on=$On /maxsize=$Max
vssadmin list shadowstorage
Réglages IIS essentiels
$pool = "ApiPool"
& "$env:windir\system32\inetsrv\appcmd.exe" set apppool $pool /recycling.periodicRestart.privateMemory:0
& "$env:windir\system32\inetsrv\appcmd.exe" set apppool $pool /recycling.periodicRestart.time:00:00:00
& "$env:windir\system32\inetsrv\appcmd.exe" set apppool $pool /startMode:AlwaysRunning
# Exemple d'ajout d'une fenêtre de recycle contrôlée à 02h30
& "$env:windir\system32\inetsrv\appcmd.exe" set apppool $pool "/+recycling.periodicRestart.schedule.[value='02:30:00']"
Conseils d’architecture et d’exploitation
- Orchestration LB : implémentez un drain par nœud (connexion persistante + santé) pour chaque maintenance dédup/VSS.
- SLO de latence : suivez p95/p99 par route API, pas uniquement la moyenne.
- Capacité mémoire : budgétez la mémoire par w3wp = Overhead CLR + caches + LOH + buffers réseau + marge de 20–30 %.
- Allocations : limitez les allocations transitoires massives (éviter
string
concat, préférerStringBuilder
, pooling d’objets,ArrayPool<T>
). - Logs : enrichissez les journaux de recyclage (ID d’App Pool, raison, mémoire, trafic) pour corréler rapidement.
Checklist de validation avant production
- PerfMon : profils .NET, Dédup, VSS établis et seuils d’alerte posés.
- Full GC dédup automatique bloquée et procédure manuelle documentée.
- Shadow storage déplacé et dimensionné, sauvegardes vérifiées.
- GC serveur activé, recycles IIS cadrés, preload actif.
- Tests de charge réalisés (≥ 1 h) avec captures de p95/p99 et % GC.
- Déploiement progressif planifié (1 → 6 nœuds) et rollback clair.
FAQ express
Le GC .NET suffit‑il à expliquer les pics ?
Pas toujours. Sur des serveurs de fichiers dédupliqués, la Full GC de déduplication est un classique des pannes « fantômes ». D’où l’importance de séparer GC applicatif et maintenance stockage.
Dois‑je augmenter indéfiniment la RAM ou le seuil IIS ?
Non. Relevez le seuil pour éviter des recyclages inutiles, mais corrigez surtout la cause (dédup/VSS, allocations) ; sinon la latence restera erratique.
Faut‑il désactiver la déduplication partout ?
Non. Évaluez le gain réel. Sur des volumes applicatifs chauds, elle est souvent contre‑productive ; sur des volumes froids/archivage, elle reste pertinente.
Conclusion
En combinant la désactivation de la Full GC automatique de la déduplication, l’isolation du shadow storage VSS et un tuning ciblé du GC .NET (GC serveur, fenêtres de recyclage maîtrisées, modes de latence adaptés), vous réduisez drastiquement les pauses GC et la probabilité de recyclages IIS. Le résultat : une API .NET stable sous forte charge, moins d’incidents de disponibilité et une expérience utilisateur nettement plus fluide.