Skip to content
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

intrenal/cri: optimize stdio create procedure #11128

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mingfukuang
Copy link

Continuously creating containers without starting them will result in the containerd process exiting.
Analyzing the exit stack of containerd and discovering:

runtime: program exceeds 10000-thread limit
fatal error: thread exhaustion
...
syscall.Syscall6(0xc0148b8750?, 0x13?, 0x0?, 0x1000?, 0x0?, 0x674d2a23?, 0x14533f40?)
/usr/local/go/src/syscall/syscall_linux.go:91 +0x36 fp=0xc005481e08 sp=0xc005481d80 pc=0x559c1bfb1236
syscall.openat(0x559c1d7bb240?, {0xc0148b8750?, 0xc000000005?}, 0x559c1d23970a?, 0x0)
/usr/local/go/src/syscall/zsyscall_linux_amd64.go:83 +0x94 fp=0xc005481e80 sp=0xc005481e08 pc=0x559c1bfad814
syscall.Open(...)
/usr/local/go/src/syscall/syscall_linux.go:272
os.openFileNolog({0xc0148b8750, 0x13}, 0x0, 0x0)
/usr/local/go/src/os/file_unix.go:245 +0x9b fp=0xc005481ec8 sp=0xc005481e80 pc=0x559c1bfde7fb
os.OpenFile({0xc0148b8750, 0x13}, 0x0, 0x12000000?)
/usr/local/go/src/os/file.go:326 +0x45 fp=0xc005481f00 sp=0xc005481ec8 pc=0x559c1bfdc165
...

And the above number of stacks is:
$ cat containerd.stack.log |grep openFileNolog |wc -l
9954

It could be seen that about 9954 threads are blocked on openFileNolog in the contaienrd process.

When each container is created, the containerd process will create three new threads and open fifos.stdin/fifo.stdout/fifo.stderr. The three threads in the fifos pipeline file will be blocked, and these blocked threads will only return from the Fifos pipeline file is opened after the corresponding shim process of the container is started. If the container remains in a state of creation without being started, the above three threads will be permanently blocked, and each created container will consume three threads. The default number of containerd threads is 10000. In extreme scenarios, creating only the container without starting it will reach the upper limit of containerd threads, ultimately causing containerd threads to exit.

Move the operation of opening the fifos.stdin/fifos.stdout/fifos.stderr for each container to the StartContainer function, so that no new threads are created in CreateContainer function. And these new threads are not constantly blocked in opening fifos.stdin/fifos.stdout/fifos.stderr file process due to the immediate start of the shim process. Thus reducing the consumption of containerd threads in this way. Prevent containerd processes from exiting due to reaching the maximum number of threads.

@k8s-ci-robot
Copy link

Hi @mingfukuang. Thanks for your PR.

I'm waiting for a containerd member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@dosubot dosubot bot added area/cri Container Runtime Interface (CRI) kind/performance labels Dec 10, 2024
@mingfukuang mingfukuang force-pushed the master branch 2 times, most recently from 4abaec7 to 961ef83 Compare December 10, 2024 06:07
When each container is created, the containerd process will create
three new threads and open fifos.stdin/fifo.stdout/fifo.stderr. The
three threads in the fifos pipeline file will be blocked, and these
blocked threads will only return from the fifos pipeline file is
opened after the corresponding shim process of the container is started.
If the container remains in a state of creation without being started,
the above three threads will be permanently blocked, and each created
container will consume three threads. The default number of containerd
threads is 10000. In extreme scenarios, creating only the container
without starting it will reach the upper limit of containerd threads,
ultimately causing containerd threads to exit.

Move the operation of opening the fifos.stdin/fifos.stdout/fifos.stderr
for each container to the StartContainer function, so that no new threads
are created in CreateContainer function. And these new threads are not
constantly blocked in opening fifos.stdin/fifos.stdout/fifos.stderr file
process due to the immediate started of the shim process. Thus reducing
the consumption of containerd threads in this way. Prevent containerd
processes from exiting due to reaching the maximum number of threads.

Signed-off-by: mingfukuang <kuang.mingfu@zte.com.cn>
@ningmingxiao
Copy link
Contributor

cc

@fuweid
Copy link
Member

fuweid commented Dec 30, 2024

Continuously creating containers without starting them will result in the containerd process exiting.

Just curiosity, is it caused in production issue? I don't have context that why we handle IO in creating stage instead of starting. It looks reasonable to change it.

cc @mikebrow @abel-von

Copy link
Member

@mikebrow mikebrow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm.. breaks the following contract:
https://github.com/containerd/containerd/blob/main/internal/cri/store/container/container.go#L42-L44

see state diagram https://github.com/containerd/containerd/blob/main/internal/cri/store/container/status.go#L39

Would need to carefully consider why that was an important must have in the state transition contract.. my memory is fuzzy on it..

@mingfukuang
Copy link
Author

Hmm.. breaks the following contract: https://github.com/containerd/containerd/blob/main/internal/cri/store/container/container.go#L42-L44

see state diagram https://github.com/containerd/containerd/blob/main/internal/cri/store/container/status.go#L39

Would need to carefully consider why that was an important must have in the state transition contract.. my memory is fuzzy on it..

Thank for your response.
But I'm a bit confused, this modification just moves the IO creation process from the create phase to the startup phase for creation. Does it affect the state of the container itself?

@mingfukuang
Copy link
Author

Continuously creating containers without starting them will result in the containerd process exiting.

Just curiosity, is it caused in production issue? I don't have context that why we handle IO in creating stage instead of starting. It looks reasonable to change it.

cc @mikebrow @abel-von

This issue was encountered during the stress testing process.

@fuweid
Copy link
Member

fuweid commented Jan 8, 2025

This issue was encountered during the stress testing process.

Hi @mingfukuang in reality, kubelet always starts created container immediately. It's unlikely to have 5K created containers.
Any blocking syscall could cause go runtime to create new thread. Current design has some limitations, but it's designed for kubelet. So, I would like to keep it as it is.

Wei

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Needs Triage
Development

Successfully merging this pull request may close these issues.

5 participants