Inbetriebnahme

Die Anwendung wird entweder als veröffentlichte .NET-9-Anwendung (Windows oder Linux) oder als Docker-Image ausgeliefert. Wählen Sie einen der beiden Wege.

Voraussetzungen

  • Eine SQL-Server-Instanz für die Überwachungsdatenbank (der eigene Metadatenspeicher der Anwendung). Version 2017 oder neuer. SQL Express genügt. Empfohlen: eine eigene Datenbank namens SqlServerHealthMonitor.
  • Ein Dienstkonto, das CREATE DATABASE ausführen darf (nur beim ersten Start), oder eine leere, bereits angelegte Datenbank mit vollen Lese- und Schreibrechten.
  • Für jeden überwachten SQL Server: ein Konto mit geringen Rechten, das VIEW SERVER STATE, VIEW DATABASE STATE sowie SELECT auf msdb.dbo.sysjobs* besitzt, falls Sie die SQL-Agent-Aufträge sehen möchten.
    • SQL Server 2022 und neuer hat diese Berechtigungen aufgeteilt. Bei Instanzen ab 2022 vergeben Sie stattdessen VIEW SERVER PERFORMANCE STATE und VIEW DATABASE PERFORMANCE STATE — mit VIEW SERVER STATE allein scheitern die Index-Scans mit „The VIEW SERVER PERFORMANCE STATE permission was denied“ (Fehler 300).
    • Das Konto braucht außerdem einen Benutzer in jeder Datenbank, die durchsucht werden soll. Fehlt er, schlägt USE [db] sofort fehl (Fehler 916) und die Datenbank wird auf /performance/index-health als übersprungen ausgewiesen.
    • Das Fehlerprotokoll zu lesen (/error-log) erfordert zwei Berechtigungen, die VIEW SERVER STATE nicht einschließt, sowie einen Benutzer in master — dort liegen die Prozeduren. Siehe den zweiten Block unten. Ohne sie meldet genau diese eine Seite, was zu vergeben ist; alles andere bleibt unberührt.
    • Betriebssystem-Metriken (CPU, Arbeitsspeicher und Datenträger des Hosts, angezeigt unter /storage) werden per WMI vom Windows-Host gelesen, nicht über die SQL-Verbindung. Sie benötigen daher eine Windows-Berechtigung statt einer SQL-Berechtigung — siehe „Betriebssystem-Metriken über WMI“ weiter unten. Ohne sie bleibt nur diese eine Kachel leer.
-- Auf einer überwachten Instanz ab SQL Server 2022
GRANT VIEW SERVER PERFORMANCE STATE TO [monitoring_login];
GRANT VIEW ANY DEFINITION TO [monitoring_login];

-- Je Datenbank, die durchsucht werden soll
USE [YourDatabase];
CREATE USER [monitoring_login] FOR LOGIN [monitoring_login];
GRANT VIEW DATABASE PERFORMANCE STATE TO [monitoring_login];  -- ab 2022
-- GRANT VIEW DATABASE STATE TO [monitoring_login];           -- 2019 und älter

Der Zugriff auf das Fehlerprotokoll (/error-log) ist davon getrennt, denn sys.xp_readerrorlog und sys.xp_enumerrorlogs liegen in master und werden von keiner VIEW ... STATE-Berechtigung abgedeckt. Die Anmeldung braucht einen Benutzer in master, damit die Berechtigungen überhaupt irgendwo landen können — dieser Schritt wird am häufigsten übersehen:

USE master;
CREATE USER [monitoring_login] FOR LOGIN [monitoring_login];
GRANT EXECUTE ON sys.xp_readerrorlog  TO [monitoring_login];   -- Protokoll lesen
GRANT EXECUTE ON sys.xp_enumerrorlogs TO [monitoring_login];   -- Archive auflisten

Eine Mitgliedschaft in securityadmin funktioniert ebenfalls und wird in den meisten Anleitungen empfohlen, doch damit lassen sich Anmeldungen anlegen und Kennwörter zurücksetzen — erheblich mehr, als ein Überwachungskonto besitzen sollte. Der Grund für diesen Ratschlag sind die Hüllen sp_readerrorlog und sp_enumerrorlogs: Sie enthalten eine interne securityadmin-Prüfung, die keine Berechtigung erfüllt. Der Monitor ruft genau deshalb die darunterliegenden erweiterten Prozeduren direkt auf und kommt ohne die Rolle aus.

Betriebssystem-Metriken über WMI

Die Host-Kachel auf /storage beantwortet die Frage, die die DMVs nicht beantworten können: Was läuft sonst noch auf diesem Rechner? SQL Server meldet die eigene CPU-Last und den eigenen Speicher — ein Sicherungsagent, ein Virenscan oder eine zweite Instanz taucht dort nur als Warten auf, nie als Ursache.

Agentenlos bleibt es trotzdem: Auf dem überwachten Rechner wird nichts installiert, der Monitor fragt WMI so ab, wie es der Systemmonitor tut. Die Abfrage läuft unter dem Dienstkonto des Monitors, es müssen also nirgends Zugangsdaten hinterlegt werden. Der Preis dafür: Das Konto muss auf dem überwachten Host bekannt sein — ein Host in einer anderen Domäne oder in einer Arbeitsgruppe antwortet mit „Zugriff verweigert“, und das Protokoll nennt genau diesen Grund.

