all_lessons/kubernetes_genai/07lesson 7 / 18

Part II - GPU platform

Multi-GPU Inference and Topology

Lesson 06 turned bare GPU machines into a schedulable platform layer: nodes labeled by accelerator type, the device plugin advertising nvidia.com/gpu, taints and node pools that keep the right pods on the right silicon. That all assumed the unit of serving is one GPU. But the moment a model's weights do not fit in one GPU's memory — or one GPU cannot serve enough concurrent users — you have to spread the work across several. This lesson is about the three fundamentally different ways to use more than one GPU, why they are not interchangeable, and why the physical wiring between those GPUs decides which one is even allowed.

Source coverage
PDF Chapter 3 and Chapter 4: multi-GPU inference, topology, and production placement.
Linear position
Prerequisite: Lesson 06 (GPU Nodes as a Platform Layer) — you can label nodes, request GPUs from a pod, and steer pods onto specific accelerator types with taints/node pools.
New capability: Decide when to replicate a model, shard it across GPUs on one node, or distribute it across nodes — and predict how each choice moves the bottleneck onto memory, compute, or the interconnect.
The plan
Five moves. (1) Frame the core question: replicate, shard within a node, or shard across nodes — and the mental model that separates them. (2) Define the three parallelism strategies precisely — data parallel / replication, tensor parallel, pipeline parallel — and what each one buys. (3) Work the numbers: does a 70B model fit one GPU? Does a 13B? Budget weights vs. KV cache separately, because "the model fits" does not mean "concurrency fits." (4) The topology layer: NVLink vs. InfiniBand vs. Ethernet, why tensor parallel turns every token into a communication event, and how to keep shards close on Kubernetes. (5) Failure modes, a checklist, and the hand-off to benchmarking, where you measure whether your placement guess was right.

1 · One GPU works — now what?

By lesson 06 you can put a model server in a pod, ask for one GPU, and have Kubernetes land it on the right node. The next question is forced on you by one of two limits. Either the model is too big — its weights exceed a single GPU's memory — or one replica is too slow — a single GPU cannot keep up with the request volume at your latency target. These two limits have completely different answers, and conflating them is the most common multi-GPU mistake.

Two terms used throughout, defined now. HBM (high-bandwidth memory) is the fast on-package memory soldered to a GPU — an NVIDIA A100 has 80GB, an H100 80GB, an H200 141GB. It is where weights, the KV cache, and activations all have to live; when people say "fits on the GPU" they mean "fits in HBM." The KV cache is the per-request store of attention keys and values for every token already generated; it grows with sequence length and with the number of concurrent requests, and it competes with the weights for the same HBM. Keep both in mind — they reappear in the capacity math.

The mental model for the whole lesson:

Replication buys throughput and fault isolation — more independent copies, each handling its own requests.
Tensor parallel buys model fit — one model too big for one GPU spread across several that act as one.
Pipeline parallel buys more model fit when even a node's worth of GPUs is not enough, at the cost of pipeline bubbles.
Multinode buys capacity beyond one host, but puts the network inside every token.

The decision rule that falls out of this: replicate until you cannot, then shard inside the fastest interconnect domain, and cross a node boundary only when forced. The rest of the lesson is making each clause precise.

2 · The three parallelism strategies

"Use more GPUs" is not one technique. The book lists three, and they differ in what they split and therefore what they cost.

Data parallel / REPLICATION
Put a full copy of the model on each GPU. A load balancer sends independent requests to whichever replica is free. Each replica is self-contained — no cross-GPU communication during inference. Buys raw throughput (N replicas ≈ N× the requests/s) and fault isolation (one replica dies, the others serve). The default; reach for anything else only when this is impossible.
Tensor parallel (TP)
Shard each layer's weight matrices across GPUs, so every GPU holds a slice of every layer. The GPUs must all-reduce (sum and redistribute partial results) at the boundary of every layer — for every token. Buys model fit: weights that exceed one GPU's HBM now fit across the group. Needs a fast interconnect (NVLink) because the all-reduce happens constantly.
Pipeline parallel (PP)
Split the layers into stages — GPU 0 runs layers 1-20, GPU 1 runs 21-40, and so on. A request flows through the stages in sequence. Buys fit for models so large that even a full node's TP group is not enough. Cost: pipeline bubbles — GPUs idle while waiting for the previous stage's first output, hurting latency and utilization.
Multinode
Any of the above stretched across more than one physical host. The cross-GPU traffic now leaves the box and crosses a network — InfiniBand if you are lucky, Ethernet if you are not. Buys capacity beyond a single server, at the price of slower collectives and a much larger failure surface.

The distinction that matters most: replication never communicates during inference; tensor and pipeline parallel do. A replica is an island. A tensor-parallel group is a single organism whose neurons are wired together — and the wire's speed becomes part of every token's latency. That is why the same GPUs can serve beautifully as four replicas and terribly as one TP-4 group spanning a slow link: the strategy, not the hardware, decides whether the interconnect is on the critical path.

