I only see a very small delta halving this (.6 seconds), and the default
will likely stress the CI machines a lot more. This change also swaps
the atomic increments to .relaxed, as there's no ordering required, just
need no torn writes.
Fixes an issue first described in
https://github.com/apple/container/issues/740.
Previously the initial process of the container was using vminitd's
environment variables to find the target executable. This PR updates the
code to use the container's configured environment for the initial
process instead. The behavior of an exec in a container should be the
same as before.
Signed-off-by: Kathryn Baldauf <k_baldauf@apple.com>
This change moves LinuxContainer to use the new AsyncMutex type. This allows us to keep using our old state machine model, but lock around the entirety of operations. This also allows us to axe quite a few states that were intermediary states only there to protect against multiple threads calling (even if it was unlikely..) the same method at the same time (.creating, .starting, .stopping etc.).
The rationale is: Mutex is somewhat tricky to use as you can't (for good reason) do any async work inside the closure. This makes some of our work difficult as every single interaction with the guest is via an rpc, which is all async. There really isn't a worry about contention, because most container methods will only ever be invoked once (create, start, stop) so a big heavy lock isn't much of a deterrent.
Fixes#320
Today we don't actually setup any cgroup limits, as because there's a
1-1 mapping from container<->vm we can just use the VMs resources as the
limit (can't use more than 1GB if that's all the guest sees :) ).
However, if we ever supported > 1 container in the guest it'd be
necessary to actually setup the cg limits. This change just sets a
memory limit and cpu toggles to match whatever was specific for the
container.
We already have `AsyncLock`, but in some cases it'd be nice to have the
type protect a piece of data that you access through the lock, much like
the Synchronization frameworks new `Mutex` type.
This change adds such a type.
- Closes#255.
- Fixes ProxyUtils so that the environment variable to be used for proxy
selection is determined by the request scheme.
- RegistryClient uses ProxyUtils to get the proxy URL used by the
HTTPClient.
- Tweak hostname resolution error message to avoid misleading output if
the proxy hostname cannot be resolved.
Closes#138
This rounds up cgroup and network stats (via netlink) and exposes them
via an agent rpc and on LinuxContainer. While this is not an entirely
accurate view into the full resources being used by the container given
the virtualized nature (doesn't account for vcpu, device etc overhead)
it should give an accurate overview of the workload resource usage in
the guest.
This change besides the stated goal also removes the interfaceStatistics
rpc and just moves these network stats into the new containerStatistics
one. Related to that, it also renames the InterfaceStatistics struct to
NetworkStatistics and moves this into ContainerStatistics as a nested
struct.
Adds support for grabbing stats for an iface. This will be surfaced in a
coming change to get container level statistics from the guest, where
one field will be network stats.
This is not wired up to anything right now, but it will be used for
implementing stats gathering on the host via an rpc in the future. I've
left off the rpc portion as this is already a decent amount to review.
Fixes#296
Today the kernel subdirectory is somewhat useless. The dockerfile we had
tried to use an image that isn't actually on ghcr, so everything would
kind of just crap out. This change swaps things so that we build a local
image that has all the bits needed to compile the kernel, and then uses
that image to build the resulting kernel and plop it in the current
working directory. This change does add a reliance on `container` being
installed however, but for folks that need to build a custom kernel this
doesn't seem like too much of an issue to me.
- Motivation is to be able to add the `swift-backtrace-static` binary as
needed.
- Use singlular `--add-file` and `--label` options since both accept
multiple invocations with single values each.
Example usage:
```bash
./bin/cctl rootfs create \
--vminitd vminitd/bin/vminitd \
--vmexec vminitd/bin/vmexec \
--add-file /Users/john/.swiftpm/swift-sdks/swift-6.2-RELEASE_static-linux-0.0.1.artifactbundle/swift-6.2-RELEASE_static-linux-0.0.1/swift-linux-musl/musl-1.2.5.sdk/aarch64/usr/libexec/swift/linux-static/swift-backtrace-static:sbin/swift-backtrace \
--label org.opencontainers.image.source=https://github.com/apple/containerization \
--image vminit:latest \
bin/init.rootfs.tar.gz
```
Have wait return a struct containing the exit code and the time at which
the process actually exited. This is useful so higher level tools (like
container) don't need to craft a date themselves, and it will be more
accurate as it's taken in the guest directly after the process exits.
swift-nio's public export of NIOFileSystem was removed in 2.86.1:
https://github.com/apple/swift-nio/pull/3370
NIOFileSystem was not yet supposed to be public, but _NIOFileSystem
depended on it as a public import. This made it possible for
`containerization` to see the `NIOFileSystem` package by accident.
Replacing the use of `NIOFileSystem` by `_NIOFileSystem`, as used
elsewhere, fixes the problem.
## Why does CI currently pass?
The change in `swift-nio` does not currently cause `containerization`'s
CI to fail because `Package.resolved` pins `swift-nio` to 2.83.0, before
the change was made. New versions of upstream dependencies will not be
tested until `Package.resolved` is explicitly updated.
When containerization is built as a dependency of a end-user project,
its `Package.resolved` file is ignored. Instead, the dependency
constraints from containerization's Package.swift file are combined with
those of the project and any other library dependencies, so SwiftPM or
Xcode can find a set of mutually compatible packages. This can lead to
new versions of containerization's upstream dependencies being used,
even though those versions have never been tested in CI.
The build failure can be demonstrated by creating a new package which
depends on `containerization` but does not constrain package versions:
```
% swift package init --type executable
Creating executable package: test
Creating Package.swift
Creating Sources
Creating Sources/test/test.swift
% cat > Package.swift <<EOF
heredoc> // swift-tools-version: 6.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "test",
platforms: [
.macOS(.v26),
],
dependencies: [
.package(url: "https://github.com/apple/containerization", from: "0.7.2"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.executableTarget(
name: "test",
dependencies: [
.product(name: "Containerization", package: "containerization"),
]
),
]
)
EOF
% swift build
...
/private/tmp/test/.build/checkouts/containerization/Sources/ContainerizationOCI/Client/RegistryClient+Fetch.swift:25:8: error: no such module 'NIOFileSystem'
23 |
24 | #if os(macOS)
25 | import NIOFileSystem
| `- error: no such module 'NIOFileSystem'
26 | #endif
27 |
```
The project has `kernel-build/` and `kernel/image/` which contain the
same files:
```
$ diff -ru kernel/image/ kernel-build/
# no output
```
I did not find any reference to `kernel-build/` in any workflow or
README or Makefile, so I removed it in favor of `kernel/image/`.
With the changes in this PR, we'll print the Swift version before
building. This mirrors a similar change in container, introduced in
https://github.com/apple/container/pull/550.
The PR updates the toolchain and Static Linux SDK. This resolves the
compilation errors below:
```
Building vminitd and vmexec...
Building for debugging...
<unknown>:0: error: stack nearly exhausted; compilation time may suffer, and crashes due to stack overflow are likely
<module-includes>:1:10: note: in file included from <module-includes>:1:
1 | #include "SwiftMusl.h"
| `- note: in file included from <module-includes>:1:
2 |
/Users/Dmitry/Library/org.swift.swiftpm/swift-sdks/swift-6.2-DEVELOPMENT-SNAPSHOT-2025-06-25-a_static-linux-0.0.1.artifactbundle/swift-6.2-DEVELOPMENT-SNAPSHOT-2025-06-25-a_static-linux-0.0.1/swift-linux-musl/musl-1.2.5.sdk/aarch64/usr/lib/swift_static/linux-static/aarch64/SwiftMusl.h:135:10: error: could not build module 'sys_stropts'
133 | #include <sys/signalfd.h>
134 | #include <sys/statfs.h>
135 | #include <sys/stropts.h>
| `- error: could not build module 'sys_stropts'
136 | #include <sys/swap.h>
137 | #include <sys/syscall.h>
<unknown>:0: error: stack nearly exhausted; compilation time may suffer, and crashes due to stack overflow are likely
<unknown>:0: error: could not build C module 'SwiftMusl'
<unknown>:0: error: stack nearly exhausted; compilation time may suffer, and crashes due to stack overflow are likely
<module-includes>:1:10: note: in file included from <module-includes>:1:
1 | #include "SwiftMusl.h"
| `- note: in file included from <module-includes>:1:
2 |
/Users/Dmitry/Library/org.swift.swiftpm/swift-sdks/swift-6.2-DEVELOPMENT-SNAPSHOT-2025-06-25-a_static-linux-0.0.1.artifactbundle/swift-6.2-DEVELOPMENT-SNAPSHOT-2025-06-25-a_static-linux-0.0.1/swift-linux-musl/musl-1.2.5.sdk/aarch64/usr/lib/swift_static/linux-static/aarch64/SwiftMusl.h:135:10: error: could not build module 'sys_stropts'
133 | #include <sys/signalfd.h>
134 | #include <sys/statfs.h>
135 | #include <sys/stropts.h>
| `- error: could not build module 'sys_stropts'
136 | #include <sys/swap.h>
137 | #include <sys/syscall.h>
<unknown>:0: error: stack nearly exhausted; compilation time may suffer, and crashes due to stack overflow are likely
<unknown>:0: error: could not build C module 'SwiftMusl'
[719/1348] Compiling x_all.cc
make[1]: *** [all] Error 1
make: *** [vminitd] Error 2
```
To update:
- Run `make cross-prep`
- List the installed Static Linux SDKs using `swift sdk list`
- Remove the old Static Linux SDKs using `swift sdk remove <SDK>`
Optionally:
- List the installed toolchains using `swiftly list`
- Uninstalled the old toolchains using `swiftly uninstall <TOOLCHAIN>`
Today, because we don't wait for execvpe to finish to continue onwards,
it's possible that if you did an exec quick enough after starting an
init process for a container, that you could join the init processes
namespaces before pivot_root has taken place which is quite fun. Let's
wait for exec to finish (or an error to occur) to prevent this.
We had a log for the initial oci runtime spec, but we don't log anything
for execed processes. This makes it hard to look into issues that may be
due to just a mistake in the spec and nothing wrong with the actual
logic of the program.
Systemd loves to move the root cgroups processes to one it created named
/init.scope and then write the root cgs subtree_control file. Because of
this we can't just add exec processes to the cg we made for the
container anymore as we'll get EBUSY. We should follow where the init
processes cg is actually at (/proc/pid/cgroup) and add it there.
This refactors the rootfs command to produce an image and have the
ability to create an ext4 formatted block of the init filesystem
directly from the command.
closes#220
Signed-off-by: crosbymichael <michael_crosby@apple.com>