-
Notifications
You must be signed in to change notification settings - Fork 318
Add cloud-hypervisor VMM backend for Linux hosts #782
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
Open
crosbymichael
wants to merge
1
commit into
apple:main
Choose a base branch
from
crosbymichael:cloud-hypervisor
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+8,587
−238
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,4 +25,4 @@ vmlinux* | |
| # API docs for local preview only. | ||
| _site/ | ||
| _serve/ | ||
|
|
||
| kernel/vmlinuz-x86_64 | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| # CLAUDE.md | ||
|
|
||
| This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. | ||
|
|
||
| ## Build / Test / Format | ||
|
|
||
| The project is built via `make`, not directly with `swift build`. Two Swift packages live in this repo: the root package (Containerization libraries + `cctl` + macOS-only integration binary) and `vminitd/` (the Linux guest init system, cross-compiled with the Static Linux SDK). | ||
|
|
||
| - `make all` — build everything (`containerization` + `vminitd` + `init.ext4` rootfs in `bin/`). Default `BUILD_CONFIGURATION=debug`; pass `release` (or use `make release`) for optimized builds. | ||
| - `make containerization` — build just the host-side Swift package (skips vminitd). | ||
| - `make vminitd` — build vminitd / vmexec only. By default uses `LIBC=musl` via the Static Linux SDK; `make linux-build LIBC=glibc` builds via a Linux dev container. | ||
| - `make test` — unit tests with code coverage. `make coverage` regenerates the coverage report. | ||
| - `make integration` — runs `bin/containerization-integration`. Requires an in-repo kernel under `bin/` (`bin/vmlinux-arm64` on arm64, `bin/vmlinuz-x86_64` or `bin/vmlinux-x86_64` on x86_64); if absent, run `make fetch-default-kernel` to download the Kata-provided kernel for the host arch. | ||
| - Single test: `swift test --filter ContainerizationOCITests.ReferenceTests/testParsing` (Swift Testing / XCTest filter syntax). Targets are listed in `Package.swift`. | ||
| - `make linux-test` — runs `swift test` inside the Linux dev container (requires the `container` CLI from apple/container). | ||
| - `make linux-build` — builds the host-side Swift package (incl. `cctl`, `Containerization`, and `CloudHypervisor`) inside the same Linux dev container. Use this to validate Linux portability of host-side code; the resulting `cctl` is what the cloud-hypervisor backend ships behind. | ||
| - `make linux-integration` — runs the cross-platform integration suite against a real cloud-hypervisor VM inside the dev container (nested virt via apple/container's `--virtualization`). Requires a KVM-capable kernel at `kernel/vmlinux-arm64` (or `kernel/vmlinuz-x86_64` on x86_64 hosts) — build via `make -C kernel`; the kata-fetched kernel doesn't include KVM. Also requires `make fetch-cloud-hypervisor` and `make linux-build` to have been run first. Linux runs only the cross-platform subset (`process true`/`false`/`echo hi`); the macOS suite is unchanged. | ||
| - `make fetch-cloud-hypervisor` — downloads the static `cloud-hypervisor` v52.0 (aarch64) binary into `bin/cloud-hypervisor` for the Linux integration tests. | ||
| - `make build-cloud-hypervisor` / `make build-virtiofsd` — build patched `cloud-hypervisor` / `virtiofsd` from sources you have cloned into `.local/cloud-hypervisor` and `.local/virtiofsd` respectively. There is no fetch target — clone the upstream repos at the revision you want pinned. `build-virtiofsd` applies `scripts/patches/virtiofsd-skip-cap-drop-with-sandbox-none.patch` and is idempotent. Both run inside the same Linux dev container as `linux-integration` so the resulting binaries are aarch64-linux-gnu. | ||
| - `make dist-x86_64` — assembles `bin/containerization-x86_64-<sha>.tar.gz` (cctl + cloud-hypervisor + virtiofsd + initfs.ext4 + kernel) for x86_64 Linux deployment, cross-compiled inside the aarch64 dev container via the Static Linux SDK (Swift) and `cargo zigbuild` (Rust). Prereqs: `.local/cloud-hypervisor` and `.local/virtiofsd` source checkouts (clone deliberately — no fetch target), and an x86_64 kernel built via `make -C kernel TARGET_ARCH=x86_64`. Per-stage rebuild env vars: `REBUILD_VMINITD=1`, `REBUILD_INITFS=1`, `REBUILD_CH=1`, `REBUILD_VIRTIOFSD=1`; cctl x86 always rebuilds. **Full pipeline, toolchain rationale, and troubleshooting in `docs/x86_64-build.md`.** The orchestrator is `scripts/build-dist-x86_64.sh`. | ||
| - `make fmt` — applies `.swift-format` and refreshes license headers via hawkeye. | ||
| - `make check` — formatting + license-header lint (this is what the pre-commit hook runs). Uses `.swift-format-nolint` for stricter linting. | ||
| - `make pre-commit` — installs `scripts/pre-commit.fmt` as a git pre-commit hook. | ||
| - `make protos` — regenerates `Sources/Containerization/SandboxContext/SandboxContext.{pb,grpc}.swift` from the `.proto`. Touch this whenever the proto changes; never hand-edit the generated files. | ||
| - `make cross-prep` — installs Swiftly, the pinned Swift toolchain (see `.swift-version`), and the Static Linux SDK. Run once before the first build. | ||
|
|
||
| `WARNINGS_AS_ERRORS=true` is the default for both packages. Don't disable it casually — CI builds with it on. | ||
|
|
||
| ## Architecture | ||
|
|
||
| This is a **Swift library package** (not a CLI tool) that lets applications run Linux containers on Apple silicon by spawning a lightweight VM per container via `Virtualization.framework`. The corresponding end-user CLI lives in [`apple/container`](https://github.com/apple/container) and is **not** part of this repo. `cctl` here is a playground/example binary, not the shipping product. | ||
|
|
||
| ### The host ↔ guest split | ||
|
|
||
| Every Linux container runs inside its own VM. The boundary between host (macOS) and guest (Linux) is the central architectural fact: | ||
|
|
||
| - **Host side** (`Sources/`, `macOS` platform): orchestrates VMs through `Virtualization.framework` (`VZVirtualMachineInstance.swift`, `VZVirtualMachine+Helpers.swift`). The user-facing entry points are `LinuxContainer` (one container per VM) and `LinuxPod` (multiple containers in one VM, experimental). These build a `VMConfiguration`, boot the VM with the chosen `Kernel` and a rootfs containing `vminitd`, then drive the guest via gRPC. | ||
| - **Guest side** (`vminitd/`, Linux platform): `vminitd` is PID 1 inside the VM. It exposes a gRPC service over **vsock** (default port `1024`) defined by `Sources/Containerization/SandboxContext/SandboxContext.proto`. `VminitdCore` implements that service: launching container processes, handling stdio over vsock, signal/event delivery, cgroups, mounts, and process lifecycle. By default it launches workloads via `vmexec` (a small helper that runs a single process inside the guest namespace); `runc` is used only when an OCI runtime path is supplied. | ||
|
|
||
| The proto is the contract between the two halves. **The `.pb.swift` and `.grpc.swift` files in `SandboxContext/` are generated** — regenerate via `make protos` after changing `SandboxContext.proto`. Both host and guest depend on the same generated Swift via the path-dependency wiring in `vminitd/Package.swift` (`containerization` is a sibling path package). | ||
|
|
||
| ### VMM backends | ||
|
|
||
| `Containerization` abstracts the VMM behind `VirtualMachineManager` / `VirtualMachineInstance`. Two backends ship in this repo, both inside the same `Containerization` target but gated by `#if`: | ||
|
|
||
| - **macOS**: `VZVirtualMachineManager` / `VZVirtualMachineInstance` (`VZ*` files, `#if os(macOS)`). Drives `Virtualization.framework` directly. | ||
| - **Linux**: `CHVirtualMachineManager` / `CHVirtualMachineInstance` (`CH*` files plus `CHProcess`, `VirtiofsdProcess`, `Vsock+Linux`, all `#if os(Linux)`). One `cloud-hypervisor` subprocess per VM, REST-on-UDS control plane via the standalone [`CloudHypervisor`](./Sources/CloudHypervisor) Swift package, virtio-blk / virtio-fs (one `virtiofsd` per share) / TAP / vsock for the data plane. Same `Vminitd` guest contract as VZ — only the host-side VMM differs. | ||
|
|
||
| The `CloudHypervisor` library is a thin NIO-based HTTP/1.1-over-UDS client targeting cloud-hypervisor's REST API. It compiles on both platforms (so it can be unit-tested on macOS without a real cloud-hypervisor binary), but is only consumed by the Linux backend at runtime. | ||
|
|
||
| **Sandbox env vars.** `CHProcess` and `VirtiofsdProcess` default to the upstream-secure spawn flags. Per-component opt-outs: | ||
|
|
||
| - `CONTAINERIZATION_NO_CH_SECCOMP=1` — launch cloud-hypervisor with `--seccomp false`. | ||
| - `CONTAINERIZATION_NO_VIRTIOFSD_SANDBOX=1` — launch virtiofsd with `--sandbox none`. | ||
|
|
||
| Both flags emit a one-line `logger.warning` at start so a relaxed-sandbox VM is loud in the host log. The legacy alias `CONTAINERIZATION_RELAXED_SANDBOX=1` continues to flip both at once. These are required inside apple/container's `--virtualization` dev container, where the host seccomp profile SIGSYS-kills both binaries; `make linux-integration` sets the legacy alias automatically. Leave them unset in production deployments where the host policy lets CH/virtiofsd run unmolested. | ||
|
|
||
| ### Library targets (`Sources/`) | ||
|
|
||
| These are independently consumable Swift modules. Keep their dependencies narrow: | ||
|
|
||
| - `Containerization` — the top-level orchestration layer (`LinuxContainer`, `LinuxPod`, `VMConfiguration`, `Vminitd` gRPC client wrapper, mounts, networking, sockets, image unpacking). Hosts both the macOS (VZ) and Linux (CH) VMM backends behind `#if os(...)`. | ||
| - `CloudHypervisor` — standalone NIO-based HTTP/1.1-over-UDS client targeting cloud-hypervisor's REST API. Cross-platform (compiles on macOS for unit tests; consumed at runtime only by the Linux side of `Containerization`). | ||
| - `ContainerizationOCI` — OCI image spec types, registry client (push/pull/auth), local OCI layout, content store. Used host-side for image management. | ||
| - `ContainerizationEXT4` — pure-Swift ext4 reader/formatter; used to build container rootfs blocks (`bin/initfs.ext4`). | ||
| - `ContainerizationArchive` — Swift wrapper around vendored libarchive headers (`Sources/ContainerizationArchive/CArchive`, refreshable via `make update-libarchive-source`). Links system `libarchive`, `lzma`, `bz2`, `z`, plus zstd via SwiftPM. | ||
| - `ContainerizationNetlink` — netlink socket bindings (used by vminitd for in-guest network configuration). | ||
| - `ContainerizationOS` — POSIX/Darwin/Linux platform shims (`Command`, `Terminal`, `Socket`, signal handling, mount syscalls, keychain). Cross-platform. | ||
| - `ContainerizationIO` — small NIO-flavored stream/reader utilities. | ||
| - `ContainerizationExtras`, `ContainerizationError`, `CShim` — shared helpers and a tiny C bridge. | ||
|
|
||
| `Sources/Integration/` is the macOS-only `containerization-integration` binary (the integration test runner; it is not a `testTarget`, it's an `executableTarget` that's invoked by `make integration`). Unit `testTarget`s live under `Tests/`. | ||
|
|
||
| ### vminitd internals (`vminitd/Sources/`) | ||
|
|
||
| - `VminitdCore/Server+GRPC.swift` is the bulk of the guest agent — it implements every RPC declared in `SandboxContext.proto`. | ||
| - `ManagedContainer.swift` / `ManagedProcess.swift` launch container processes via `vmexec` by default; `VminitdCore/Runc/` plus `RuncProcess.swift` shell out to `runc` only when an OCI runtime path is supplied. `ProcessSupervisor` reaps and dispatches exit events. | ||
| - `Cgroup/` handles cgroup v2 setup. `LCShim/` and `CVersion/` are small C bridges (the latter injects `GIT_COMMIT`/`GIT_TAG`/`BUILD_TIME` at compile time). | ||
| - `vmexec` runs a single container process inside the guest namespace and is what `vminitd` execs to launch container workloads. | ||
|
|
||
| ## Conventions | ||
|
|
||
| - **License headers are required** on every Swift file. `make check-licenses` runs hawkeye against `scripts/license-header.txt`. New files: run `make update-licenses` (or `make fmt`) before committing. | ||
| - **Formatting**: `.swift-format` (line length 180, 4-space indent). The lint config (`.swift-format-nolint`) is what CI enforces. `NeverForceUnwrap`, `NeverUseForceTry`, and `NeverUseImplicitlyUnwrappedOptionals` are all on — don't introduce `!` / `try!`. | ||
| - **Package isolation**: prefer adding code to the smallest applicable module. Don't pull `Containerization` into `ContainerizationOCI` or similar — the leaf modules are intentionally light so they can be consumed standalone. | ||
| - **`SandboxContext.proto` is excluded from the `Containerization` target** (see `Package.swift`). The generated `.pb.swift` / `.grpc.swift` files are checked in. | ||
| - **Squash-and-merge**: PRs land as a single commit, so the PR title/body becomes the commit message — write it accordingly. Commits must be signed (per `CONTRIBUTING.md`). | ||
|
|
||
| ## Requirements | ||
|
|
||
| Apple silicon Mac, macOS 26, Xcode 26. Swift toolchain version is pinned in `.swift-version` (currently `6.3.0`) and installed via Swiftly during `make cross-prep`. Older macOS releases are not supported. | ||
Oops, something went wrong.
Oops, something went wrong.
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.
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.
CLAUDE.mdorAGENTS.md? I don't see anything Claude-specific.