Initial: Add all homelab manifests

This commit is contained in:
2026-03-20 00:05:50 +00:00
commit b538e87d69
33 changed files with 3036 additions and 0 deletions

272
docs/08-omada-mcp.md Normal file
View File

@@ -0,0 +1,272 @@
# 08 · TPLink Omada MCP Server
**Erstellt:** 2026-03-18
**Aktualisiert:** 2026-03-18 — Credentials rotiert (CLIENT_ID/SECRET), Service auf NodePort 31777 umgestellt
**Namespace:** `omada-mcp`
**Image:** `jmtvms/tplink-omada-mcp:latest`
**Status:** Running · Pod `omada-mcp-dfdbfbcf8-x6t5k` · NodePort `31777` → Container `3000`
---
## Übersicht
Der [tplink-omada-mcp](https://github.com/jmtvms/tplink-omada-mcp) Server stellt eine MCP-Schnittstelle (Model Context Protocol) für den TPLink Omada Controller bereit. Claude und andere KI-Assistenten können darüber das Netzwerk verwalten (Clients, SSIDs, VLANs, Geräte usw.).
Der Server läuft im HTTP-Modus auf Port **3000** (`MCP_SERVER_USE_HTTP=true`).
---
## Dateien
```
homelab/k8s/omada-mcp/
├── kustomization.yaml # Kustomize-Einstiegspunkt
├── namespace.yaml # Namespace omada-mcp
├── secret.yaml # Credentials (Platzhalter vor Anwenden befüllen!)
├── deployment.yaml # Deployment (1 Replica)
└── service.yaml # NodePort Service Port 3000 → NodePort 31777
```
---
## Credentials (Secret)
Das Secret `omada-mcp-credentials` enthält folgende Schlüssel:
| Key | Beschreibung |
|-----|-------------|
| `OMADA_BASE_URL` | URL des Omada Controllers, z. B. `https://192.168.11.29` |
| `OMADA_CLIENT_ID` | OAuth Client-ID aus dem Omada Controller |
| `OMADA_CLIENT_SECRET` | OAuth Client-Secret |
| `OMADA_OMADAC_ID` | Omada-Controller-ID (zu finden in den Controller-Einstellungen) |
| `OMADA_STRICT_SSL` | `false` deaktiviert SSL-Verifikation (Self-signed Certs) |
| `MCP_SERVER_USE_HTTP` | `true` aktiviert HTTP-Modus |
| `MCP_HTTP_BIND_ADDR` | `0.0.0.0` lauscht auf allen Interfaces |
### Secret anlegen (empfohlen kein base64 nötig)
```bash
kubectl create secret generic omada-mcp-credentials \
--namespace omada-mcp \
--from-literal=OMADA_BASE_URL='https://192.168.11.29' \
--from-literal=OMADA_CLIENT_ID='dein-client-id' \
--from-literal=OMADA_CLIENT_SECRET='dein-client-secret' \
--from-literal=OMADA_OMADAC_ID='dein-omadac-id' \
--from-literal=OMADA_STRICT_SSL='false' \
--from-literal=MCP_SERVER_USE_HTTP='true' \
--from-literal=MCP_HTTP_BIND_ADDR='0.0.0.0'
```
Danach `secret.yaml` **nicht** anwenden (das Secret existiert bereits).
### Alternativ: secret.yaml befüllen
```bash
# Werte base64-kodieren
echo -n 'https://omada.example.com' | base64
echo -n 'dein-client-id' | base64
echo -n 'dein-client-secret' | base64
echo -n 'dein-omadac-id' | base64
```
Die Ausgaben in `secret.yaml` bei den `REPLACE_WITH_*`-Platzhaltern eintragen.
---
## Deployment
### Voraussetzungen
- Namespace existiert (wird durch `namespace.yaml` erstellt)
- Secret ist befüllt (siehe oben)
### Anwenden mit Kustomize
```bash
# Dry-run Manifeste prüfen
kubectl apply -k homelab/k8s/omada-mcp/ --dry-run=client
# Anwenden
kubectl apply -k homelab/k8s/omada-mcp/
```
### Einzelne Manifeste anwenden
```bash
kubectl apply -f homelab/k8s/omada-mcp/namespace.yaml
kubectl apply -f homelab/k8s/omada-mcp/secret.yaml # nur wenn nicht per kubectl create
kubectl apply -f homelab/k8s/omada-mcp/deployment.yaml
kubectl apply -f homelab/k8s/omada-mcp/service.yaml
```
---
## Umgebungsvariablen im Container
| Variable | Wert | Quelle |
|----------|------|--------|
| `OMADA_BASE_URL` | `https://192.168.11.29` | Secret `omada-mcp-credentials` |
| `OMADA_CLIENT_ID` | aus Secret | `omada-mcp-credentials` |
| `OMADA_CLIENT_SECRET` | aus Secret | `omada-mcp-credentials` |
| `OMADA_OMADAC_ID` | aus Secret | `omada-mcp-credentials` |
| `OMADA_STRICT_SSL` | `false` | `omada-mcp-credentials` |
| `MCP_SERVER_USE_HTTP` | `true` | `omada-mcp-credentials` |
| `MCP_HTTP_BIND_ADDR` | `0.0.0.0` | `omada-mcp-credentials` |
---
## Verifikation
```bash
# Pod-Status prüfen
kubectl get pods -n omada-mcp
# Logs anzeigen
kubectl logs -n omada-mcp deployment/omada-mcp
# Service prüfen
kubectl get svc -n omada-mcp
# Direkter Test via Port-Forward
kubectl port-forward -n omada-mcp svc/omada-mcp 3000:3000
curl http://localhost:3000/
```
### Erwartete Ausgabe (Pod läuft)
```
NAME READY STATUS RESTARTS AGE
omada-mcp-xxxxxxxxx-xxxxx 1/1 Running 0 1m
```
---
## MCP-Endpunkt in Claude Code einbinden
Sobald der Pod läuft, kann der MCP-Server per Port-Forward oder Ingress angebunden werden.
### Option A: NodePort (dauerhaft, direkt erreichbar)
Der Service ist auf NodePort **31777** auf allen Nodes erreichbar:
| Node | URL |
|------|-----|
| rnk-cp01 | `http://192.168.11.170:31777` |
| rnk-wrk01 | `http://192.168.11.171:31777` |
| rnk-wrk02 | `http://192.168.11.172:31777` |
Claude Code `.mcp.json`:
```json
{
"mcpServers": {
"omada": {
"type": "http",
"url": "http://192.168.11.171:31777/mcp"
}
}
}
```
### Option B: Port-Forward (lokal/temporär)
```bash
kubectl port-forward -n omada-mcp svc/omada-mcp 3000:3000 &
```
### Option B: Ingress (dauerhaft, mit Traefik)
Ingress-Manifest erstellen (optional):
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: omada-mcp
namespace: omada-mcp
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
cert-manager.io/cluster-issuer: letsencrypt-production
spec:
ingressClassName: traefik
tls:
- hosts:
- omada-mcp.homelab.local
secretName: omada-mcp-tls
rules:
- host: omada-mcp.homelab.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: omada-mcp
port:
number: 3000
```
---
## Ressourcen
- CPU: 50m Request / 200m Limit
- Memory: 64Mi Request / 256Mi Limit
---
## Troubleshooting
```bash
# Pod-Events anzeigen
kubectl describe pod -n omada-mcp -l app.kubernetes.io/name=omada-mcp
# Secret-Inhalte prüfen (base64-dekodiert)
kubectl get secret omada-mcp-credentials -n omada-mcp -o jsonpath='{.data.OMADA_BASE_URL}' | base64 -d
# Pod neu starten
kubectl rollout restart deployment/omada-mcp -n omada-mcp
```
### Häufige Fehler
| Fehler | Ursache | Lösung |
|--------|---------|--------|
| `ImagePullBackOff` | Image nicht erreichbar | Internet-Zugang der Nodes prüfen |
| `CrashLoopBackOff` | Falsche Credentials | Logs prüfen, Secret aktualisieren |
| `Pending` | Ressourcen fehlen | `kubectl describe pod` → Events |
| SSL-Fehler | Self-signed Cert | `OMADA_STRICT_SSL=false` bestätigen |
---
## Deployment-Geschichte
| Datum | Aktion | Ergebnis |
|-------|--------|---------|
| 2026-03-18 | Namespace, Secret, Deployment, Service erstellt | Pod in CrashLoopBackOff wegen falscher Probe-Pfade |
| 2026-03-18 | Liveness/Readiness Probe auf `/healthz` geändert | Pod `1/1 Running`, `/healthz``{"status":"ok"}` |
| 2026-03-18 | MCP Initialize-Request getestet | Server antwortet korrekt, Version 0.1.0 |
| 2026-03-18 | Service von ClusterIP auf NodePort 31777 umgestellt | Extern erreichbar auf allen Nodes |
| 2026-03-18 | Credentials rotiert (CLIENT_ID + CLIENT_SECRET) | Pod-Rollout erfolgreich, `1/1 Running` |
### Bekannte Besonderheit: Probe-Pfad
Der MCP-Server gibt auf `GET /` HTTP 404 zurück (MCP-Protokoll beantwortet nur POST mit korrekten Headers).
Der Health-Endpoint ist `/healthz``{"status":"ok"}`.
### MCP-Endpunkt aufrufen
```bash
curl -s -X POST http://192.168.11.171:31777/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
```
## Nächste Schritte
- [x] Credentials im Secret befüllen und anwenden
- [x] Pod-Status und Logs nach Deployment prüfen
- [ ] MCP-Server in Claude Code `.mcp.json` eintragen
- [ ] Optional: Ingress für dauerhaften Zugriff erstellen
- [ ] Optional: Horizontal Pod Autoscaler bei Bedarf hinzufügen