3 · Does it fit? The capacity math

Whether you can replicate is a memory question, and you answer it with arithmetic, not intuition. The dominant term is the weights. A model in fp16 (2 bytes per parameter) needs roughly 2 × params bytes:

Worked number — 70B vs. 13B
A 70B model in fp16 needs 70 × 10⁹ × 2 bytes ≈ 140 GB. One 80GB A100/H100 holds 80GB. 140 > 80, so the weights cannot fit one GPU — replication is off the table, and you need TP ≥ 2. With TP-2 each GPU holds 140 / 2 = 70 GB of weights, leaving 80 − 70 = 10 GB per GPU for KV cache and activation scratch. Contrast a 13B model: 13 × 10⁹ × 2 ≈ 26 GB, comfortably under 80GB. It fits one GPU with ~54GB to spare — so you replicate it, you do not shard it. Sharding a 13B model across two GPUs would only add all-reduce latency to every token for no fit benefit.

But "the weights fit" is the wrong stopping point, because weights are not the only thing in HBM. You must budget four pools separately:

Memory poolWhat it isScales with
WeightsThe model parameters, loaded once and shared by all requestsModel size and precision (fixed)
KV cachePer-request attention keys/values for generated tokensConcurrency × sequence length (grows)
Activation scratchTransient buffers for the current forward passBatch size, hidden dim
Communication buffersStaging for all-reduce / pipeline transfers (TP/PP only)Shard count, message size
"It fits" ≠ "concurrency fits"
In the TP-2 case above, weights leave 10 GB per GPU (20GB across the pair) for everything else. If each concurrent request's KV cache costs, say, ~1.2GB at your average sequence length, that 20GB serves only ~16 simultaneous requests before HBM is exhausted — and then new requests queue or get rejected, regardless of how much compute is idle. The model "fitting" tells you nothing about how many users you can serve. Always size the KV-cache pool against your target concurrency, not just the weights against HBM.
Does it fit? — replicate vs. shard, and is the interconnect the bottleneck
Set the model size, precision, per-GPU HBM, and TP degree. The bars show weights-per-GPU (blue) against the HBM ceiling, with the leftover for KV cache in green. The verdict line tells you whether to replicate, whether you need more TP, and — critically — whether your chosen TP degree would have to span a slow link.
Weights / GPU
Leftover / GPU (KV+scratch)
Verdict
Interconnect

4 · Topology — the wire decides what is allowed

