Building a CSI Driver with Go

A hands-on guide, one gRPC call at a time

How this book works

Every chapter adds one working piece to a real CSI driver — no chapter is theory-only. Starting in Chapter 3, every method is written test-first (red, green, refactor), and design decisions are pointed out through the SOLID principles at the exact point they apply, rather than taught as separate theory. By the end you'll have a driver that provisions, mounts, and snapshots volumes on a kind cluster, backed by nothing more exotic than local directories on the node (so you can run every example without cloud credentials). The RPC signatures — what CreateVolume, NodePublishVolume, and the rest take and return — are exactly what a real EBS or NAS-backed driver would implement too; what's different is everything inside each method: attach/detach against a real API, durable metadata, topology, and backend-specific failure handling all have to be built out for real, not swapped in from one file.

Worth naming honestly up front: later chapters mostly show edits to files a previous chapter already built — "replace the old return with this," "add this method below" — rather than reprinting a complete file every time. That keeps each chapter's code block focused on the one thing it's teaching, but it also means there's no single "final state" file to compare against if you get lost. The fix is cheap and worth doing as a habit, not a one-time setup step: after applying a chapter's edits, run

gofmt -l . && go vet ./... && go build ./... && go test ./...

before moving on. Every chapter in this book holds to exactly that bar before its own code block is considered finished — if any of those four commands complain, something from that chapter didn't get applied the way it was written, and it's much easier to find on a chapter boundary than three chapters later.

Table of contents

  1. Why a CSI Driver — what CSI actually is, the three gRPC services, why Kubernetes offloads storage this way
  2. Set Up the Project — Go module, directory layout, protobuf/gRPC toolchain, kind cluster 2.5. A Brief gRPC Primer — the request/response model, why CSI uses Unix sockets instead of TCP, how spec error codes become Go code
  3. The Identity Service — the simplest of the three services; get it running end-to-end over a Unix socket before touching anything else
  4. Registering with Kubelet — the node-driver-registrar sidecar, CSIDriver object, deploying your first pod
  5. The Node Service, Part 1NodeGetInfo, NodeGetCapabilities
  6. The Node Service, Part 2NodePublishVolume / NodeUnpublishVolume: actually mounting something into a pod
  7. The Controller ServiceCreateVolume / DeleteVolume, idempotency, the external-provisioner sidecar
  8. Attaching VolumesControllerPublishVolume, why this single-node driver doesn't deploy the external-attacher sidecar, StorageClass and PVC end to end
  9. Testing with csi-sanity — the official conformance suite, what it catches that manual testing won't
  10. SnapshotsCreateSnapshot, external-snapshotter, VolumeSnapshot objects
  11. Getting to Production — structured error codes, observability (structured logging, Prometheus metrics served for real over HTTP)
  12. Integration Testing — automated Go tests against a real kind cluster, codifying the manual kubectl/grpcurl proofs this book has done by hand since Chapter 8, including a regression test for the real CreateSnapshot idempotency bug Chapter 10 hit live

Each chapter ends with a working git commit-able state. Chapters 3–8 are the core; 9–10 round the driver out to something conformant; 11 is about what changes when this stops being a toy; 12 closes the loop by automating the real-cluster verification this book has relied on by hand throughout.

Cut from the original plan: Volume Expansion (ControllerExpandVolume/ NodeExpandVolume) doesn't teach much for this driver — CreateVolume never enforced real capacity limits (just recorded a number in metadata), so "expanding" a local directory has no real backing mechanism to exercise. Left out rather than padded with a hollow chapter.