Getting started 2: explained Kubernetes installation
This is the complete walkthrough behind the short happy path. Use it when the short path does not work, when the defaults do not describe the cluster, or before treating the installation as anything beyond a disposable test.
Detailed walkthrough
-
Experimental: zccusan is an early-stage project whose installation, kernel module, networking, CPU placement, huge-page allocation, memlock, and storage topology still require careful configuration.
-
Expect to use a capable automation agent to inspect your nodes, adapt these examples, and tune the installation before evaluating performance or safety.
-
In one recorded reference—not a promise—a Linux-block
/dev/zcnblk0client averaged 12.32 million 4 KiB remote-read IOPS across three runs on a tuned two-nodec8gn.48xlargeconfiguration with 64 pinned lanes, QD64 per worker, 4,096 aggregate outstanding operations, two EFA devices, EFA-direct RMA, volatile remote memory, and initiator-local CQ data-visible completion. -
In a separate recorded reference—not directly comparable—the same block frontend averaged 1.68 million 4 KiB early-local-ack write IOPS across three runs on a tuned two-node
c8in.metal-96xlconfiguration with 32 pinned lanes, QD128 per worker, 4,096 aggregate outstanding operations, two TCP rails, huge-page-backed shared arenas, and volatile remote memory, while remote sync and FUA were outside the timed workload. -
Those results demonstrate multi-million-IOPS potential but do not predict this one-lane mirrored PVC tutorial, durable media, mixed workloads, fsync latency, smaller machines, different kernels, or an untuned cluster.
-
You need Helm 3,
kubectl, three distinct Ready Linux workers, an encrypted pod or physical backplane, and permission to run the chart's privileged node setup and CSI containers. -
The normal multi-architecture DaemonSet image contains the Rust node loader and the current audited module matrix; it selects only an exact architecture and full
uname -rmatch and fails closed before CSI startup on an unsupported kernel. -
Create the namespace explicitly so its ownership and lifecycle are visible.
kubectl create namespace zccusan -
Choose two example servers and one example client. The RAM
MediaGrantuses the server label as its complete placement boundary, while the fio manifest asks Kubernetes for the client label.kubectl get nodes kubectl label node some-node-a storage.zcutils.io/example-server=true kubectl label node some-node-b storage.zcutils.io/example-server=true kubectl label node some-node-c storage.zcutils.io/example-client=true kubectl get nodes -L \ storage.zcutils.io/example-server,storage.zcutils.io/example-client -
If
InternalIPis not the dedicated storage address, annotate both example servers with their storage addresses.
kubectl annotate node some-node-a \
storage.zcutils.io/backplane-address=10.20.0.11
kubectl annotate node some-node-b \
storage.zcutils.io/backplane-address=10.20.0.12
- Leave
HELM_ARGSempty for TCP. For EFA-direct on EKS, use EFA-capable nodes configured for EFA traffic, install the pinned AWS EFA device plugin, and verify that all three selected nodes advertise one EFA resource before enabling the strict RDMA capability gate. Other RDMA environments must provide their corresponding device plugin and resource name.
HELM_ARGS=()
# EKS EFA-direct example; TCP users skip these commands.
helm repo add aws-eks https://aws.github.io/eks-charts
helm repo update aws-eks
helm upgrade --install aws-efa-device-plugin \
aws-eks/aws-efa-k8s-device-plugin \
--version v0.5.30 --namespace kube-system --wait --timeout 120s
kubectl get nodes \
-o 'custom-columns=NAME:.metadata.name,EFA:.status.allocatable.vpc\.amazonaws\.com/efa'
# HELM_ARGS+=(--set backplane.rdma.enabled=true)
# HELM_ARGS+=(--set backplane.rdma.provider=efa-direct)
# A custom unsupported kernel can use reviewed host or HTTP module-source
# values, or a custom full DaemonSet image; the matrix needs no extra values.
- Install the CRDs, operator, CSI driver, and Rust node bootstrap in one Helm step; pin
image.digestfor a reproducible deployment, and provide custom module-source values only when your exact kernel is outside the bundled matrix.
helm repo add zcutils https://robjcaskey.github.io/zcutils
helm repo update
helm upgrade --install zccusan \
zcutils/zcblock-csi \
--version 0.1.6 \
--namespace zccusan \
"${HELM_ARGS[@]}" \
--wait \
--timeout 10m
- Wait for the operator and node DaemonSet, then inspect setup logs if
/dev/zcnblk0and/dev/zcnblk-shmctldo not appear on eligible client nodes.
kubectl -n zccusan rollout status \
deployment/zccusan-zcblock-csi-operator --timeout=10m
kubectl -n zccusan rollout status \
daemonset/zccusan-zcblock-csi-node --timeout=10m
kubectl -n zccusan get pods -o wide
- Apply the safest first storage declaration: two userspace mirror copies over volatile RAM on nodes selected by the
MediaGrant. The operator turns that declaration into thezc-mirror-ramStorageClass. For real media, inspect and rendermirror-block.template.yamlonly when you intentionally dedicate two empty partitions and approve destructive raw writes.
kubectl apply -f \
zccusan/deploy/zcblock-csi/getting-started/mirror-ram.yaml
kubectl get mediagrant getting-started-ram -o yaml
kubectl get storageprofile getting-started-mirror-ram
kubectl get storageclass zc-mirror-ram
- For a qualified RDMA cluster, review
mirror-rdma.template.yaml, set its device-plugin resource and provider for your environment, apply it, and usezc-mirror-rdma-raminstead ofzc-mirror-rambelow without expecting silent TCP fallback.
less zccusan/deploy/zcblock-csi/getting-started/mirror-rdma.template.yaml
kubectl apply -f \
zccusan/deploy/zcblock-csi/getting-started/mirror-rdma.template.yaml
- Create the TCP PVC and fio Pod from their standalone files, or substitute the reviewed RDMA StorageClass in the PVC before applying the same workload.
kubectl apply -f \
zccusan/deploy/zcblock-csi/getting-started/mirror-pvc.yaml
kubectl apply -f \
zccusan/deploy/zcblock-csi/getting-started/mirror-fio.yaml
# RDMA alternative
# sed 's/zc-mirror-ram/zc-mirror-rdma-ram/' \
# zccusan/deploy/zcblock-csi/getting-started/mirror-pvc.yaml | \
# kubectl apply -f -
# kubectl apply -f \
# zccusan/deploy/zcblock-csi/getting-started/mirror-fio.yaml
- Follow fio live through the functional 4 KiB
randrwrun, remembering that this QD16 pod is deliberately not the tuned record configuration described above.
kubectl -n zccusan logs --follow \
--pod-running-timeout=10m \
zc-mirror-fio
- Verify that both storage-copy Pods run on the example servers, fio runs on the example client, and
/dev/zcnblk0remains only the client edge while the userspace mirror owns placement.
export ZCV="$(kubectl -n zccusan get zcvolumes \
-o jsonpath='{.items[0].metadata.name}')"
kubectl -n zccusan get zcvolume "$ZCV" -o yaml
kubectl -n zccusan get pods \
-l "storage.zcutils.io/volume=$ZCV" -o wide
kubectl -n zccusan get pod zc-mirror-fio \
-o jsonpath='{.spec.nodeName}{"\n"}'
-
Treat RAM media as explicitly volatile, provide transport encryption outside these preview data paths, and create any
VolumeSnapshotClassseparately because the chart does not create one. -
Before cleanup, optionally run the single-region HA boundary validation. It keeps a synchronized canary on the volume while you restart components, transfer custody at the userspace mirror's replicated HWM, and cause storage-copy failures. The exercise uses the continuous raw WAL path rather than moving a one-time snapshot and identifies the automatic failover and repair behavior that the current preview does not yet implement.
-
Remove the test pod and PVC when finished so CSI can delete the
ZcVolumeafter the operator tears down its userspace runtime.
kubectl delete -f \
zccusan/deploy/zcblock-csi/getting-started/mirror-fio.yaml
kubectl delete -f \
zccusan/deploy/zcblock-csi/getting-started/mirror-pvc.yaml
- Continue with tiering, then begin the three-part cross-region replication and recovery series. Consult the detailed performance documentation before publishing representative claims.