docs 📄 NAS→VLAN20 Docker-Migration: Runbook + Root-Cause

Diagnose der Docker-Container-Fehler nach NAS-NIC-Wechsel auf VLAN20:
- Config-Only-Macvlan-Falle (Portainer 2-Schritt, Driver=null → kein Attach)
- Macvlan-Parent ovs_eth0 exklusiv → default_external muss weg für vlan20
- default_internal (172.18) existiert nicht mehr (Range von gitea_default belegt)

RUNBOOK: IP-Mapping .41.X→.20.X (Oktett behalten), Phase-A-Teardown,
Post-Steps (ap1-DNS, CF-Ingress-Repoint, omada-Inform .20.175), CF-Access
claude-mcp-Profil-Status.

Stack-Compose-Files + Inspect-JSONs (docs/nas-vlan20/stacks/, inspect_*.json)
via .gitignore ausgeschlossen — enthalten Live-Secrets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-09 21:14:49 +02:00
parent 711b46f92c
commit 06e491f609
2 changed files with 81 additions and 0 deletions

4
.gitignore vendored
View File

@@ -1,2 +1,6 @@
.DS_Store
.secrets
# NAS-VLAN20-Migration: Compose-Files/Inspects enthalten Live-Secrets → nicht committen
docs/nas-vlan20/stacks/
docs/nas-vlan20/inspect_*.json

View File

