bug: Zombie processes with host runner #911
Labels
No labels
FreeBSD
Kind/Breaking
Kind/Bug
Kind/Chore
Kind/DependencyUpdate
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
Windows
linux-powerpc64le
linux-riscv64
linux-s390x
run-end-to-end-tests
run-forgejo-tests
run-multi-platform-tests
No milestone
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
forgejo/runner#911
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Can you reproduce the bug on the Forgejo test instance?
No
Description
I'm running the official forgejo-runner container. Some actions use a
self-hosted:hostlabel and therefore run commands directly inside the container. When a running action is cancelled due to new commits, the runner stops the currently running action. This produces zombie processes and eventually blocks the runner.It looks like the
forgejo-runnerprocess does not callwaitpid(4)for its grandchildren here.Forgejo Version
11
Runner Version
9
How are you running Forgejo?
Self hosted on a raspberry pi
How are you running the Runner?
Running as a podman container.
Logs
n/a
Workflow file
n/a
The runner does not set a process group where it should, I think. It should use something similar to https://code.forgejo.org/f3/gof3/src/branch/main/util/exec.go and kill the entire process group to ensure there is no leftover. Of course it can't help the case where a sub-process establishes its own process group and/or runs something in the background.
Since there is no way to guarantee that jobs are 100% sure to be cleaned up (such a guarantee requires containers), I'm inclined to say this is not in scope for the runner. However, it certainly deserves to be documented.
Does that make sense? Or do you think there is a way for the runner to provide guarantees to cleanup everything when running jobs directly on the host?
Proposed an update to the documentation at https://codeberg.org/forgejo/docs/pulls/1406
Closing as resolved by https://codeberg.org/forgejo/docs/pulls/1406
That's hillarious, cleaning leftover processes status does not require container technology.
/sbin/initcould do this decades ago. :-)I think, that you didn't get the point. It is not the problem, that the grandchild processes do not terminate. In fact they do and they leave zombie processes lying around. It is forgejo-runners duty to collect their exit status with
waitpid(2)until no result code is returned. Otherwise these zombies clutter the system.If
forgejo-runnershould scan for running processes started by actions and kill them, is a different topic. Although I would vote for it.Please re-open this issue and leave it open until a fix was provided. There are users forging software on *nix system without the fairy dust of container technology.
Please consider the following scenario:
When a job runs out of a host, there is no way for the runner to know that this daemon was spawned by the job and needs cleanup.
@fpedrei wrote in #911 (comment):
This is not what I was referring to. Adding an init daemon to the container would do that, sure. But it is a bandaid rather than a solution.
Jobs that run test may not just leave lingering process, they are also susceptible of removing files from the host and make it unusable. It is also not possible for the runner to ensure isolation on that level when containers are not available.
A personal note on the tone of your message:
It reads like you are mocking my reply and look down on my technical skills. Even if you were right, this is unpleasant to read and I hope we can communicate in a more friendly tone in the future.
I think, that you didn't the point here. Let's examine the situation of the issue reported. This is not about running processes.
After some jobs, the runner looks like this:
As you can see, all processes have been terminated. The
ps(1)man page says:"Processes marked are dead processes (so-called "zombies") that remain because their parent has not destroyed them properly. These processes will be destroyed by init(8) if the parent process exits."
This is worked out in
waitpid(2)man page a little bit:"In the case of a terminated child, performing a wait allows the system to release the resources associated with the child; if a wait is not performed, then the terminated child remains in a "zombie" state [...].
A child that terminates, but has not been waited for becomes a "zombie". The kernel maintains a minimal set of information about the zombie process (PID, termination status, resource usage information) in order to allow the parent to later perform a wait to obtain information about the child. As long as a zombie is not removed from the system via a wait, it will consume a slot in the kernel process table, and if this table fills, it will not be possible to create further processes. If a parent process terminates, then its "zombie" children (if any) are adopted by init(1),"
A look inside
forgejo-runnershows, that the responsible process (with PID 1) for collecting orphaned exit codes isforgejo-runneritself.As you've suggested a possible solution would be a dedicated init process here. This could be something like adding
tinito the container, or starting the container with--init. Another solution would letforgejo-runnercollect outstanding exit codes.I suppose, that you agree, that using host based runners might have security implications. But if you're accepting these and know, that your CI processes do no harm, they should get cleaned up, once they terminate.
@fpedrei wrote in #911 (comment):
Is there a word missing in this sentence?
@fpedrei wrote in #911 (comment):
I agree that it would help solve that particular problem. It would not help for daemons running in the background and other various leftovers that a faulty test can create, but it would take care of that.
Since this is an improvement over the current implementation rather than a bug by which it does not work as intended, please file a feature request.
Good that you want to improve here.
Please keep this issue (orwhatever you call it) open until it is fixed.
I created a feature request at forgejo/forgejo-actions-feature-requests#50, the discussion continues there.