Skip to content

Commit ad35808

Browse files
committed
fix(snapshotter): address scheduling review feedback
Signed-off-by: Yuan Chen <yuanchen97@gmail.com>
1 parent 347aee9 commit ad35808

5 files changed

Lines changed: 41 additions & 27 deletions

File tree

docs/user/cli-reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1172,7 +1172,7 @@ spec:
11721172
serviceAccountName: aicr
11731173
nodeSelector:
11741174
my-org/gpu-pool: "true"
1175-
tolerations:
1175+
tolerations: # [] clears the live snapshot agent's tolerate-all default
11761176
- "gpu-type=h100:NoSchedule"
11771177
requireGpu: true
11781178
execution:

docs/user/validation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -910,7 +910,7 @@ aicr validate \
910910
--toleration dedicated=worker-workload:NoExecute
911911
```
912912

913-
These flags affect the inner benchmark pods that run on GPU nodes (NCCL workers, Dynamo workers). When `--snapshot` is omitted, they also configure the preliminary live snapshot agent. With no toleration override, that agent tolerates all taints; an explicit `spec.validate.agent.tolerations: []` clears that default. Neither flag affects the validator orchestrator Job itself.
913+
These flags affect the inner benchmark pods that run on GPU nodes (NCCL workers, Dynamo workers). The example above supplies `--snapshot`, so it does not launch the live snapshot agent. When `--snapshot` is omitted, the flags also configure that preliminary agent. With no toleration override, the agent tolerates all taints; an explicit `spec.validate.agent.tolerations: []` clears that default. Neither flag affects the validator orchestrator Job itself.
914914

915915
For `inference-perf` specifically, `--node-selector` narrows the pool of candidate GPU nodes — the validator then picks the candidate with the most free GPUs (subtracting same-ledger occupancy only — DRA allocations from DRA capacity, device-plugin requests from device-plugin capacity — and skipping DRA candidates that carry scalar `nvidia.com/gpu` workloads) and pins all Dynamo Frontend + worker pods to that node via `kubernetes.io/hostname`. The AIPerf benchmark runner pod is CPU-only, uses a tolerate-all / no-nodeSelector pod spec, and is unaffected by these flags.
916916

pkg/cli/validate.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ func resolveValidateNodeSelector(cmd *cli.Command, resolved *config.ValidateReso
135135
// inference-perf that want to mirror the target node's taints by default
136136
// must distinguish "operator opted into tolerate-all" from "operator said
137137
// nothing". Returning nil here when neither CLI nor config set the field
138-
// keeps the env var unset, so the inner validator context sees nil.
138+
// keeps the env var unset, so the inner validator context sees nil. The live
139+
// snapshot path consumes that same nil as its signal to apply the agent's
140+
// tolerate-all default at the Job projection boundary.
139141
func resolveValidateTolerations(cmd *cli.Command, resolved *config.ValidateResolved) ([]corev1.Toleration, error) {
140142
if cmd.IsSet("toleration") {
141143
tols, err := snapshotter.ParseTolerations(cmd.StringSlice("toleration"))

pkg/snapshotter/agent.go

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,32 @@ type AgentConfig struct {
148148
Limits corev1.ResourceList
149149
}
150150

151+
// buildAgentConfig projects snapshotter configuration onto the deployer's
152+
// Job configuration. Keep scheduling defaults at this projection boundary so
153+
// every snapshot-agent caller gets the same nil-versus-empty behavior.
154+
func buildAgentConfig(config *AgentConfig, agentOutput string) agent.Config {
155+
return agent.Config{
156+
Namespace: config.Namespace,
157+
ServiceAccountName: config.ServiceAccountName,
158+
JobName: config.JobName,
159+
Image: config.Image,
160+
ImagePullSecrets: config.ImagePullSecrets,
161+
NodeSelector: config.NodeSelector,
162+
Tolerations: effectiveAgentTolerations(config.Tolerations),
163+
Output: agentOutput,
164+
Debug: config.Debug,
165+
Privileged: config.Privileged,
166+
RequireGPU: config.RequireGPU,
167+
RuntimeClassName: config.RuntimeClassName,
168+
MaxNodesPerEntry: config.MaxNodesPerEntry,
169+
OS: config.OS,
170+
ClusterConfigPath: config.ClusterConfigPath,
171+
DiscoverNetwork: config.DiscoverNetwork,
172+
Requests: config.Requests,
173+
Limits: config.Limits,
174+
}
175+
}
176+
151177
// deployAndWaitForResult handles the common deploy-wait-retrieve lifecycle for an agent Job.
152178
// It creates the deployer, deploys RBAC and the Job, streams logs, waits for completion,
153179
// and retrieves the snapshot data from the result ConfigMap.
@@ -170,26 +196,7 @@ func deployAndWaitForResult(ctx context.Context, clientset k8sclient.Interface,
170196
// name the injected selector (TOCTOU: node may be cordoned after detection).
171197
autoInjectedGPUSelector := maybeInjectGPUNodeSelector(ctx, clientset, config)
172198

173-
agentConfig := agent.Config{
174-
Namespace: config.Namespace,
175-
ServiceAccountName: config.ServiceAccountName,
176-
JobName: config.JobName,
177-
Image: config.Image,
178-
ImagePullSecrets: config.ImagePullSecrets,
179-
NodeSelector: config.NodeSelector,
180-
Tolerations: effectiveAgentTolerations(config.Tolerations),
181-
Output: agentOutput,
182-
Debug: config.Debug,
183-
Privileged: config.Privileged,
184-
RequireGPU: config.RequireGPU,
185-
RuntimeClassName: config.RuntimeClassName,
186-
MaxNodesPerEntry: config.MaxNodesPerEntry,
187-
OS: config.OS,
188-
ClusterConfigPath: config.ClusterConfigPath,
189-
DiscoverNetwork: config.DiscoverNetwork,
190-
Requests: config.Requests,
191-
Limits: config.Limits,
192-
}
199+
agentConfig := buildAgentConfig(config, agentOutput)
193200

194201
deployer := agent.NewDeployer(clientset, agentConfig)
195202

@@ -268,6 +275,7 @@ func deployAndWaitForResult(ctx context.Context, clientset k8sclient.Interface,
268275
fmt.Fprintln(logWriter(), logs)
269276
fmt.Fprintln(logWriter(), "--- end logs ---")
270277
}
278+
isTransient := errors.IsTransient(waitErr)
271279
msg := "job failed"
272280
if autoInjectedGPUSelector {
273281
msg = "job failed (auto-injected node selector nvidia.com/gpu.present=true — " +
@@ -276,11 +284,15 @@ func deployAndWaitForResult(ctx context.Context, clientset k8sclient.Interface,
276284
"key=value:effect. To override placement, pass --node-selector " +
277285
"kubernetes.io/hostname=<gpu-node>; --require-gpu selects a node " +
278286
"advertising the nvidia.com/gpu resource)"
287+
} else if isTransient {
288+
msg = "job failed (verify target nodes are Ready and schedulable; if " +
289+
"tolerations were explicitly cleared or replaced, pass a matching " +
290+
"--toleration key=value:effect)"
279291
}
280292
// A wait that exceeded the deadline (pending pod, image pull, no schedulable
281293
// node) is transient and retryable — classify it as ErrCodeTimeout rather
282294
// than masking it as a deterministic ErrCodeInternal failure.
283-
if errors.IsTransient(waitErr) {
295+
if isTransient {
284296
return nil, errors.Wrap(errors.ErrCodeTimeout, msg, waitErr)
285297
}
286298
return nil, errors.Wrap(errors.ErrCodeInternal, msg, waitErr)

pkg/snapshotter/agent_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func TestDefaultTolerations(t *testing.T) {
5252
}
5353
}
5454

55-
func TestEffectiveAgentTolerations(t *testing.T) {
55+
func TestBuildAgentConfigTolerations(t *testing.T) {
5656
explicit := []corev1.Toleration{
5757
{
5858
Key: "dedicated",
@@ -84,9 +84,9 @@ func TestEffectiveAgentTolerations(t *testing.T) {
8484

8585
for _, tt := range tests {
8686
t.Run(tt.name, func(t *testing.T) {
87-
got := effectiveAgentTolerations(tt.input)
87+
got := buildAgentConfig(&AgentConfig{Tolerations: tt.input}, "snapshot.yaml").Tolerations
8888
if !reflect.DeepEqual(got, tt.want) {
89-
t.Errorf("effectiveAgentTolerations() = %#v, want %#v", got, tt.want)
89+
t.Errorf("buildAgentConfig().Tolerations = %#v, want %#v", got, tt.want)
9090
}
9191
})
9292
}

0 commit comments

Comments
 (0)