fix: fall back to the default runtime when the toolkit config file is missing - #2023
fix: fall back to the default runtime when the toolkit config file is missing#2023polyglotm wants to merge 1 commit into
Conversation
… missing The generated runtime wrapper checks whether the NVIDIA driver modules are loaded and falls back to the default low-level runtime if they are not. It never checks whether its own configuration file is present. When the NVIDIA runtime is containerd's default runtime, every container on the node is routed through this wrapper, not only GPU containers. If the driver is loaded but the toolkit's config.toml is absent, the module guard passes, nvidia-container-runtime.real runs, and it fails to open its required configuration file. Sandbox creation then fails for every pod on the node, including the nvidia-container-toolkit DaemonSet that would rewrite the file. The node reports Ready with zero allocatable GPUs and cannot be repaired by Kubernetes, because every tool it has is a container. We hit this in production on a node whose toolkit reinstall was interrupted by a reboot after the executables were written and before config.toml was. Restoring that single file released the node: the toolkit pod started within a minute and allocatable GPUs returned with no other change. Add a second guard in the same shape, and with the same fallback target, as the existing module check. A missing configuration file becomes a reason to invoke the default runtime rather than a reason to fail, so the node degrades to running GPU containers without GPU access instead of running nothing at all, and the toolkit DaemonSet can start and repair it. The guard is gated on CheckModules in addition to the config path. nvidia-container-runtime-hook also receives a config path but is not a runtime, and must not be given a low-level runtime fallback. This deliberately fails open: during the window a GPU container may start without GPU access. That window is bounded by the DaemonSet rewriting the file, and the state is externally visible as a Ready node with zero allocatable GPUs. Signed-off-by: Jay Lee <polyglot.m@gmail.com>
The
nvidia-container-toolkit/internal/runtime/runtime.go Lines 46 to 49 in 3a21b97 Constructing a new config (note, that the nvidia-container-toolkit/api/config/v1/config.go Lines 93 to 95 in 3a21b97 Relevant code in nvidia-container-toolkit/api/config/v1/toml.go Lines 88 to 98 in 3a21b97 @polyglotm can you provide more information on the exact scenario you hit? Steps on how to reproduce and exact logs from the incident, if possible, would help diagnose this better. I am not convinced the code in this PR is the direction we should go in. |
What happens today
The generated runtime wrapper has one guard: it checks whether the NVIDIA driver modules are
loaded, and falls back to the default low-level runtime if they are not. It never checks
whether its own configuration file is present.
When the NVIDIA runtime is containerd's default runtime, every container on the node is
routed through that wrapper, not only GPU containers. If the driver is loaded but
.config/nvidia-container-runtime/config.tomlis absent, the module guard passes,nvidia-container-runtime.realruns, and it fails withcouldn't open required configuration file. Sandbox creation then fails for every pod on the node — including thenvidia-container-toolkit DaemonSet that would rewrite the file.
The node reports
Readywith clean conditions and zero allocatable GPUs. Kubernetes cannotrepair it, because every tool it has is a container. Recovery requires host access.
We hit this in production on 2026-08-23. A reboot interrupted a toolkit reinstall after the
executables were written and before
config.tomlwas. Restoring that single 1122-byte filereleased the node completely: the toolkit pod went
PendingtoRunningwithin 46 seconds,allocatable GPUs returned, and the node recovered with no other change. A sibling node with
the same toolkit build and its config file intact was healthy throughout.
What this changes
One additional guard, in the same shape and with the same fallback target as the existing
module check. A missing configuration file becomes a reason to invoke the default runtime
rather than a reason to fail.
The node degrades to "GPU containers start without GPU access" instead of "nothing starts at
all" — and because the toolkit DaemonSet can now start, it repairs the node itself.
collectExecutablesalready computes this path and already passes it to the wrapper asNVIDIA_CTK_CONFIG_FILE_PATH, so no new plumbing is required.Why the guard is gated on
CheckModulesas wellnvidia-container-runtime-hookalso receives a config path but is not a runtime, so itmust not be given a low-level runtime fallback. Gating on
.ConfigFilePathalone would injectexec runc "$@"into the hook's wrapper. The condition is therefore{{- if and .CheckModules .ConfigFilePath }}, reusing the flag that already means "this is aruntime".
Tests
Three cases were added to the existing
TestWrapperRendertable:config check is added when a config path is setconfig check is not added without the module checkconfig check is not added without a config pathEach was confirmed to fail when the implementation is wrong, not merely to pass when it is
right:
{{- if .ConfigFilePath }}, exactly one case fails, and it isconfig check is not added without the module check;config check is added when a config path is set.TestToolkitInstallerfixtures were updated for the three runtime wrappers. Thenvidia-container-runtime-hookandnvidia-container-clifixtures are deliberatelyunchanged, which is an independent check that the guard scopes correctly.
gofmt,go vet,go build ./...andgo test ./cmd/nvidia-ctk-installer/...are clean.The trade-off, stated up front
This fails open. During the window a GPU container may start believing it has a GPU and not
have one. That is a real cost, and it is a policy decision that belongs to you. Two things
bound it: the window ends when the DaemonSet rewrites the file, and the state is externally
visible as a
Readynode with zero allocatable GPUs.For non-GPU containers the change is strictly an improvement — today they are taken down by a
GPU-specific configuration file with which they have no relationship.
If you would rather fail closed, or fix the install sequence so that the wrapper cannot
become active before its configuration exists, we would be glad to take that instead. The
reproduction stands either way, and the outcome we care about is that a reboot cannot strand a
node with no automated recovery.
Versions
Reproduced in production on v1.17.8. Confirmed still present on
mainand in v1.20.0 by sourceinspection: the generator moved from
tools/container/toolkit/runtime.gotocmd/nvidia-ctk-installer/toolkit/installer/executables.goand became a template, but noconfiguration check was added. Upgrading does not resolve this.
Related
DefaultRuntimeExecutablePath, which this change reuses as its fallbacktarget.
interrupt the install. A maintainer noted in 2023 that unnecessary restarts were worth
avoiding; that issue was closed for inactivity in 2026 without resolution. We are not
claiming this proves our sequence, only that the behaviour is known.
Open questions
executables and writing
config.toml?wrapper from becoming active before its required configuration file is present?
setting?