251 lines
8.8 KiB
Markdown
251 lines
8.8 KiB
Markdown
# Betrieb mit mehreren Ollama-Instanzen
|
||
|
||
Der Agent kann bis zu 64 voneinander unabhängige Ollama-Server als gemeinsamen Inferenz-Pool verwenden. Jeder Node lädt das vollständige Chat- und – sofern für RAG erforderlich – Embedding-Modell lokal. Der Pool erhöht damit den **Gesamtdurchsatz und die Ausfallsicherheit**; er teilt ein einzelnes Modell nicht über mehrere Rechner auf.
|
||
|
||
## Architektur
|
||
|
||
```text
|
||
GLPI AI Agent
|
||
Queue / Worker / Policies
|
||
|
|
||
v
|
||
Ollama Pool Router
|
||
| | |
|
||
Node 1 Node 2 Node 3
|
||
```
|
||
|
||
Jeder logische KI-Lauf – Kategorie, Priorität, Status, Antwort oder Eskalation – wird einem verfügbaren Node zugewiesen. Bei retryfähigen Netzwerk- oder Serverfehlern kann derselbe Request auf einem anderen kompatiblen Node wiederholt werden.
|
||
|
||
## Voraussetzungen je Node
|
||
|
||
Auf allen Nodes sollten installiert sein:
|
||
|
||
```text
|
||
Chat-Modell: OLLAMA_MODEL
|
||
Embedding-Modell: OLLAMA_EMBEDDING_MODEL
|
||
```
|
||
|
||
Bei `OLLAMA_REQUIRE_SAME_MODEL_DIGEST=true` prüft der Agent über `/api/tags`, dass alle erreichbaren Nodes exakt dieselben Modelldigests melden. Schon ein abweichender Digest macht den gesamten divergierenden Pool fail-closed, damit identische Tickets nicht aufgrund verschiedener Modellstände unterschiedlich bewertet werden.
|
||
|
||
Für Lenovo-Systeme mit integrierter Radeon-Grafik und gemeinsamem RAM ist als Ausgangspunkt sinnvoll:
|
||
|
||
```env
|
||
OLLAMA_NODE_MAX_INFLIGHT=1
|
||
OLLAMA_ROUTING_MODE=least_inflight
|
||
OLLAMA_KEEP_ALIVE=10m
|
||
OLLAMA_THINK=false
|
||
```
|
||
|
||
Der Gesamtdurchsatz wird zusätzlich durch `WORKERS` begrenzt. Mit drei Nodes und `WORKERS=2` können höchstens zwei Ticketpipelines gleichzeitig Inferenz anfordern. Für einen Lasttest mit drei gleichartigen Nodes ist daher beispielsweise sinnvoll:
|
||
|
||
```env
|
||
WORKERS=3
|
||
OLLAMA_NODE_MAX_INFLIGHT=1
|
||
```
|
||
|
||
Die Analysestufen eines einzelnen Tickets bleiben aus fachlichen Gründen weitgehend geordnet. Der größte Poolnutzen entsteht deshalb bei mehreren gleichzeitig wartenden Tickets oder Eskalationsläufen.
|
||
|
||
## Minimale Pool-Konfiguration
|
||
|
||
```env
|
||
OLLAMA_URLS=http://10.20.30.21:11434,http://10.20.30.22:11434,http://10.20.30.23:11434
|
||
OLLAMA_NODE_NAMES=lenovo-01,lenovo-02,lenovo-03
|
||
OLLAMA_ROUTING_MODE=least_inflight
|
||
OLLAMA_NODE_MAX_INFLIGHT=1
|
||
OLLAMA_FAILOVER_ENABLED=true
|
||
OLLAMA_FAILOVER_ATTEMPTS=0
|
||
OLLAMA_REQUIRE_SAME_MODEL_DIGEST=true
|
||
OLLAMA_REQUIRE_EMBEDDING_MODEL=true
|
||
```
|
||
|
||
`OLLAMA_FAILOVER_ATTEMPTS=0` bedeutet: maximal alle konfigurierten Nodes versuchen.
|
||
|
||
## Routing-Modi
|
||
|
||
### `least_inflight`
|
||
|
||
Empfohlener Standard. Der Node mit den wenigsten laufenden Requests wird bevorzugt. Bei gleicher Auslastung wird zunächst der bislang seltener verwendete Node gewählt; anschließend dienen mittlere Laufzeit und Name als stabile Tie-Breaker. Dadurch verteilt sich auch serieller Verkehr über gleichartige Nodes.
|
||
|
||
```env
|
||
OLLAMA_ROUTING_MODE=least_inflight
|
||
```
|
||
|
||
### `round_robin`
|
||
|
||
Requests werden zyklisch verteilt. Dieser Modus ist einfach, berücksichtigt aber Leistungsunterschiede nur begrenzt.
|
||
|
||
```env
|
||
OLLAMA_ROUTING_MODE=round_robin
|
||
```
|
||
|
||
### `weighted`
|
||
|
||
Geeignet für gemischte Hardware. Die Gewichte stehen positionsgleich zu `OLLAMA_URLS`.
|
||
|
||
```env
|
||
OLLAMA_URLS=http://lenovo-1:11434,http://lenovo-2:11434,http://gpu-server:11434
|
||
OLLAMA_NODE_NAMES=lenovo-1,lenovo-2,gpu-server
|
||
OLLAMA_NODE_WEIGHTS=1,1,6
|
||
OLLAMA_ROUTING_MODE=weighted
|
||
```
|
||
|
||
### `fastest_recent`
|
||
|
||
Bevorzugt Nodes mit der niedrigsten gleitenden mittleren Request-Laufzeit. Neue oder zurückgekehrte Nodes ohne Messwert werden zunächst einmal vermessen, damit sie nicht dauerhaft verhungern.
|
||
|
||
```env
|
||
OLLAMA_ROUTING_MODE=fastest_recent
|
||
```
|
||
|
||
## Startverhalten und Docker Compose
|
||
|
||
Der Webserver startet unabhängig vom Pool. Vor Knowledge-Initialisierung und Ticketverarbeitung wartet der Agent wiederholt auf mindestens einen gesunden, kompatiblen Ollama-Node. Ein noch bootender Node führt dadurch nicht mehr zu einem einmaligen dauerhaften Knowledge-Fehler; im Dashboard bleibt der Zustand währenddessen sichtbar.
|
||
|
||
Die Compose-Dateien besitzen keine harte Abhängigkeit des Agenten vom mitgelieferten `ollama`-Service mehr. Für ausschließlich externe Nodes kann gezielt nur der Agent gestartet werden:
|
||
|
||
```bash
|
||
docker compose up -d agent
|
||
```
|
||
|
||
`OLLAMA_URLS` hat Vorrang vor dem weiterhin aus Kompatibilitätsgründen gesetzten `OLLAMA_URL=http://ollama:11434`. Bei `docker compose up -d` ohne Servicenamen wird der gebündelte lokale Ollama-Service weiterhin mitgestartet, aber nur verwendet, wenn seine URL im effektiven Pool steht.
|
||
|
||
## Healthchecks und Cooldown
|
||
|
||
```env
|
||
OLLAMA_NODE_HEALTH_INTERVAL=15s
|
||
OLLAMA_NODE_FAILURE_COOLDOWN=30s
|
||
OLLAMA_NODE_REQUEST_TIMEOUT=10m
|
||
```
|
||
|
||
Der Healthcheck ruft `/api/tags` auf und prüft:
|
||
|
||
- HTTP-Erreichbarkeit,
|
||
- Vorhandensein des Chat-Modells,
|
||
- Vorhandensein des Embedding-Modells,
|
||
- Modelldigests,
|
||
- Kompatibilität mit den übrigen Nodes.
|
||
|
||
Ein retryfähiger Fehler setzt den betroffenen Node in einen Cooldown. Währenddessen erhält er keine neuen Requests. Ein späterer erfolgreicher Healthcheck macht ihn wieder sichtbar; der Cooldown läuft dennoch bis zu seinem Ende, um Flapping zu dämpfen.
|
||
|
||
## Failover
|
||
|
||
Failover wird ausgelöst bei:
|
||
|
||
- Verbindungsfehlern,
|
||
- Zeitüberschreitungen,
|
||
- HTTP 408,
|
||
- HTTP 429,
|
||
- HTTP 5xx,
|
||
- ungültigem äußerem Ollama-Response-JSON.
|
||
|
||
```env
|
||
OLLAMA_FAILOVER_ENABLED=true
|
||
OLLAMA_FAILOVER_ATTEMPTS=0
|
||
```
|
||
|
||
Nicht retryfähige 4xx-Fehler werden nicht auf andere Nodes gespiegelt. Die Modellaufrufe sind rein lesende Inferenzaufrufe; GLPI-Schreibaktionen erfolgen erst später durch die deterministische Go-Policy.
|
||
|
||
## Analyse-Diagnose
|
||
|
||
Jeder `AnalysisRun` speichert unter `provider`:
|
||
|
||
```json
|
||
{
|
||
"provider": "ollama-pool",
|
||
"routing_mode": "least_inflight",
|
||
"selected_node": "lenovo-02",
|
||
"selected_url": "http://10.20.30.22:11434",
|
||
"failover_used": true,
|
||
"attempt_count": 2,
|
||
"attempts": [
|
||
{
|
||
"attempt": 1,
|
||
"stage": "priority",
|
||
"node_name": "lenovo-01",
|
||
"outcome": "error",
|
||
"retryable": true
|
||
},
|
||
{
|
||
"attempt": 2,
|
||
"stage": "priority",
|
||
"node_name": "lenovo-02",
|
||
"outcome": "success"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
Zusätzlich werden – sofern Ollama sie liefert – Ladezeit, Prompt-Tokens, Generierungstokens und zugehörige Laufzeiten gespeichert.
|
||
|
||
## Dashboard und Prometheus
|
||
|
||
`/api/status` enthält unter anderem:
|
||
|
||
```text
|
||
ollama_nodes
|
||
ollama_node_count
|
||
ollama_healthy_nodes
|
||
ollama_available_nodes
|
||
ollama_routing_mode
|
||
```
|
||
|
||
Prometheus exportiert pro Node:
|
||
|
||
```text
|
||
glpi_agent_ollama_node_healthy
|
||
glpi_agent_ollama_node_available
|
||
glpi_agent_ollama_node_inflight
|
||
glpi_agent_ollama_node_requests_total
|
||
glpi_agent_ollama_node_failures_total
|
||
glpi_agent_ollama_node_average_duration_ms
|
||
```
|
||
|
||
## Netzwerksicherheit
|
||
|
||
Ollama besitzt an seiner lokalen API üblicherweise keine eigene Mandantenauthentifizierung. Die Nodes sollten daher:
|
||
|
||
- in einem eigenen Server-/KI-Netz liegen,
|
||
- Port 11434 nur vom GLPI-AI-Agent-Host akzeptieren,
|
||
- nicht aus Benutzer-VLANs erreichbar sein,
|
||
- niemals direkt aus dem Internet erreichbar sein,
|
||
- bei standortübergreifender Nutzung über VPN oder einen TLS-Reverse-Proxy mit Netzwerk-/IP-Allowlist angebunden werden.
|
||
|
||
Beispiel auf jedem Node:
|
||
|
||
```env
|
||
OLLAMA_HOST=0.0.0.0:11434
|
||
```
|
||
|
||
Diese Freigabe allein ist nicht ausreichend; eine Host- oder Netzfirewall muss den Zugriff auf die Agent-IP begrenzen.
|
||
|
||
## Rollout-Empfehlung
|
||
|
||
1. Auf allen Nodes identische Ollama- und Modellstände installieren.
|
||
2. Chat- und Embedding-Modell einmal lokal laden.
|
||
3. Jeden Node einzeln mit `/api/tags` prüfen.
|
||
4. Pool zunächst mit `OLLAMA_REQUIRE_SAME_MODEL_DIGEST=true` starten.
|
||
5. Im Dashboard kontrollieren, dass alle Nodes `healthy=true` und `compatible=true` melden.
|
||
6. `OLLAMA_NODE_MAX_INFLIGHT=1` beibehalten und mehrere Testtickets parallel analysieren.
|
||
7. Erst nach Messung von RAM, Temperatur und Laufzeiten höhere Parallelität testen.
|
||
|
||
## Modellupdates bei strikter Digest-Prüfung
|
||
|
||
Bei `OLLAMA_REQUIRE_SAME_MODEL_DIGEST=true` ist ein gemischter Modellstand absichtlich nicht verfügbar. Ein Pull oder Austausch nur auf einem einzelnen aktiven Node kann den Pool deshalb beim nächsten Healthcheck fail-closed setzen. Sichere Varianten sind:
|
||
|
||
1. Agent in ein Wartungsfenster nehmen und das Modell auf allen Nodes aktualisieren.
|
||
2. Einen neuen, eindeutig versionierten Modelltag zunächst auf allen Nodes bereitstellen und erst danach `OLLAMA_MODEL` zentral umstellen.
|
||
3. Für Hardwarewartung einen Node aus `OLLAMA_URLS` entfernen, Agent neu starten und ihn erst mit passendem Digest wieder aufnehmen.
|
||
|
||
`OLLAMA_REQUIRE_SAME_MODEL_DIGEST=false` sollte nicht als normale Rolling-Update-Strategie verwendet werden, weil dann identische Tickets während der Übergangszeit von unterschiedlichen Modellständen bewertet werden können.
|
||
|
||
## Rückfall auf einen Einzelnode
|
||
|
||
Die bisherige Konfiguration bleibt kompatibel:
|
||
|
||
```env
|
||
OLLAMA_URL=http://localhost:11434
|
||
OLLAMA_URLS=
|
||
```
|
||
|
||
Ist `OLLAMA_URLS` leer, wird automatisch `OLLAMA_URL` als einzelner Pool-Node verwendet.
|