@@ -0,0 +1,77 @@
# NAS (DS923) → VLAN20 Docker-Migration — Runbook
> Stand: 2026-07-09. Ziel: NAS-NIC ist auf VLAN20; **ein** Docker-Macvlan `vlan20`
> (10.40.20.0/24, gw .1, parent `ovs_eth0`), **alle** Container darauf, `default_external`
> (10.40.41.0/24) weg. Mapping-Regel: **letztes Oktett behalten**, Subnetz .41→.20.
## Root-Cause der Dauer-Fehler (diagnostiziert)
1. `vlan20` war ein **Config-Only-Macvlan** (`ConfigOnly:true`, `Driver:null`) — Portainers
zweistufiger Assistent, nur Schritt 1. An Config-Only-Netze kann Docker **keine** Container
hängen → jeder Container auf `vlan20` blieb `created`/`exited`. **(gelöscht am 2026-07-09)**
2. Macvlan belegt seinen Parent (`ovs_eth0`) **exklusiv** → `vlan20` kann erst existieren,
wenn `default_external` weg ist. Deshalb ist „alle LANs raus" technisch zwingend.
3. `default_internal` (172.18er Bridge) **existiert nicht mehr** (404); der Range ist von
`gitea_default` (172.18.0.0/16) belegt. Darum sind Multi-Netz-Stacks doppelt kaputt.
→ Lösung „nur vlan20": interne Bridge wird nicht neu gebaut, jeder Service nur auf `vlan20`
(Container am selben Macvlan erreichen sich gegenseitig; DB-/Sidecar-Refs waren fast alle
auskommentiert).
## IP-Mapping (default_external .41.X → vlan20 .20.X)
| Container | Stack (ep14) | alt | neu |
|---|---|---|---|
| omada-controller | omada (198) | 10.40.41.175 | 10.40.20.175 |
| prometheus | oc-stack (112) | .41.151 | .20.151 |
| grafana | oc-stack (112) | .41.109 | .20.109 |
| alertmanager | oc-stack (112) | .41.153 | .20.153 |
| node-exporter | oc-stack (112) | .41.154 | .20.154 |
| iob_slave | iob_slave (64) | .41.33 | .20.33 |
| vaultwarden | vaultwarden (73) | .41.127 | .20.127 |
| oc-tzcharlie-gateway | oc-charlie (158) | .41.162 | .20.162 |
| oc-johnny2-gateway | oc-johnny2 (150) | .41.161 | .20.161 |
| oc-qdrant-central | oc-central (160) | .41.159 | .20.159 |
| dashboard-web | nginx (101) | .41.148 | .20.148 |
| hms-mqtt | default (72) | .41.124 | .20.124 |
| gitea | gitea (111) | .41.157 | .20.157 |
| drawio-server | **standalone** | .41/.20.139 | .20.139 |
| pve-mcp | **standalone** | .41.173 | .20.173 |
Unangetastet: `cloudflared` (host-Netz), `portainer_agent` (bridge).
Transformierte Compose-Files: `docs/nas-vlan20/stacks/*.yml` (Netz-Block auf `vlan20`,
IPs .41→.20, `dns:`→10.40.20.1). Standalone-Specs: `docs/nas-vlan20/inspect_*.json`.
## Phase A — Netz-Cutover (irreversibel für laufende Container-States; Volumes bleiben)
Container-Prozesse werden neu gestartet, **Volumes werden NICHT angefasst** (`v=false`).
Skript: `docs/nas-vlan20/phaseA-...py` (bzw. /tmp/nasmig/phaseA.py). Es:
1. entfernt alle Container auf `default_external` + die 2 auf dem toten `vlan20`,
2. löscht `default_external`,
3. legt `vlan20` an: `-d macvlan --subnet 10.40.20.0/24 --gateway 10.40.20.1 -o parent=ovs_eth0`.
Manuell (falls per Hand): in Portainer → NAS(ep14) → Networks → `default_external`:
alle Container disconnecten/stoppen → Netz löschen → „Add network": macvlan, parent `ovs_eth0`,
Subnet 10.40.20.0/24, GW 10.40.20.1, Name `vlan20`.
## Phase B — Redeploy (macht Christoph selbst in Portainer)
Je Stack den Inhalt aus `docs/nas-vlan20/stacks/<id>-*.yml` einspielen → Update/Redeploy.
Standalone neu bauen (drawio-server, pve-mcp) siehe inspect_*.json (image/env/binds), Netz `vlan20`.
## Post-Migration (erst NACH Redeploy, wenn Container real auf .20.x)
- **ap1 DNS (bf.lan A-Records):** alle gewanderten Hosts .41.X→.20.X nachziehen (MikroMCP `manage_dns_entry`).
- **CF-Tunnel-Ingress** (`70d6a4cf…`, remote-managed): NAS-Origins .41→.20 repointen —
hass(→schon .20.105?), gitea .41.157→.20.157, grafana .109, pve-mcp .173, vw .127, ds-bf, portainer …
SSoT: `docs/cf-tunnel-mapping.md`. Vorher pve→VLAN20-Reachability smoke-testen.
- **omada-mcp** (`~/.claude.json` + Stack ep2/ep14): `OMADA_URL` .41.175→.20.175.
- ⚠️ **omada-controller Inform-Adresse:** adoptierter Omada-Switch sw2 kennt Controller unter
.41.175 → nach Umzug im Controller die Controller/Inform-Adresse auf .20.175 setzen, sonst sw2
„Disconnected". UI wandert auf https://10.40.20.175:8043.
- **CLAUDE.md / memory:** .41→.20 Referenzen nachziehen.
## CF-Access-Profil für Claude (Thread „ich komm schwer durch den Tunnel")
- `*.emsenhuber.net` = CF-Access-App. Policies: `christoph` (E-Mail-Login), `bf4`
(non_identity, **IP-Bypass** = BF-Egress → darum on-LAN offen), **`claude-mcp`
(allow, service_token)** = mein Profil, **policy-seitig bereits vorhanden**.
- Fehlt nur das **Secret** des `claude-mcp`-Tokens (liegt nirgends lokal). Ohne
`CF-Access-Client-Id/Secret`-Header komme ich nur über den BF-IP-Bypass durch.
- **Offen (braucht Freigabe):** Service-Token `claude-mcp` rotieren → neues Secret →
ablegen in `mcp/cloudflare/.env` (chmod 600) als `CF_ACCESS_CLIENT_ID` / `CF_ACCESS_CLIENT_SECRET`.
Rotieren invalidiert das alte Secret (das aber ohnehin verloren ist).
- `hass, vw`-App = `bypass everyone` → hass/vw sind **öffentlich** (nur HA-Login davor). Separat bewerten.