Kernel-level QoS enforcement

Every pod on a Temper node belongs to one of five QoS tiers, derived from standard Kubernetes PriorityClasses and resource requests — no CRDs, no custom labels, no application changes. Tiers become kernel scheduler layers with real teeth.

The five tiers

TierPriorityClassPriority valueLayer kindProtectedPreemptExclusive coresTimeslice
Criticaltemper-critical≥ 1,000,000Confinedyesyesyes5 ms
Hightemper-high≥ 100,000Groupedyesyesno10 ms
Normaltemper-normal≥ 0Groupednonono20 ms
Lowtemper-low≥ −100Opennonono30 ms
Backgroundtemper-background< −100Opennonono5 ms

The thresholds are configurable in the agent’s [priority_mapping] config section; the values above are the shipped defaults, matching the PriorityClasses the helm chart installs.

Assigning a tier

Assignment is one field in the pod spec:

spec:
  priorityClassName: temper-critical

Because the input is pod.spec.priority, existing PriorityClasses you already use for eviction ordering participate automatically — the same priority signal now also means something at the CPU scheduler.

Defaults for unlabeled pods

Pods with no PriorityClass (priority 0) are tiered by their Kubernetes QoS class:

Kubernetes QoS classDefault tier
Guaranteed / BurstableNormal
BestEffortBackground

So an untouched cluster behaves sensibly on day one: nothing is fenced, best-effort work yields, and enforcement sharpens only as you hand out PriorityClasses.

Resource-aware layer parameters

Tier membership decides the kind of layer; your pods’ actual resource specs decide its size. On every assignment change the agent recomputes, per tier:

Two system layers are always appended: a small always-on layer that keeps the scheduler and agent threads serviced even under total saturation, and a catch-all Open layer for everything else (kubelet, node daemons). Empty layers are omitted. If the Critical tier requests more whole cores than the node can actually fence, Temper demotes that layer to Grouped instead of silently caging it — graceful degradation over false confinement (sizing guidance: operations).

Inspecting the result

The generated scheduler configuration is never a black box:

kubectl get node <node> -o jsonpath='{.metadata.annotations.temper\.codes/qos-distribution}'
# per-tier pod counts and CPU millis, as published for the scheduler plugin

The full generated layer config is available via the agent’s gRPC (GetLayeredConfig), the CLI, or the dashboard, which also shows a diff across config generations.

Division of labor with profiles

Tiers arbitrate between workloads. To schedule the threads inside one workload differently — hot loops vs. I/O chains vs. housekeeping — use workload profiles on top of the tier system.