5.0 KiB
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, parentovs_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)
vlan20war 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 aufvlan20bliebcreated/exited. (gelöscht am 2026-07-09)- Macvlan belegt seinen Parent (
ovs_eth0) exklusiv →vlan20kann erst existieren, wenndefault_externalweg ist. Deshalb ist „alle LANs raus" technisch zwingend. default_internal(172.18er Bridge) existiert nicht mehr (404); der Range ist vongitea_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 aufvlan20(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:
- entfernt alle Container auf
default_external+ die 2 auf dem totenvlan20, - löscht
default_external, - legt
vlan20an:-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). OhneCF-Access-Client-Id/Secret-Header komme ich nur über den BF-IP-Bypass durch. - Offen (braucht Freigabe): Service-Token
claude-mcprotieren → neues Secret → ablegen inmcp/cloudflare/.env(chmod 600) alsCF_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.