Added section 8: existing UI/admin infrastructure from V1: - iSCSI admin HTTP server (admin.go: /status, /assign, /rebuild, /snapshot) - Grafana dashboard JSON (block-overview.json, already built) - Master UI HTML (master.html, add Block Volumes tab) - Volume server UI HTML (volume.html, add Block section) - Prometheus metrics (already integrated) Added section 10: existing vs new code map showing most backend exists — work is wiring to user-facing interfaces. Updated Phase 1 to include Master UI tab (+200 lines HTML/JS). Updated Phase 5 with two options (lightweight extend vs full SPA). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
20 KiB
V3 Operations Design
Date: 2026-04-13 Status: design draft Purpose: define the user-facing operations experience for sw-block
1. Design Principles
1.1 One command to start
A developer should go from zero to a working block volume in under 10 seconds with one command. No config files, no YAML, no separate services.
1.2 Dry-run by default
Every destructive operation previews what it will do. The user must
explicitly confirm with -apply. This is already the SeaweedFS shell
convention — extend it to all block operations.
1.3 Shell is the operations interface
weed shell is the single entry point for admin operations. No separate
CLI tool. The shell already provides tab completion, command history,
distributed locking, and 90+ commands. Block operations plug into the
same framework.
1.4 Health at a glance
An admin should never need to grep logs to understand cluster state.
block.health shows everything in one screen. Green/yellow/red, not
log lines.
1.5 Developer sees only PVC
K8s developers interact with storage through PersistentVolumeClaim.
They never see iSCSI, NVMe-oF, replication, failover, or rebuild.
The CSI driver handles everything.
1.6 Three personas, three interfaces
| Persona | Interface | What they see |
|---|---|---|
| Developer | K8s PVC / REST API | "Give me 100GB storage" |
| Operator | weed shell |
Volume health, failover, rebuild, snapshots |
| Platform engineer | Config + monitoring | Cluster sizing, Prometheus, alerting |
2. Setup Experience
2.1 Single-node development (10 seconds)
# Download
curl -O https://github.com/seaweedfs/seaweedfs/releases/latest/weed
# Start everything in one process
./weed server -block -block.size=100G -dir=/data
# Output:
# Master started on :9333
# Volume server started on :8480
# Block service started (iSCSI :3260)
# Created default volume "vol-1" (100GB, RF=1)
#
# Connect from this machine:
# iscsiadm -m discovery -t st -p localhost:3260
# iscsiadm -m node -T iqn.2024-01.com.seaweedfs:vol.vol-1 -l
#
# Or use NVMe-oF:
# nvme discover -t tcp -a 127.0.0.1 -s 4420
The output tells the user exactly what to do next. No documentation needed for the first connection.
2.2 Two-node replicated (30 seconds)
# Node 1 (master + volume server):
./weed server -block -block.size=100G -ip=10.0.0.1
# Node 2 (volume server only):
./weed volume -block -block.size=100G -ip=10.0.0.3 \
-mserver=10.0.0.1:9333
# Create replicated volume:
./weed shell -master=10.0.0.1:9333
> block.create -name=mydb -size=100G -rf=2 -apply
Created "mydb" (100GB, RF=2, sync_all)
Primary: 10.0.0.1:8480
Replica: 10.0.0.3:8480
iSCSI: iqn.2024-01.com.seaweedfs:vol.mydb at 10.0.0.1:3260
2.3 Kubernetes (2 minutes)
# Install CSI driver
helm repo add seaweedfs https://seaweedfs.github.io/charts
helm install sw-block seaweedfs/sw-block-csi \
--set master.address=10.0.0.1:9333
# Create StorageClass
kubectl apply -f - <<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: sw-block
provisioner: seaweedfs.com/block
parameters:
replicaFactor: "2"
durabilityMode: sync_all
allowVolumeExpansion: true
EOF
# Developer just uses PVC:
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-database
spec:
storageClassName: sw-block
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 100Gi
EOF
2.4 Setup comparison
| System | Commands to first working volume | Time |
|---|---|---|
| sw-block | 1 (weed server -block) |
10s |
| MinIO | 1 (minio server) |
5s (object only, no block) |
| SeaweedFS (object) | 1 (weed server) |
5s |
| Longhorn | 3 (helm install + PVC) |
5min |
| Ceph | 5-8 (cephadm bootstrap + OSD + RBD) |
30min |
| TrueNAS | Web installer + wizard | 15min |
3. Shell Commands
3.1 Command naming convention
All block commands start with block.. Subcommands use dots.
This matches the existing shell convention (volume.list, fs.ls,
ec.encode).
3.2 Volume lifecycle
block.create Create a new block volume
block.delete Delete a block volume (dry-run by default)
block.list List all block volumes with health status
block.status Show detailed status of one volume
block.expand Expand volume size (online, no downtime)
3.3 Data protection
block.snapshot Create a CoW snapshot
block.snapshot.list List snapshots for a volume
block.snapshot.delete Delete a snapshot
block.snapshot.export Export snapshot to S3 (SeaweedFS object store)
block.snapshot.restore Restore volume from snapshot
3.4 HA operations
block.promote Manual failover: promote replica to primary
block.rebuild Trigger manual rebuild for a replica
block.assign Change replica assignment (add/remove replica)
3.5 Cluster health
block.health Cluster-wide health summary
block.servers List block-capable volume servers
block.events Recent events (failover, rebuild, degraded)
3.6 Benchmarking
block.bench Run fio benchmark on a volume (creates temp volume if needed)
block.bench.compare Compare performance of two runs
3.7 Detailed command specs
block.list
> block.list
NAME SIZE RF MODE PRIMARY REPLICA HEALTH IOPS
pvc-data-01 100G 2 publish_healthy 10.0.0.1:8480 10.0.0.3:8480 1.0 45.2K
pvc-db-01 50G 2 degraded 10.0.0.3:8480 (rebuilding 78%) 0.8 38.1K
pvc-temp 200G 1 healthy 10.0.0.1:8480 — 1.0 52.0K
> block.list -json
[{"name":"pvc-data-01","size":107374182400,"rf":2,"mode":"publish_healthy",...}]
Columns:
- NAME: volume name
- SIZE: allocated size
- RF: replica factor
- MODE: engine projection mode
- PRIMARY: current primary server
- REPLICA: replica server(s) with status
- HEALTH: 0.0-1.0 score
- IOPS: current write IOPS (from last heartbeat)
block.status
> block.status mydb
Volume: mydb
Size: 100G (used: 34G, 34%)
RF: 2 (sync_all)
Mode: publish_healthy
Epoch: 3
Primary: 10.0.0.1:8480
WAL head: 284756
Checkpoint: 284700
Uptime: 3d 14h
Replica: 10.0.0.3:8480
State: in_sync
Flushed: 284756
Lag: 0 entries
iSCSI: iqn.2024-01.com.seaweedfs:vol.mydb at 10.0.0.1:3260
NVMe-oF: nqn.2024-01.com.seaweedfs:vol.mydb at 10.0.0.1:4420
Snapshots:
mydb@2026-04-13T10:30 34G (3 hours ago)
mydb@2026-04-12T22:00 32G (15 hours ago)
Recent events:
2026-04-11 02:12 rebuild completed (2.1s, 1GB transferred)
2026-04-09 22:54 auto-failover (10.0.0.1 → 10.0.0.3, epoch 1→2)
Health: 1.0 (last scrub: 2026-04-12, 0 errors)
block.health
> block.health
╔═══════════════════════════════════════╗
║ BLOCK CLUSTER HEALTH: OK ║
╚═══════════════════════════════════════╝
Volumes: 12 total
11 healthy
1 degraded (pvc-db-01: rebuilding)
0 offline
Servers: 3 online, 0 offline
10.0.0.1:8480 ██████████ 6 volumes, 450G used
10.0.0.3:8480 ████████ 5 volumes, 380G used
10.0.0.5:8480 ██████ 4 volumes, 280G used
Capacity: 1.1T used / 3.0T total (37%)
Last 24h:
Failovers: 0
Rebuilds: 1 (pvc-db-01, in progress, 78%)
Scrub errors: 0
Alerts: none
block.create (dry-run)
> block.create -name=analytics -size=500G -rf=2
DRY RUN — no changes made
Would create volume "analytics":
Size: 500G
RF: 2 (sync_all)
Primary: 10.0.0.5:8480 (most free space: 720G)
Replica: 10.0.0.1:8480 (550G free, different rack)
iSCSI IQN: iqn.2024-01.com.seaweedfs:vol.analytics
Run with -apply to execute.
> block.create -name=analytics -size=500G -rf=2 -apply
Created "analytics" (500GB, RF=2, sync_all)
Primary: 10.0.0.5:8480
Replica: 10.0.0.1:8480
iSCSI: iqn.2024-01.com.seaweedfs:vol.analytics at 10.0.0.5:3260
block.delete (always requires -apply -confirm)
> block.delete -name=old-vol
DRY RUN — no changes made
Would delete volume "old-vol":
Size: 50G (used: 23G)
RF: 2 (primary: 10.0.0.1, replica: 10.0.0.3)
Snapshots: 3 (would also be deleted)
Age: 45 days
WARNING: This permanently destroys all data and snapshots.
Run with -apply -confirm to execute.
> block.delete -name=old-vol -apply -confirm
Deleted "old-vol" (50GB, 3 snapshots removed)
block.promote (manual failover)
> block.promote -name=mydb -target=10.0.0.3:8480
DRY RUN — no changes made
Would promote 10.0.0.3:8480 to primary for "mydb":
Current primary: 10.0.0.1:8480 (epoch 2)
New primary: 10.0.0.3:8480 (would become epoch 3)
Replica state: in_sync (lag: 0 entries)
Impact: iSCSI connections will reconnect (~2s disruption)
Run with -apply to execute.
> block.promote -name=mydb -target=10.0.0.3:8480 -apply
Promoted 10.0.0.3:8480 to primary for "mydb" (epoch 2→3)
Old primary 10.0.0.1:8480 will rejoin as replica after next heartbeat.
4. REST API
4.1 Endpoints
All under /block/ prefix on the master server.
| Method | Path | Description |
|---|---|---|
GET |
/block/volumes |
List all block volumes |
POST |
/block/volumes |
Create block volume |
GET |
/block/volumes/{name} |
Get volume status |
DELETE |
/block/volumes/{name} |
Delete volume |
PUT |
/block/volumes/{name}/expand |
Expand volume |
POST |
/block/volumes/{name}/promote |
Manual failover |
POST |
/block/volumes/{name}/snapshots |
Create snapshot |
GET |
/block/volumes/{name}/snapshots |
List snapshots |
DELETE |
/block/volumes/{name}/snapshots/{id} |
Delete snapshot |
GET |
/block/health |
Cluster health summary |
GET |
/block/servers |
List block-capable servers |
GET |
/block/events |
Recent events |
4.2 Examples
# Create volume
curl -X POST http://master:9333/block/volumes \
-H "Content-Type: application/json" \
-d '{"name":"mydb","sizeBytes":107374182400,"replicaFactor":2}'
# Response:
{
"name": "mydb",
"sizeBytes": 107374182400,
"replicaFactor": 2,
"durabilityMode": "sync_all",
"primary": "10.0.0.1:8480",
"replica": "10.0.0.3:8480",
"iscsiIqn": "iqn.2024-01.com.seaweedfs:vol.mydb",
"iscsiPortal": "10.0.0.1:3260",
"epoch": 1,
"mode": "bootstrap_pending"
}
# Get health
curl http://master:9333/block/health
# Response:
{
"status": "ok",
"volumes": {"total": 12, "healthy": 11, "degraded": 1},
"servers": {"total": 3, "online": 3},
"capacity": {"usedBytes": 1181116006400, "totalBytes": 3221225472000}
}
5. Observability
5.1 Prometheus metrics
Already implemented. Key metrics:
# Volume level
sw_block_volume_mode{name="mydb"} 1 # 1=healthy
sw_block_volume_iops{name="mydb",op="write"} 45200
sw_block_volume_size_bytes{name="mydb"} 107374182400
sw_block_volume_used_bytes{name="mydb"} 36507222016
# Replica level
sw_block_replica_state{name="mydb",server="10.0.0.3"} 3 # 3=in_sync
sw_block_replica_lag_entries{name="mydb",server="10.0.0.3"} 0
# Cluster level
sw_block_volumes_total 12
sw_block_volumes_healthy 11
sw_block_volumes_degraded 1
sw_block_servers_online 3
# Events
sw_block_failovers_total 2
sw_block_rebuilds_total 5
sw_block_rebuild_duration_seconds{name="mydb"} 2.1
5.2 Alerting rules
Recommended Prometheus alert rules:
groups:
- name: sw-block
rules:
- alert: BlockVolumeDegraded
expr: sw_block_volume_mode != 1
for: 5m
labels:
severity: warning
annotations:
summary: "Block volume {{ $labels.name }} is degraded"
- alert: BlockVolumeOffline
expr: sw_block_volume_mode == 0
for: 1m
labels:
severity: critical
- alert: BlockRebuildStalled
expr: sw_block_rebuild_duration_seconds > 300
labels:
severity: warning
summary: "Rebuild for {{ $labels.name }} taking > 5 minutes"
- alert: BlockCapacityHigh
expr: sw_block_cluster_used_bytes / sw_block_cluster_total_bytes > 0.85
labels:
severity: warning
summary: "Block cluster capacity > 85%"
5.3 Grafana dashboard
Provide a pre-built Grafana dashboard JSON that shows:
- Cluster health panel (big green/yellow/red indicator)
- Volume list with mode, health, IOPS
- IOPS over time (per-volume and aggregate)
- Latency percentiles (p50, p99, p999)
- Rebuild/failover event markers
- Capacity trend
- Per-server utilization
6. Error Messages
Every error should tell the user what happened, why, and what to do:
# Bad:
Error: volume not found
# Good:
Error: block volume "mydb" not found.
Available volumes: pvc-data-01, pvc-db-01, pvc-temp
Run 'block.list' to see all volumes.
# Bad:
Error: cannot create volume
# Good:
Error: cannot create volume "analytics" (500GB, RF=2).
Reason: not enough servers. Need 2 servers for RF=2, but only 1 is online.
Run 'block.servers' to check server status.
# Bad:
Error: promote failed
# Good:
Error: cannot promote 10.0.0.3:8480 for "mydb".
Reason: replica is not in_sync (current state: rebuilding, 78% complete).
Wait for rebuild to complete, then retry.
Run 'block.status mydb' for details.
7. Comparison with Competitors
7.1 What we steal
| From | What | How |
|---|---|---|
| MinIO | One-command startup | weed server -block |
| SeaweedFS | Shell as operations interface | block.* commands in weed shell |
| Ceph | Dry-run on all destructive ops | -apply flag required |
| Ceph | Declarative cluster spec | Future: block.apply -f cluster.yaml |
| Longhorn | Auto-rebuild on config change | Change RF in shell, system handles rebuild |
| Longhorn | One-click snapshot | block.snapshot mydb (one command, no params) |
| TrueNAS | iSCSI setup wizard | block.setup interactive guide |
| TrueNAS | Actionable error messages | Every error says what to do next |
7.2 What we do better
| Area | Competitors | sw-block |
|---|---|---|
| Setup time | Ceph: 30min, Longhorn: 5min | 10 seconds |
| Write IOPS (RF=2) | Ceph: 4.5K | 61K (13.5x) |
| Rebuild time (1GB) | Ceph: minutes | 2 seconds |
| CLI | Ceph: many tools, Longhorn: kubectl | Single weed shell |
| Object + Block | Separate systems | Same binary, same cluster |
7.3 What we do not provide (yet)
| Feature | Who has it | Priority |
|---|---|---|
| Web dashboard | Ceph, Longhorn, TrueNAS | P1 (existing foundation) |
| Declarative cluster spec | Ceph ceph orch |
P3 |
| Multi-cluster management | None (all single-cluster) | Not planned |
8. Existing UI / Admin Foundation (V1)
V1 already built several admin interfaces that V3 should reuse:
8.1 iSCSI Admin HTTP Server
File: weed/storage/blockvol/iscsi/cmd/iscsi-target/admin.go
Provides HTTP endpoints for the standalone iscsi-target binary:
| Endpoint | Method | What it does |
|---|---|---|
/status |
GET | Volume status JSON (role, epoch, WAL head, replica state) |
/assign |
POST | Inject assignment (epoch, role, lease TTL) |
/replica |
POST | Set WAL shipping target (data/ctrl addr) |
/rebuild |
POST | Start/stop rebuild server |
/snapshot |
POST | Create/delete/restore/list snapshots |
/resize |
POST | Expand volume size |
/metrics |
GET | Prometheus metrics (with optional auth token) |
These endpoints powered the CP4b-4 HA integration tests. The admin server runs alongside the iSCSI target and controls it via HTTP.
V3 action: Move these endpoints into weed server's volume server
HTTP handler. The standalone binary keeps its admin server for testing;
the production path uses the integrated weed server endpoints.
8.2 Grafana Dashboard
File: weed/storage/blockvol/monitoring/dashboards/block-overview.json
Pre-built Grafana dashboard with panels for:
- Cluster health (stat panel)
- WAL pressure state
- Volumes by role (bar gauge)
- Prometheus metric queries
V3 action: Update metric names if changed, add V2 metrics (rebuild duration, failover count, shipper state). The dashboard is importable as-is into any Grafana instance.
8.3 Master UI HTML
File: weed/server/master_ui/master.html
The master already serves an HTML UI at http://master:9333/ showing:
- Cluster topology (data centers, racks, nodes)
- Volume count and IDs per node
- Raft cluster state
V3 action: Add a "Block Volumes" tab that calls /block/volumes
REST API and renders a table. This is the fastest path to a web UI —
~200 lines of HTML/JS added to the existing template.
8.4 Volume Server UI HTML
File: weed/server/volume_server_ui/volume.html
The volume server serves an HTML UI at http://vs:8480/ui/ showing
local volume information.
V3 action: Add block volume section showing local BlockVol status, iSCSI target info, and replica state.
8.5 Prometheus Metrics
Already integrated in weed server. Key block metrics are exported via
the standard /metrics endpoint. No separate metrics server needed.
9. Implementation Phases
Phase 1: Shell commands + Master UI tab (P0)
Shell commands in weed/shell/:
block.list— volume list with healthblock.status— detailed volume statusblock.create— create with dry-runblock.delete— delete with dry-run + confirmblock.health— cluster health summary
These call existing master gRPC APIs. No new backend code needed.
Master UI block tab in weed/server/master_ui/master.html:
- Add "Block Volumes" tab to existing UI
- Table: name, size, RF, mode, primary, replica, health
- Auto-refresh every 5 seconds
- Calls
/block/volumesREST endpoint
Estimated: ~600 lines (400 shell + 200 HTML/JS).
Phase 2: REST API + Grafana update (P1)
REST API in weed/server/master_server_handlers_block.go:
- CRUD endpoints for volumes
- Health endpoint
- Server list endpoint
These wrap existing BlockVolumeRegistry and blockAssignmentQueue
methods. JSON request/response.
Grafana dashboard update:
- Update
block-overview.jsonwith V2/V3 metrics - Add rebuild duration, failover count panels
- Add per-volume IOPS panel
Estimated: ~350 lines (300 API + 50 dashboard JSON).
Phase 3: Enhanced operations (P2)
block.snapshot/block.snapshot.listblock.expandblock.promotewith dry-runblock.events(recent failover/rebuild log)block.bench(built-in fio wrapper)- Migrate iSCSI admin endpoints to weed server
Phase 4: Developer experience (P2)
weed server -blockone-click mode- Actionable error messages for all block operations
- Connection string output on volume creation
block.setupinteractive wizard
Phase 5: Full Web UI (P3)
Two options:
Option A: Extend master UI (lightweight)
- Add volume detail page (click volume name → full status)
- Add snapshot/expand/promote buttons
- Add cluster health dashboard panel
- ~500 lines HTML/JS, no framework
Option B: Standalone dashboard (full)
- Separate SPA (React/Vue) served from master
- Real-time WebSocket updates
- Performance charts (IOPS, latency over time)
- User auth integration
- ~2000+ lines, needs framework
Recommendation: Start with Option A. Move to Option B only when customer demand justifies the investment.
10. Existing vs New Code Map
| Feature | Existing Code | New Code Needed |
|---|---|---|
| Volume CRUD | BlockVolumeRegistry, CreateBlockVolume gRPC |
Shell formatting + REST wrapper |
| Status/health | BlockVolumeEntry, heartbeat data |
Shell formatting |
| Prometheus metrics | weed/storage/blockvol/metrics.go |
Already done |
| Grafana dashboard | monitoring/dashboards/block-overview.json |
Update metrics |
| Master web UI | master_ui/master.html |
Add Block tab |
| Volume server UI | volume_server_ui/volume.html |
Add Block section |
| Admin HTTP (iSCSI) | iscsi/cmd/iscsi-target/admin.go |
Migrate to weed server |
| Snapshot API | blockvol.CreateSnapshot() etc |
Shell + REST wrapper |
| Failover API | PromoteBestReplica(), ManualPromote() |
Shell wrapper |
| Rebuild API | StartRebuildFromProbe() |
Shell wrapper |
Most of the backend exists. The work is wiring it to user-facing interfaces (shell, REST, HTML).