Auf jedem überwachten Windows-Host, für das Dienstkonto des Monitors:

  • Mitgliedschaft in Leistungsüberwachungsbenutzer (und, für entferntes DCOM, in Distributed COM-Benutzer).
  • Remoteaktivierung für den WMI-Namensraum root\cimv2 (wmimgmt.msc → WMI-Steuerung → Eigenschaften → Sicherheit).
  • Die eingehende Firewallregel Windows-Verwaltungsinstrumentation (WMI eingehend) aktiviert.

In zwei Fällen ist das bewusst nicht verfügbar: Azure SQL Database und Managed Instance haben keinen Host, den wir auslesen könnten (der Monitor weiß das und überspringt sie), und ein Monitor, der im Linux-Container läuft, kann WMI überhaupt nicht nutzen — die Kachel sagt das dann, statt Nullen anzuzeigen. Die Erfassung lässt sich mit OsMetrics:Enabled = false ganz abschalten; Aufbewahrungsdauer und Zeitlimit je Host liegen im selben Abschnitt (Standardwerte: 30 Tage, 10 Sekunden).

Windows — physisch oder virtuell

# Als Dienstkonto
dotnet publish -c Release -r win-x64 --self-contained false -o C:\Apps\SqlServerHealthMonitor

# Konfiguration für den ersten Start
cd C:\Apps\SqlServerHealthMonitor
notepad appsettings.json
# ConnectionStrings:DefaultConnection auf Ihre Überwachungsdatenbank setzen
# SecuritySettings:RequireHttps und SecuritySettings:HttpsPort prüfen

# Interaktiv starten und prüfen
.\SqlServerHealthMonitor.exe

Wenn die Anwendung startet und Now listening on: https://[::]:8443 protokolliert, ist alles in Ordnung. Rufen Sie https://localhost:8443 auf (die Warnung wegen des selbstsignierten Zertifikats beim ersten Mal bestätigen).

Als Windows-Dienst

Der MSI-Installer (docs/installer.md) erledigt alle folgenden Schritte für Sie und ist der empfohlene Weg. Nutzen Sie die manuellen Befehle hier nur, wenn Sie die Installation lieber selbst skripten.