Kubernetes decides which node a pod lands on. It does not, by default, know or care about the physical graph of links between GPUs — and that graph is what determines whether tensor parallel is fast or fatal. (NVLink is NVIDIA's direct GPU-to-GPU link; NVSwitch is the on-board crossbar that wires every GPU in a node to every other at full NVLink speed, so an 8-GPU node behaves as one all-to-all fabric rather than a chain.) The interconnect hierarchy, fastest to slowest:

LinkWhereRough bandwidthGood for
NVLink / NVSwitchGPUs inside one node~900 GB/s (NVLink 4)Tensor parallel — the all-reduce is cheap enough to do every layer
InfiniBandGPUs across nodes (HPC fabric)~25-50 GB/sPipeline parallel across nodes; barely-tolerable TP if you must
Standard EthernetGeneric cloud networking~1-12 GB/sReplication only — never put TP collectives over this

Why this matters so much: tensor parallel makes every token a communication event. Each layer ends in an all-reduce across the TP group, and a model has dozens of layers, and you generate hundreds of tokens per response. If that all-reduce travels over NVLink at ~900 GB/s it is invisible; if it travels over Ethernet at ~10 GB/s it dominates, and your per-token latency — TPOT, time per output token — collapses by an order of magnitude. A TP group that spans a slow link does not "run slower" — it stops being viable.

So the placement rule is concrete: keep a tensor-parallel group entirely within one NVLink domain (one node, where the GPUs are NVSwitch-connected). On Kubernetes you enforce this with the same tools from lesson 06, now aimed at locality rather than just type:

# Pin a TP-8 server to a single 8-GPU node so all-reduce stays on NVLink.
# Request all 8 GPUs in one pod -> the scheduler must place it on one node.
resources:
  limits:
    nvidia.com/gpu: 8        # whole node; TP group cannot be split across hosts
# And steer to nodes that actually have NVSwitch:
nodeSelector:
  gpu.topology/nvlink-domain: "full"   # a label your node setup applies

For multinode (pipeline parallel across hosts, or TP-then-replicate layouts), you additionally want the cooperating pods placed close in the network — same rack, same InfiniBand partition. Pod affinity with a topology key keeps cooperating pods together; the gang-scheduling concern (all-or-nothing placement for a distributed group) is what the next lessons build toward. The point for now: topology-aware placement is a deliberate act. Left alone, the scheduler will happily pack two unrelated GPU pods onto the host you wanted whole, breaking the NVLink locality your TP group assumed.

5 · Choosing — the trade table

Putting it together, here is the decision as a table you can run top-to-bottom:

StrategyBuysCostsWhen
ReplicationThroughput, fault isolation, simplicity (no collectives)Duplicates weights and warm KV caches across copiesModel fits one GPU — the default; scale out, not up
Tensor parallelModel fit when weights exceed one GPU's HBMAll-reduce every layer, every token; needs NVLinkWeights > one GPU but ≤ one node's combined HBM
Pipeline parallelFit for models too big for even one node's TP groupPipeline bubbles; latency and utilization hitModel exceeds a single node; combine with TP within stages
MultinodeCapacity beyond a single hostSlower collectives over the network; larger failure surfaceForced by size; keep TP inside nodes, PP/replication across

6 · Where placement goes wrong

Failure modes

  • TP across a slow link. A tensor-parallel group is placed across nodes (or onto GPUs joined only by PCIe/Ethernet), so every layer's all-reduce crosses a slow boundary and every token pays. The signal: TPOT is 5-20× worse than a single-node TP run on the same model; network counters spike in lockstep with generation.
  • The scheduler packs your "whole" node. You assumed eight free GPUs on one host for a TP-8 group, but the platform placed an unrelated GPU pod there first, breaking NVLink locality or leaving the group split. The signal: intermittent latency depending on which node won the placement race; the fix is requesting the whole node or using affinity/anti-affinity.
  • KV cache ignored in capacity math. You sized GPUs so the weights just fit, declared victory, and shipped. Under real concurrency the KV cache exhausts HBM, requests queue or OOM, and compute sits idle. The signal: out-of-memory errors or a hard concurrency ceiling well below what the compute could handle.
  • Sharding a model that fits. A 13B model put behind TP-2 "to use the GPUs," adding all-reduce latency to every token for zero fit benefit when two independent replicas would have served more, faster. The signal: lower throughput and higher latency than the same GPUs running as replicas.

Implementation checklist

  • Did you compute weights = bytes/param × params and check it against one GPU's HBM before reaching for TP? Replicate if it fits.
  • Is every tensor-parallel group confined to a single NVLink/NVSwitch domain (one node), never split across a slower link?
  • Are you using node selectors, whole-node GPU requests, pod affinity, or topology labels to keep cooperating shards physically close?
  • Did you budget the four memory pools separately — weights, KV cache, activation scratch, communication buffers — and size KV cache against target concurrency?
  • For multinode, is the slow boundary on the least chatty axis (replication or pipeline stages), with the chatty TP collectives kept inside nodes?

Checkpoint exercise

Try it
You are given a cluster of 80GB-HBM nodes, eight GPUs each, NVSwitch within a node and Ethernet between nodes. Plan the serving layout for (a) a 13B model and (b) a 70B model, both fp16, targeting 64 concurrent requests at ~2GB KV cache each. For each: state whether you replicate or shard, the TP degree if any, how many GPUs/replicas you need to hold the KV cache, and which interconnect each communication path uses. Then identify the one placement constraint you must give Kubernetes so the plan is not silently broken by the scheduler.

Where this points next

You can now reason your way to a layout — replicate the 13B, TP-2 the 70B inside a node, keep the collectives on NVLink. But a layout chosen by arithmetic is a hypothesis, not a result. The next lesson, 08 - Benchmarking and Runtime Tuning, gives you the machinery to test it: how to measure TTFT (time to first token), TPOT, and throughput under realistic load, how to find the batch size and concurrency where latency knees upward, and how to tell whether the bottleneck your placement created is memory, compute, or — as this lesson warned — the interconnect. Placement decides what is possible; benchmarking tells you what actually happened.

Takeaway
Using more than one GPU is three different moves, not one. Replication (data parallel) puts a full model copy per GPU and buys throughput and fault isolation with zero inference-time communication — it is the default, and you reach past it only when forced. Tensor parallel shards each layer's matrices across GPUs and buys model fit, but all-reduces every layer for every token, so it must live inside a fast NVLink domain. Pipeline parallel splits layers into stages for models too big for even a node, paying in bubbles. The fit question is arithmetic — a 70B fp16 model needs ~140GB, so it cannot fit one 80GB GPU and demands TP ≥ 2; a 13B (~26GB) fits and should be replicated, not sharded. But "the model fits" is never the whole story: budget weights, KV cache, activation scratch, and communication buffers separately, because the KV cache, not the weights, usually caps concurrency. Finally, the physical topology decides which strategy is even legal — TP across a slow link turns every token into a communication problem, so keep tensor-parallel groups inside one node and use Kubernetes labels, whole-node requests, and affinity to stop the scheduler from breaking the locality your plan assumed.

Interview prompts

Cross-links