Add support for creating the monitor's execution environment with libcontainer - #982
Open
cmainas wants to merge 11 commits into
Open
Add support for creating the monitor's execution environment with libcontainer#982cmainas wants to merge 11 commits into
cmainas wants to merge 11 commits into
Conversation
Since, we are going to use libcontainers, we need the init of runc which intercepts the "normal flow" of starting a GO program from main and continues with the setup of the container process ( after the namespaces have been created). Signed-off-by: Charalampos Mainas <[email protected]>
The monitor process runs in its own rootfs and cannot reach the state dir, so InitialSetup marshals everything it needs into .monitor_spec.json inside that rootfs. - Move MonitorSpec out of types into monitor_spec.go and extend it with everything the urunc monitor process needs to finalize the setup. - Guest rootfs path rewritten to "/": the urunc monitor process has pivoted inside the new rootfs. - Environment is nulled before writing because the monitor inherits it from libcontainer anyway, and persisting host env into the rootfs is exposure with no upside. Signed-off-by: Charalampos Mainas <[email protected]>
Build the monitor's libcontainer config using as a base the contianer's OCI spec. SPecify the root based on the monitor rootfs, append the monitor mounts/devices and create+start the init that "urunc monitor" becomes. - Process argv is "/proc/self/exe monitor <id>": /proc is always mounted post-pivot, so the urunc binary re-execs without being copied into the rootfs. - Seccomp stripped, since urunc applies monitor-specific filters - Allow-all cgroup device rule, no resource limits, just placement for the time being. - Caps = container's set + CAP_NET_ADMIN (tap setup) in all five sets. - Drop Poststart/Poststop from the config, keep them on urunc's execution. - libcontainer state nested under <root>/libcontainer to avoid the state.json collision. - On failure after Create/Start, destroy and reap the init before the cgroup teardown, matching runc's ordering. Signed-off-by: Charalampos Mainas <[email protected]>
The "urunc monitor init" process can't reach the urunc-mode socket, so signal readiness over a FIFO in the state dir. Create wires the write end to the monitor as its single ExtraFile (fd 3 / ReadyPipeFD). Start reads the message. - Create opens O_RDWR so a writer is always present: start blocks instead of seeing a premature EOF, yet still gets EOF if the monitor dies without writing. - Read side opens O_RDONLY|O_NONBLOCK (open never waits on a writer); the read itself blocks via the runtime poller. - One byte: message readyOK(0)=success; any other byte, read error, or EOF=fail. Signed-off-by: Charalampos Mainas <[email protected]>
Split startContainer: in libcontainer mode Load the container and Exec() it (opens the exec fifo, releasing the init blocked since create); otherwise keep the reexec socket handshake. Both paths then wait on AwaitMsg, FIFO- or socket-backed by mode Signed-off-by: Charalampos Mainas <[email protected]>
This is the command that libcontainer's init will execve to inside the monitor's container. Then ExecMonitor loads the spec, sets up the tap, drops to the container user, signals ready, and execve's the monitor (no return on success). - Skip root/XDG_RUNTIME_DIR resolution for this invocation: it never touches urunc's state dir, and the inherited XDG_RUNTIME_DIR names a path absent inside the rootfs, so preparing it would fail the monitor. - Container ID is only for log/ps labelling; everything real comes from the spec file. Env is the one libcontainer set (spec carries none). - signalReady(true) fires only after the exec command is built and closes the pipe so it isn't left open across the execve; failures signal false. Signed-off-by: Charalampos Mainas <[email protected]>
In libcontainer mode Delete() destroys the monitor's libcontainer state + cgroup (Load then Destroy, runc-style) and removes the .monitor_spec.json left in the container rootfs. - destroyLibcontainer split cgo/nocgo: the real implementation imports runc's libcontainer (cgo-only); the shim is built without cgo and never takes this path, so a no-op stub keeps Delete linking. - Failed/absent Load = "nothing to destroy" (create that failed before libcontainer.Create, or an already-completed teardown). - Missing spec file on removal is ignored (os.ErrNotExist). Signed-off-by: Charalampos Mainas <[email protected]>
✅ Deploy Preview for urunc ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
cmainas
force-pushed
the
feat/libcontainer
branch
from
August 18, 2026 09:26
d8a1d6f to
a88174b
Compare
Gather everything related to the guest rootfs (along with the preStart) command in once during InitialSetup and store them in monitorResources. Exec simply loads the struct and uses them. No rootfs decision is made in Exec. Signed-off-by: Charalampos Mainas <[email protected]>
Break the large Exec function and the create function into smaller functions that cna be reused: - split createUnikontainer into newUnikontainer (bundle parse and InitialSetup) and the reexec handshake - make SetupNet a standalone function - pull buildUnikernelCommand and execMonitor out of Exec The rationale is to let the later port of libcontainer to use some of this functionality directly instead of duplicating logic. Signed-off-by: Charalampos Mainas <[email protected]>
Add a configuration option in urunc's configuration to let users choose between libcontianer's and urunc's own implementation for the setup of the monitor's execution environment. THe option is under the [runtime] section of the configuration which should hold generic runtime options. For the time being it is off by default. Signed-off-by: Charalampos Mainas <[email protected]>
cmainas
force-pushed
the
feat/libcontainer
branch
3 times, most recently
from
August 18, 2026 17:38
0f7b035 to
4a09bfd
Compare
Since we currently have 2 versions of urunc, we need to test both urunc native way and the libcontainer one for the setup of the monitor's execution environment setup To do that, repeat the vm_tests and kind test by simply editing the urunc configuration enabling libcontainer. We might want to check how to improve this in the future. Signed-off-by: Charalampos Mainas <[email protected]>
cmainas
force-pushed
the
feat/libcontainer
branch
from
August 19, 2026 10:40
4a09bfd to
9627bfe
Compare
Contributor
Author
|
Some notes / TODOs to have them in our mind, also edited in the PR description:
|
Contributor
Author
|
Some more notes:
|
cmainas
force-pushed
the
feat/libcontainer
branch
2 times, most recently
from
August 19, 2026 12:14
3dddca5 to
abe8288
Compare
Contributor
Author
|
Extra notes:
|
Contributor
Author
|
Let me add some more TODOs:
|
cmainas
force-pushed
the
feat/libcontainer
branch
from
August 19, 2026 12:47
abe8288 to
3d72bea
Compare
cmainas
marked this pull request as ready for review
August 19, 2026 16:08
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds an experimental, opt-in path that uses runc's
libcontainerto setup the monitor's execution environment (namespaces, cgroups, mounts, device nodes, pivot into the monitor rootfs) instead of urunc's native way. It is selected per-runtime via a new config flag and is off by default; the existing reexec/nsenter path is untouched when the flag is off.The workflow:
urunc createbuilds a libcontainer config from the container's OCI spec with re-rooted at the monitor rootfs, carrying themonitor mounts/devices gathered in
InitialSetupand creates, then starts the init that will become the urunc monitor process. It also writes a.monitor_spec.jsoninto the monitor rootfs with everything the urunc monitor process needs to finalize the setup.urunc startloads the container and callsExec()(opens the exec fifo, releasing the init), then waits on the urunc's ready pipe.urunc monitorcli option. ThenExecMonitorreads the spec back, creates the tap device, drops to the container user, signals ready, andexecves the monitor which replaces it and becomes the container PID.urunc deletetears down the monitor's libcontainer state + cgroup and removes the monitor's spec file.There have been some new tests for the new functionality in the CI. The tests simply re-execute the vm_tests and the kind tests after changing the urunc configuration to enable the libcontainer mode.
Things to note:
--sandboxcli option must be set to off, because inside the monitor container the process that spawns virtiofsd does not have the necessary capabilities to create a namespace. Will be addressed in a follow up PR./proc/self/exewill fail due to missing libraries in the case of Firecracker (note, we do not add any host libraries for Firecracker in its execution environment).Related issues
How was this tested?
With e2e tests
LLM usage
Opus 4.8 for various rewrites, unit tests.
Checklist
make lint).make test_ctr,make test_nerdctl,make test_docker,make test_crictl).