-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Preventing containers from being unable to be deleted #4757
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
11c5aba
to
60ae641
Compare
I was unable to add integration tests for this PR without resorting to some hacky methods, but I tested whether this issue was resolved in the kubernetes-sigs/kind repository. In brief, I discovered this issue while working in the kubernetes/kubernetes repo to propagate kubelet's context to the container runtime. The issue manifested as the test job being unable to tear down after the k/k repo's e2e tests completed, because the leaked Therefore, I opened a PR in the kubernetes-sigs/kind repo to debug this issue by manually replacing the containerd/runc binaries in the CI environment. After building the code from this PR and replacing the binaries in the CI environment, the test job no longer failed to tear down due to systemd being unable to shut down, as the leaked processes were resolved. Ref: kubernetes-sigs/kind#3903 (Some job failures occurred due to the instability of the k/k repo e2e tests, but they are unrelated to this issue.) I also conducted some manual tests targeting the scenarios where the leftover container is in the paused and stopped states.Paused: Inject sleep to allow us to control where the code is interrupted.You can add a headerdiff --git a/vendor/github.com/opencontainers/cgroups/systemd/v1.go b/vendor/github.com/opencontainers/cgroups/systemd/v1.go
index 8453e9b4..bbe3524c 100644
--- a/vendor/github.com/opencontainers/cgroups/systemd/v1.go
+++ b/vendor/github.com/opencontainers/cgroups/systemd/v1.go
@@ -6,6 +6,7 @@ import (
"path/filepath"
"strings"
"sync"
+ "time"
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
"github.com/sirupsen/logrus"
@@ -361,6 +362,7 @@ func (m *LegacyManager) Set(r *cgroups.Resources) error {
}
}
setErr := setUnitProperties(m.dbus, unitName, properties...)
+ time.Sleep(time.Second * 30)
if needsThaw {
if err := m.doFreeze(cgroups.Thawed); err != nil {
logrus.Infof("thaw container after SetUnitProperties failed: %v", err)
stopped: Inject sleep to allow us to control where the code is interrupted.You can add a headerdiff --git a/libcontainer/process_linux.go b/libcontainer/process_linux.go
index 96e3ca5f..350e3660 100644
--- a/libcontainer/process_linux.go
+++ b/libcontainer/process_linux.go
@@ -613,6 +613,7 @@ func (p *initProcess) start() (retErr error) {
return fmt.Errorf("unable to apply cgroup configuration: %w", err)
}
}
+ time.Sleep(time.Second * 30)
if p.intelRdtManager != nil {
if err := p.intelRdtManager.Apply(p.pid()); err != nil {
return fmt.Errorf("unable to apply Intel RDT configuration: %w", err)
|
Signed-off-by: HirazawaUi <695097494plus@gmail.com>
60ae641
to
29dcef9
Compare
See also: #2575 |
// because the container lacks a state.json file. | ||
// This typically occurs when higher-level | ||
// container runtimes terminate the runc create process due to context cancellation or timeout. | ||
_, err = p.container.updateState(nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this fixing the problem or there is still a race if the signal is sent before we do this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although
runc init STAGE_PARENT/STAGE_CHILD
may exist simultaneously whenrunc create
receives SIGKILL signal, after runc create terminates,STAGE_PARENT/STAGE_CHILD
will also terminate due to the termination ofrunc create
:
- STAGE_PARENT: Directly relies on
pipenum
to communicate withrunc create
. Whenrunc create
terminates,pipenum
is closed, causingSTAGE_PARENT
to fail when reading/writing topipenum
, triggering bail and termination.- STAGE_CHILD: Relies on syncfd to synchronize with
STAGE_PARENT
. WhenSTAGE_PARENT
terminates,syncfd
is closed, causingSTAGE_CHILD
to fail when reading/writing tosyncfd
, triggering bail and termination.
As stated here, runc init [STAGE_PARENT/STAGE_CHILD]
will terminated after runc create
terminates, and at this point, the cgroup has not yet been created. I believe this does not lead to a race condition, nor does it cause process leaks or other resources to remain uncleaned.
@HirazawaUi thanks! So my comment was on-spot, but you didn't need to remove the assignment? For testing, I'd like to have something. It should be simple and kind of reliable. Here are some ideas, but we don't need a test if we don't find a reasonable and simple way to test this:
|
I believe that removing this assignment and delaying the assignment process until after runc/libcontainer/container_linux.go Lines 893 to 895 in a4b9868
runc/libcontainer/container_linux.go Lines 905 to 913 in a4b9868
|
I will try testing it in the direction of Suggestion 2 (it seems the most effective). If it cannot be implemented, I will promptly provide feedback here :) |
39d801e
to
a6ebd29
Compare
Test case has been added. While attempting to use Compared to event monitoring, this approach better aligns with the scenario we encountered and is completely asynchronous. The only downside seems to be its fragility, but I added numerous @rata Do you think this test case sufficiently covers the scenarios for this PR? |
This comment was marked as spam.
This comment was marked as spam.
ping @kolyshkin @AkihiroSuda @rata Could you take another look at this PR? Any feedback would be greatly appreciated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Sorry, had some pending review comments which I forgot to submit)
Also, you need a proper name/description for the second commit. Currently it just says "add integration test" which is enough in the context of this PR, but definitely not enough when looking at git history.
done | ||
|
||
# Start runc create and kill it after 5ms with SIGKILL | ||
timeout --signal=SIGKILL --kill-after=0 0.05s "$RUNC" --debug ${RUNC_USE_SYSTEMD+--systemd-cgroup} --root "$ROOT/state" create --console-socket "$CONSOLE_SOCKET" test_create_killed || true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe use __runc
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried both runc
and __runc
, but after being wrapped by timeout
, neither achieved the expected results. So, I used $RUNC
to directly call the runc binary and specified --root
, which gave me the desired outcome.
1606d12
to
4b1b1e0
Compare
Signed-off-by: HirazawaUi <695097494plus@gmail.com>
4b1b1e0
to
72e3fac
Compare
This is follow-up to PR #4645, I am taking over from @jianghao65536 to continue addressing this issue.
If the
runc-create
process is terminated due to receiving a SIGKILL signal, it may lead to the runc-init process leaking due to issues like cgroup freezing, and it cannot be cleaned up byrunc delete/stop
because the container lacks astate.json
file. This typically occurs when higher-level container runtimes terminate the runc create process due to context cancellation or timeout.In PR #4645, the Creating state was added to clean up processes in the
STAGE_PARENT/STAGE_CHILD
stage within the cgroup. This PR no longer adds theCreating
state for the following reasons:Although
runc init STAGE_PARENT/STAGE_CHILD
may exist simultaneously whenrunc create
receives SIGKILL signal, after runc create terminates,STAGE_PARENT/STAGE_CHILD
will also terminate due to the termination ofrunc create
:pipenum
to communicate withrunc create
. Whenrunc create
terminates,pipenum
is closed, causingSTAGE_PARENT
to fail when reading/writing topipenum
, triggering bail and termination.STAGE_PARENT
. WhenSTAGE_PARENT
terminates,syncfd
is closed, causingSTAGE_CHILD
to fail when reading/writing tosyncfd
, triggering bail and termination.If the
runc-create
process is terminated during execution, the container may be in one of the following states:SIGKILL
signal during the process of setting the cgroup, the container will be in a paused state. At this point, therunc init
process becomes zombie process and cannot be killed. However,pausedState.destroy
will thaw the cgroup and terminate therunc init
process.STAGE_PARENT
->STAGE_CHILD
phase, the container will be in a stopped state. As described above,STAGE_PARENT/STAGE_CHILD
will terminate due to the termination of runc create, so no processes will be left behind. We only need to clean up the remaining cgroup files, andstoppedState.destroy
will handle this cleanup.Therefore, based on the above reasons, the existing
paused
andstopped
states are sufficient to handle the abnormal termination of runc create due to a SIGKILL signal.