Repository navigation
shim: don't tear down shared pod UVM on Hyper-V container restart - #2846
Dawei Wei (rzlink) wants to merge 1 commit into
Conversation
65fd990 to
808e988
Compare
|
Dawei Wei (@rzlink) |
Thanks. This is for Kubernetes |
808e988 to
0125160
Compare
0125160 to
6543d7c
Compare
6543d7c to
ecf5524
Compare
On Hyper-V isolated Windows pods, a container restart can tear down the shared pod UVM, killing the sandbox and every other container in the pod. A Windows guest holds the WaitForProcess response of a process until the host closes the process's stdio relay. A descendant process (for example the child of "sh -c") can keep the relay open after the process itself has exited, and the host only closes the relay once the wait completes, so neither side makes progress. The SIGKILL that follows returns hrNotFound, which was logged and ignored, leaving the wait pending. The 30s SIGKILL watchdog in KillExec then closed the UVM, even when the task did not own it. Fix both sides: - Gate the KillExec watchdog on ownsHost, so only a task that owns its UVM can close it. A workload container shares the pod UVM. - When a Windows guest reports a process missing while its wait is still pending, wait a grace period for the real exit, then close the host side of the process's stdio relay. The guest then delivers the real exit code; no exit is synthesized. The grace period matches the default CopyAfterExitTimeout the host already allows stdio relays after a process exits. Linux guests are excluded: they drain and close the relay themselves before answering the wait, and reconnect if the host closes it first. Signed-off-by: Dawei Wei <wei.dawei.cn@gmail.com>
ecf5524 to
076bbe1
Compare
|
I've reworked the fix based on further investigation and squashed it into a single commit; the description is updated. To answer the earlier question: yes, the exec SIGKILL hangs well past 30s. The container runs Changes from the previous revision:
Validated on a CAPZ Hyper-V cluster with ETW traces: the 4 |
|
Dawei Wei (@rzlink) - I'm not sure I fully grasp this yet. So your goal is to still allow the signal to be sent to the init process, but not to allow the closure unless the target was the init process? The logic here is a bit convoluted IMO. Isn't your goal that we would not tear down the init container at all? |
Justin (@jterry75) - Yes, if by "init container" you mean the pod sandbox (pause) task that owns the UVM: the goal is that stopping one workload container never tears down the sandbox / shared UVM. Today, when a workload container's exit isn't reported within 30s after SIGKILL, the watchdog closes that shared UVM and takes down the whole pod. The
The |
|
Justin (@jterry75) - I also have a standalone repro that doesn't need Kubernetes: one script provisions a throwaway Azure VM with containerd, runs a two-container Hyper-V pod, stops one container the way kubelet does, and checks whether the other one survives. With the current shim the pod UVM dies; Happy to share it ([gist link] / ping me on Teams). |
Description
Fix in-place container restarts for Hyper-V isolated Windows pods (
runhcs-wcow-hypervisor), e.g. withContainerRestartRules/RestartAllContainers. Today a workload container restart can tear down the shared pod UVM, killing the sandbox and every other container in the pod. This is the failure reported in kubernetes/kubernetes#140187.Root cause
A Windows guest holds the
WaitForProcessresponse of a process until the host closes the process's stdio relay. A descendant process (for example the child ofsh -c "...; sleep ...") can keep the relay open after the process itself has exited, while the host only closes the relay once the wait completes, so neither side makes progress. The SIGKILL that follows returnshrNotFound, which was logged and ignored, leaving the wait pending. The 30s SIGKILL watchdog inKillExecthen closed the UVM, even when the task did not own it.Fix
KillExecSIGKILL watchdog onownsHost, so only a task that owns its UVM can close it. A workload container shares the pod UVM.CopyAfterExitTimeoutthe host already allows stdio relays after a process exits, and the path is only taken on the abnormalhrNotFoundpath.Linux guests (LCOW) are excluded: they drain and close the relay themselves before answering the wait, and reconnect if the host closes it first. Process-isolated containers don't use this code path.
Testing
go test ./internal/gcs ./cmd/containerd-shim-runhcs-v1, including new tests that:RestartAllContainerse2e specs that fail today 10 times each: 40/40 passed (0/4 before). With ETW (ContainerPlatform) traces: no watchdog, no UVM close/terminate during the tests; everyhrNotFoundwas followed by the stdio close after ~1s and the real exit (0xC000013A) within a few ms, which containerd reported as the container's exit status.sh -cwith a child,sh -cwith redirect /exec, direct process, grace 0/2): sandbox and sibling containers stayed running and the UVM was not closed in every case; real exit codes were reported.