# Den interaktiven Lauf beenden, dann:
sc.exe create "SqlServerHealthMonitor" `
    binPath= "C:\Apps\SqlServerHealthMonitor\SqlServerHealthMonitor.exe" `
    start= auto `
    DisplayName= "SQL Server Health Monitor"
sc.exe description "SqlServerHealthMonitor" "Monitors SQL Server fleet health and alerts on degradations."
sc.exe start "SqlServerHealthMonitor"

Der Dienst läuft standardmäßig als LocalSystem. Für den Betrieb unter einem Domänenkonto (empfohlen, wenn die Überwachungsdatenbank über integrierte Sicherheit erreicht wird):

sc.exe config "SqlServerHealthMonitor" obj= "DOMAIN\monitor-svc" password= "..."
sc.exe stop "SqlServerHealthMonitor"
sc.exe start "SqlServerHealthMonitor"

Linux — systemd

sudo dotnet publish -c Release -r linux-x64 --self-contained false -o /opt/sshm
sudo useradd --system --no-create-home sshm
sudo chown -R sshm:sshm /opt/sshm

sudo tee /etc/systemd/system/sshm.service <<'EOF'
[Unit]
Description=SQL Server Health Monitor
After=network.target

[Service]
WorkingDirectory=/opt/sshm
ExecStart=/usr/bin/dotnet /opt/sshm/SqlServerHealthMonitor.dll
User=sshm
Restart=on-failure
RestartSec=10
Environment=ASPNETCORE_ENVIRONMENT=Production
SyslogIdentifier=sshm

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now sshm
sudo journalctl -u sshm -f

Docker

docker run -d --name sshm \
    -p 8443:8443 \
    -v sshm-keys:/root/.local/share/SqlServerHealthMonitor \
    -e ASPNETCORE_ENVIRONMENT=Production \
    -e ConnectionStrings__DefaultConnection="Server=db;Database=SqlServerHealthMonitor;User Id=sshm;Password=...;TrustServerCertificate=true" \
    sqlserverhealthmonitor:latest

Empfohlen: Behalten Sie die Volume-Einbindung -v sshm-keys:/root/.local/share/SqlServerHealthMonitor bei. Ohne sie werden die DataProtection-Schlüssel bei jedem Containerneustart neu erzeugt, was alle Benutzer abmeldet und das selbstsignierte Zertifikat neu ausstellt. Ihre verschlüsselten Spalten sind davon nicht betroffen — die schützt der feste Encryption:Key, nicht der Schlüsselbund. Siehe Sicherung & Wiederherstellung.

Hinter einem Reverse Proxy (Traefik / nginx)

Wenn TLS am Proxy terminiert wird, schalten Sie die HTTPS-Bindung der Anwendung ab:

// appsettings.Production.json
{
  "SecuritySettings": {
    "RequireHttps": false,
    "BindHttps": false
  }
}

… und lassen den Proxy per einfachem HTTP auf Port 8080 mit dem Container sprechen.

HTTPS und Zertifikate

Standardverhalten: Beim ersten Start wird ein selbstsigniertes RSA-2048-Zertifikat erzeugt und unter %LOCALAPPDATA%\SqlServerHealthMonitor\Certs\sshm-server.pfx (Windows) bzw. ~/.local/share/SqlServerHealthMonitor/Certs/sshm-server.pfx (Linux) abgelegt. Das Startprotokoll nennt den genauen Pfad samt einer Warnung „REPLACE FOR PRODUCTION“.

Um ein eigenes Zertifikat zu verwenden (von einer öffentlichen oder internen CA):

{
  "SecuritySettings": {
    "BindHttps": true,
    "HttpsPort": 8443,
    "CertificatePath": "C:\\certs\\sshm.pfx",
    "CertificatePassword": "..."
  }
}

Um das selbstsignierte Zertifikat neu erzeugen zu lassen (etwa nach einer Namensänderung des Hosts): die .pfx löschen und neu starten. Es wird automatisch ein neues mit den aktuellen alternativen Antragstellernamen erstellt.

Erstes Administratorkonto

Beim ersten Start erkennt die Anmeldeseite, dass noch kein Administrator existiert, und leitet auf /Account/Setup weiter. Wählen Sie einen Benutzernamen und ein starkes Kennwort — daraus wird der erste Administrator, der anschließend unter /Settings/Users weitere Benutzer anlegen kann.

Ist appsettings.json:InitialAdmin:Password gesetzt, wird dieses Kennwort statt des Einrichtungsablaufs verwendet (eine Warnung wird protokolliert, wenn es schwach oder ein bekannter Platzhalter ist). Existiert InitialAdmin ohne Kennwort, wird ein starkes Kennwort erzeugt und in INITIAL_ADMIN_PASSWORD.txt im Inhaltsverzeichnis der Anwendung abgelegt — bewusst nicht im Anwendungsprotokoll. Lesen Sie diese Datei, melden Sie sich an, ändern Sie das Kennwort und löschen Sie die Datei. (Lässt sich die Datei nicht schreiben, wird das Kennwort ersatzweise protokolliert.)

Nach der Anmeldung: Zwei-Faktor-Anmeldung unter /Account/Security aktivieren. Für jeden Administrator empfohlen.

Geheimnisse und sichere Konfiguration

Halten Sie Geheimnisse aus appsettings.json heraus (die Datei liegt in der Versionsverwaltung). Jeder Konfigurationswert lässt sich über eine Umgebungsvariable setzen, mit __ (doppelter Unterstrich) für die Verschachtelung — diese überschreiben die JSON-Dateien automatisch:

Geheimnis Umgebungsvariable
Datenbankverbindung ConnectionStrings__DefaultConnection
Überwachungsdatenbank ConnectionStrings__MonitoringDatabase
Kennwort des ersten Administrators InitialAdmin__Password
OIDC-Clientgeheimnis OidcSettings__ClientSecret
Öffentlicher Lizenzschlüssel Licensing__PublicKeyBase64
# Docker
docker run -e ConnectionStrings__DefaultConnection="Server=db;Database=SQLSpa;User Id=svc;Password=…;TrustServerCertificate=true" …

# systemd-Unit
Environment=ConnectionStrings__DefaultConnection=Server=db;Database=SQLSpa;…

Hinweise:

  • Produktivschutz: Die Anwendung verweigert den Start, wenn ConnectionStrings:DefaultConnection in der Umgebung Production leer ist — setzen Sie den Wert vor dem ersten Start.
  • Lokale Entwicklung: dotnet user-secrets verwenden (das Projekt hat bereits eine UserSecretsId), statt appsettings.Development.json zu bearbeiten.
  • Geheimnisse der Benachrichtigungskanäle (E-Mail/Teams/PagerDuty) liegen verschlüsselt in der Datenbank, nicht in der Konfiguration. Sie werden unter Einstellungen → Benachrichtigungen gepflegt. Werte in appsettings dienen nur dazu, die Datenbank beim ersten Start einmalig vorzubelegen.
  • Encryption:Key ist es, was die gespeicherten Verbindungszeichenfolgen und Benachrichtigungsgeheimnisse verschlüsselt (AES-256-GCM). Lassen Sie ihn ungesetzt, um den eingebauten Standard zu nutzen, oder setzen Sie einen eigenen 32-Byte-Base64-Schlüssel — und sichern Sie diesen dann gemeinsam mit der Datenbank, denn ohne ihn lassen sich diese Spalten von nichts mehr entschlüsseln (siehe backup-and-restore.md).
  • DataProtection-Schlüssel decken allein den Schlüsselbund des Frameworks ab: Anmelde-Cookies, Antiforgery-Token, das selbstsignierte Zertifikat. Geht dieser Ordner verloren, kostet das eine erneute Anmeldung, keine Daten.