Files
expertfab-infra/diagrams/storage_flow.dot
Sascha Dodenhöft 2f8fb1349c Add storage flow & HA analysis diagram
Graphviz DOT-based diagram showing the complete storage path:
App pods → PVCs (StorageClass/Retain-Policy) → Longhorn replicas →
K3s worker nodes → Proxmox hosts.

HA analysis annotated with color coding:
- Red: SPOF (control-plane on n01, share-manager on n01)
- Orange: Degraded on failure (CSI controllers on n02, RWX volumes)
- Green: HA covered (2 Longhorn replicas on different Proxmox hosts)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 14:00:45 +02:00

227 lines
13 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
digraph storage_flow {
rankdir=LR;
graph [
fontname="Helvetica Neue",
fontsize=16,
pad=1.2,
nodesep=0.45,
ranksep=1.6,
bgcolor="#f0f4f8",
label="ExpertFab Speicherfluss & HA-Analyse",
labelloc=t,
labeljust=c,
splines=polyline,
compound=true,
newrank=true,
];
node [fontname="Helvetica Neue", fontsize=11, shape=box, style="filled,rounded", margin="0.15,0.10"];
edge [fontname="Helvetica Neue", fontsize=10, arrowsize=0.8];
/* ─────────────────────────────────────────────
SCHICHT 1 APPLIKATIONEN
───────────────────────────────────────────── */
subgraph cluster_apps {
label=<<b>① APPLIKATIONEN</b><br/>(K3s Pods)>;
style=filled; bgcolor="#e3eef7"; color="#2980b9"; penwidth=2; fontcolor="#1a5276";
subgraph cluster_erpnext {
label="ns: erpnext"; style=filled; bgcolor="#d6eaf8"; color="#5dade2";
gui [label=<<b>Gunicorn</b><br/>+ Workers>, shape=hexagon, fillcolor="#aed6f1", color="#2980b9"];
nginx [label=<<b>Nginx</b><br/>(Frontend)>, shape=hexagon, fillcolor="#aed6f1", color="#2980b9"];
mdb [label=<<b>MariaDB 10.6</b><br/>StatefulSet>, shape=cylinder, fillcolor="#fdebd0", color="#d35400"];
}
subgraph cluster_paperless {
label="ns: paperless"; style=filled; bgcolor="#d6eaf8"; color="#5dade2";
ppl [label=<<b>Paperless-NGX</b>>, shape=hexagon, fillcolor="#aed6f1", color="#2980b9"];
pgp [label=<<b>PostgreSQL</b>>, shape=cylinder, fillcolor="#fdebd0", color="#d35400"];
}
subgraph cluster_zitadel {
label="ns: zitadel"; style=filled; bgcolor="#d6eaf8"; color="#5dade2";
pgz [label=<<b>PostgreSQL</b><br/>(Zitadel)>, shape=cylinder, fillcolor="#fdebd0", color="#d35400"];
}
subgraph cluster_rmq {
label="ns: rabbitmq"; style=filled; bgcolor="#d6eaf8"; color="#5dade2";
rmq [label=<<b>RabbitMQ</b>>, shape=cylinder, fillcolor="#fdebd0", color="#d35400"];
}
}
/* ─────────────────────────────────────────────
SCHICHT 2 PVCs
───────────────────────────────────────────── */
subgraph cluster_pvcs {
label=<<b>② KUBERNETES PVCs</b>>;
style=filled; bgcolor="#eaf5ea"; color="#27ae60"; penwidth=2; fontcolor="#1e8449";
subgraph cluster_pvc_erpnext {
label=<<font point-size="10"><b>longhorn-erpnext</b> [Retain ✅]</font>>;
style=filled; bgcolor="#d5f5e3"; color="#52be80";
pvc_mdb [label=<<b>mariadb-sts-0</b><br/>3 Gi RWO>, fillcolor="#d5f5e3", color="#1e8449"];
pvc_sites [label=<<b>erpnext-sites</b><br/>3 Gi <font color="#e67e22"><b>RWX ⚠</b></font>>, fillcolor="#fef9e7", color="#e67e22"];
pvc_logs [label=<<b>erpnext-logs</b><br/>1 Gi <font color="#e67e22"><b>RWX ⚠</b></font>>, fillcolor="#fef9e7", color="#e67e22"];
}
subgraph cluster_pvc_paperless {
label=<<font point-size="10"><b>longhorn-paperless</b> [Retain ✅]</font>>;
style=filled; bgcolor="#d5f5e3"; color="#52be80";
pvc_ppl [label=<<b>media</b><br/>10 Gi RWO>, fillcolor="#d5f5e3", color="#1e8449"];
pvc_pgp [label=<<b>postgres</b><br/>5 Gi RWO>, fillcolor="#d5f5e3", color="#1e8449"];
}
subgraph cluster_pvc_del {
label=<<font point-size="10"><b>longhorn</b> [Delete <font color="#e67e22">⚠</font>]</font>>;
style=filled; bgcolor="#fef9e7"; color="#e67e22";
pvc_pgz [label=<<b>zitadel-pg</b><br/>10 Gi RWO>, fillcolor="#fef9e7", color="#d35400"];
pvc_rmq [label=<<b>rabbitmq</b><br/>5 Gi RWO>, fillcolor="#fef9e7", color="#d35400"];
}
}
/* ─────────────────────────────────────────────
SCHICHT 3 LONGHORN ENGINE
───────────────────────────────────────────── */
subgraph cluster_longhorn {
label=<<b>③ LONGHORN STORAGE ENGINE</b><br/><font point-size="10">(driver.longhorn.io)</font>>;
style=filled; bgcolor="#f4ecf7"; color="#8e44ad"; penwidth=2; fontcolor="#6c3483";
subgraph cluster_sm {
label=<<font point-size="10" color="#e67e22"><b>⚠ RWX Share-Manager</b><br/>läuft auf: efsckubnode02 (Proxmox n01)<br/>→ SPOF: fällt n01, fallen RWX-Volumes (ERPNext)</font>>;
style=filled; bgcolor="#fef9e7"; color="#e67e22"; penwidth=2;
sm_sites [label=<<b>share-manager</b><br/>erpnext-sites>, shape=ellipse, fillcolor="#fdebd0", color="#e67e22"];
sm_logs [label=<<b>share-manager</b><br/>erpnext-logs>, shape=ellipse, fillcolor="#fdebd0", color="#e67e22"];
}
subgraph cluster_replicas {
label=<<font point-size="10" color="#27ae60"><b>✅ Block-Replikation: 2 Replicas je Volume</b><br/>Daten auf verschiedenen K3s-Worker-Nodes</font>>;
style=filled; bgcolor="#eafaf1"; color="#27ae60"; penwidth=2;
rep_a [label=<<b>Replica A</b><br/>auf: efsckubnode1<br/>(Proxmox n02)>, shape=cylinder, fillcolor="#d5f5e3", color="#1e8449"];
rep_b [label=<<b>Replica B</b><br/>auf: efsckubnode02<br/>(Proxmox n01)>, shape=cylinder, fillcolor="#d5f5e3", color="#1e8449"];
}
subgraph cluster_csi {
label=<<font point-size="10" color="#e67e22"><b>⚠ CSI Controller-Pods</b><br/>Nur auf efsckubnode1 (n02)<br/>→ fällt n02: keine neuen Volumes</font>>;
style=filled; bgcolor="#fef9e7"; color="#e67e22";
csi [label=<<b>csi-attacher (3×)</b><br/>csi-provisioner (3×)<br/>csi-resizer (3×)>, fillcolor="#fdebd0", color="#e67e22"];
}
}
/* ─────────────────────────────────────────────
SCHICHT 4 K3s NODES (VMs)
───────────────────────────────────────────── */
subgraph cluster_k3s {
label=<<b>④ K3s CLUSTER</b>>;
style=filled; bgcolor="#fdf2f8"; color="#c0392b"; penwidth=2; fontcolor="#922b21";
subgraph cluster_ctrl {
label=<<font color="#c0392b" point-size="10"><b>🔴 Control Plane SINGLE POINT OF FAILURE</b><br/>Nur 1 Node, VM auf Proxmox n01<br/>→ n01 fällt = kein Cluster-Management, kein Pod-Rescheduling</font>>;
style=filled; bgcolor="#fadbd8"; color="#e74c3c"; penwidth=3;
kubadm [label=<<b>efsckubadm</b><br/>10.42.71.50<br/><font point-size="10">VM: efsckubctl (n01)</font>>, shape=diamond, fillcolor="#f1948a", color="#c0392b"];
}
subgraph cluster_workers {
label=<<font point-size="10" color="#27ae60"><b>✅ Worker Nodes auf verschiedenen Proxmox Hosts</b></font>>;
style=filled; bgcolor="#eafaf1"; color="#27ae60";
node1 [label=<<b>efsckubnode1</b><br/>10.42.71.51<br/><font point-size="10">VM auf: Proxmox n02</font><br/><font point-size="9">→ Replica A + CSI Controllers</font>>, fillcolor="#d5f5e3", color="#1e8449"];
node2 [label=<<b>efsckubnode02</b><br/>10.42.71.52<br/><font point-size="10">VM auf: Proxmox n01</font><br/><font point-size="9">→ Replica B + share-manager</font>>, fillcolor="#fdebd0", color="#e67e22"];
}
}
/* ─────────────────────────────────────────────
SCHICHT 5 PROXMOX HOSTS
───────────────────────────────────────────── */
subgraph cluster_proxmox {
label=<<b>⑤ PROXMOX CLUSTER efproxcl02</b><br/><font point-size="10">⚠ Nur 2 Nodes kein 3-Node-Quorum / kein automatisches Fencing</font>>;
style=filled; bgcolor="#fdfefe"; color="#7f8c8d"; penwidth=2;
subgraph cluster_pn01 {
label=<<b>efproxcl02n01</b> 10.42.70.1<br/><font point-size="10" color="#c0392b"><b>🔴 KRITISCH: Control-Plane + efsckubnode02 + share-manager</b><br/>Ausfall → ERPNext vollständig down (kein Rescheduling möglich)</font>>;
style=filled; bgcolor="#fadbd8"; color="#e74c3c"; penwidth=2;
pn01 [label=<<b>Proxmox Host 1</b><br/>64 vCPU / 128 GB RAM>, shape=box, fillcolor="#f1948a", color="#c0392b"];
disk_n01 [label=<<b>Lokaler Speicher</b><br/>Replica B<br/><font point-size="9">erpnext / paperless<br/>zitadel / rabbitmq</font>>, shape=cylinder, fillcolor="#fadbd8", color="#e74c3c"];
}
subgraph cluster_pn02 {
label=<<b>efproxcl02n02</b> 10.42.70.2<br/><font point-size="10" color="#e67e22"><b>🟡 Ausfall → degradiert:</b> Daten intakt (Replica B ok)<br/>Keine neuen Volumes (CSI weg), ERPNext-Pods reschedule auf node02</font>>;
style=filled; bgcolor="#fef9e7"; color="#e67e22"; penwidth=2;
pn02 [label=<<b>Proxmox Host 2</b><br/>64 vCPU / 128 GB RAM>, shape=box, fillcolor="#fdebd0", color="#d35400"];
disk_n02 [label=<<b>Lokaler Speicher</b><br/>Replica A<br/><font point-size="9">erpnext / paperless<br/>zitadel / rabbitmq</font>>, shape=cylinder, fillcolor="#fef9e7", color="#e67e22"];
}
}
/* ─────────────────────────────────────────────
EDGES
───────────────────────────────────────────── */
/* Apps → PVCs */
mdb -> pvc_mdb [color="#2980b9", penwidth=2];
gui -> pvc_sites [color="#2980b9", penwidth=2];
nginx -> pvc_logs [color="#2980b9", penwidth=2];
ppl -> pvc_ppl [color="#2980b9", penwidth=2];
pgp -> pvc_pgp [color="#2980b9", penwidth=2];
pgz -> pvc_pgz [color="#2980b9", penwidth=2];
rmq -> pvc_rmq [color="#2980b9", penwidth=2];
/* RWX PVCs → share-manager */
pvc_sites -> sm_sites [color="#e67e22", penwidth=2, style=dashed, label="NFS"];
pvc_logs -> sm_logs [color="#e67e22", penwidth=2, style=dashed, label="NFS"];
/* share-manager → Replica B */
sm_sites -> rep_b [color="#e67e22", penwidth=2];
sm_logs -> rep_b [color="#e67e22", penwidth=2];
/* RWO PVCs → beide Replicas (Longhorn CSI) */
pvc_mdb -> rep_a [color="#27ae60", penwidth=2, label="Longhorn CSI"];
pvc_mdb -> rep_b [color="#27ae60", penwidth=2];
pvc_ppl -> rep_a [color="#27ae60", penwidth=2];
pvc_ppl -> rep_b [color="#27ae60", penwidth=2];
pvc_pgp -> rep_a [color="#27ae60", penwidth=2];
pvc_pgp -> rep_b [color="#27ae60", penwidth=2];
pvc_pgz -> rep_a [color="#27ae60", penwidth=2];
pvc_pgz -> rep_b [color="#27ae60", penwidth=2];
pvc_rmq -> rep_a [color="#27ae60", penwidth=2];
pvc_rmq -> rep_b [color="#27ae60", penwidth=2];
/* Sync zwischen Replicas */
rep_a -> rep_b [color="#27ae60", penwidth=2.5, style=bold, label="sync\n(synchron)"];
/* CSI → Volumes */
csi -> rep_a [color="#95a5a6", style=dotted, label="provision"];
/* Replicas → K3s Worker Nodes */
rep_a -> node1 [color="#27ae60", penwidth=2, label="physisch auf"];
rep_b -> node2 [color="#27ae60", penwidth=2, label="physisch auf"];
/* Control-Plane → Workers */
kubadm -> node1 [color="#7f8c8d", style=dashed, label="API"];
kubadm -> node2 [color="#7f8c8d", style=dashed];
/* K3s Nodes → Proxmox Hosts */
kubadm -> pn01 [color="#e74c3c", penwidth=3, label="VM auf n01"];
node2 -> pn01 [color="#e67e22", penwidth=2.5, label="VM auf n01"];
node1 -> pn02 [color="#27ae60", penwidth=2, label="VM auf n02"];
/* Proxmox → Disk */
pn01 -> disk_n01 [color="#95a5a6"];
pn02 -> disk_n02 [color="#95a5a6"];
/* Backup */
disk_n01 -> disk_n02 [color="#95a5a6", style=dashed,
label="daily-backup CronJob\n(Longhorn, auf efsckubnode02)"];
/* ─────────────────────────────────────────────
RANK CONSTRAINTS Spalten erzwingen (newrank=true erlaubt cross-cluster)
───────────────────────────────────────────── */
subgraph rank_apps { rank=same; gui; nginx; mdb; ppl; pgp; pgz; rmq; }
subgraph rank_pvcs { rank=same; pvc_mdb; pvc_sites; pvc_logs; pvc_ppl; pvc_pgp; pvc_pgz; pvc_rmq; }
subgraph rank_lh { rank=same; sm_sites; sm_logs; rep_a; rep_b; csi; }
subgraph rank_nodes { rank=same; kubadm; node1; node2; }
subgraph rank_prox { rank=same; pn01; disk_n01; pn02; disk_n02; }
}