Skip to content

ateworker-microvm: run containers on EROFS layer images over virtio-pmem with DAX - #2610

Open
ajaysundar.k (ajaysundark) wants to merge 6 commits into
agent-substrate:mainfrom
ajaysundark:pr4-microvm-erofs
Open

ajaysundar.k (ajaysundark) wants to merge 6 commits into
agent-substrate:mainfrom
ajaysundark:pr4-microvm-erofs

Conversation

@ajaysundark

@ajaysundark ajaysundar.k (ajaysundark) commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Run micro-VM containers on EROFS layer images mapped over virtio-pmem with DAX.

Motivation

With the host-merged rootfs, each guest copies the image bytes it reads into its own page cache and its memory snapshot, and every open, stat and read is a FUSE round trip. With DAX the guest maps the node's page cache for the layer image directly, so actors on the same layer share those pages and snapshots don't carry them. Kata already carries the dependencies: erofs, virtio_pmem and FS_DAX in the guest kernel.

Fixes #2440

What changed

  • atelet pulls micro-VM images as Form{EROFS: true} (imagecache: build EROFS layers #2609). Cleanup for removing the unused host-merged code from ateworker-microvm is a follow-up.
  • Each distinct layer image is a read-only virtio-pmem device (/dev/pmem1..). Layer devices sit on their own PCI segments from segment 2 up (31 slots / layers) per segment, and the VM creates only the segments it needs. Currently the limit is set as 5 segments, allowing up to 155 layer images per actor across its containers.
  • The kata agent mounts each image erofs ro,dax when it creates the sandbox. The guest mounts the overlay, with the upper and work dirs on the virtio-fs share (<cid>/rw, userxattr, volatile).
  • A checkpoint records each container's layer images and digests in erofs-layers.json and archives upper and work together in rootfs-rw-<cid>.tar. Restore checks the node's layer digests against this record, and accepts a layer pmem device from the snapshot config only if the record names its file and discard_writes is set. MEMORY and ROOTFS snapshots taken before this change cannot be restored.
  • If the actor deleted a directory the host writes into (/etc for resolv.conf), a ROOTFS restore replaces that whiteout with an opaque directory, as overlayfs does for a mkdir over a whiteout.
  • Image volumes still go over virtio-fs; ateworker-microvm: mount image volumes from EROFS layer images in the guest #2826 moves them to layer images as a follow-up, so they can also share layers across guests.

Numbers

SWE-bench astropy (14 layers), one c3-highcpu-192-metal node (COS), suspend to object storage, 15 users, 10 min per run, same tree.

Metric host-merged DAX
Tasks completed 158 374
Resume p50 / p99 (ms) 1,900 / 2,800 1,200 / 2,500
Suspend p50 / p99 (ms) 4,500 / 25,000 1,800 / 7,600
Task compute p50 / p99 (s) 11.0 / 13.0 7.6 / 9.2
Task wall clock p50 / p99 (s) 52 / 83 22 / 31

Verified against swe-perf prow jobs (150 users on the c3-highcpu-metal), for two days against main of the same day.

Metric main this PR
Tasks completed 673 / 605 729 / 745
Resume p50 (s) 2.4 / 2.6 1.6 / 1.3
Snapshot size p50 (MB) 358 167

Inside a guest, reading 568 MB of .so files raised Cached by 579 MB with the host-merged rootfs and by 0 with DAX.

Testing

  • go test -race ./cmd/ateworker-microvm/... ./cmd/atelet/... ./internal/imagecache/... ./internal/ocispec/..., the root-gated imagecache tests under docker run --privileged (7.1 kernel), and hack/verify/{gofmt,go-modules,licenses,boilerplate,golangci-lint}.sh. Each commit builds alone.

  • On a c3 metal node (COS): cold boot, MEMORY suspend and resume, and golden restore with a 14-layer image (segment 2) and a 40-layer image (segments 2 and 3). In the guest, the /dev/pmemN sizes match the VM config's order for all 40 devices, and a file from the 40th layer reads through the overlay. Golden restores (solo, 15 concurrent, and 38 in a 15-user run) took 0.09 to 0.25 s.

  • Tests pass

  • Appropriate changes to documentation are included in the PR (hack/microvm-assets/README.md, docs/api-guide.md)

Comment thread internal/ocispec/microvm.go Resolved
Comment thread internal/ocispec/microvm.go Resolved
@ajaysundark
ajaysundar.k (ajaysundark) marked this pull request as ready for review October 17, 2026 09:56
@ajaysundark
ajaysundar.k (ajaysundark) force-pushed the pr4-microvm-erofs branch 2 times, most recently from 571448c to 7e09937 Compare October 18, 2026 11:41
@bowei Bowei Du (bowei) added area/microVM area/storage kind/feature An enhancement / feature request or implementation labels Oct 19, 2026
@ajaysundark ajaysundar.k (ajaysundark) changed the title ateom-microvm: run containers on EROFS layer images over virtio-pmem with DAX ateworker-microvm: run containers on EROFS layer images over virtio-pmem with DAX Oct 19, 2026
@ajaysundark
ajaysundar.k (ajaysundark) force-pushed the pr4-microvm-erofs branch 3 times, most recently from 6eb4821 to 9f07487 Compare October 23, 2026 02:22
A pure Go EROFS writer and reader, so atelet can build layer images
without mkfs.erofs or privilege. It is pinned to a main commit because
v0.3.1 lacks Link, RemoveAll, Lstat and ReadLink, which the builder
needs for hardlinks and for replacing earlier entries in a layer.
EnsureImageAs takes a Form. Form{EROFS: true} streams the layer tar
through go-erofs into layer.erofs and its sha256, for a guest to mount
over virtio-pmem with DAX (4 KiB blocks, no inline data, 2 MiB aligned).
The image carries whiteouts, ownership and xattrs, so the build needs no
privilege. A layer stays in the form it was first pulled in, and atelet
still asks for Form{} everywhere.
A guest that mounts the layer images itself leaves the host no merged
view to write into before boot. PrepareEROFSUpper resolves paths across
the images with overlayfs lookup rules and creates the directories the
host needs, plus the implicit-parent repairs, in a fresh upper.
SetupBundleRootfs refuses an EROFS bundle.
A node that runs gVisor and micro-VM actors needs a shared layer as both
fs/ and layer.erofs. ensureLayer downloads the blob again and builds the
missing form in the same layer directory, under the same per-layer
flight. Adding fs/ to an EROFS layer rewrites whiteouts.json, since the
image build records only implicit directories.
Let CreateSandboxOpts carry pmem mounts so the agent mounts them when it
creates the sandbox, before any container starts. They use the virtio-fs
storage driver, which mounts Source directly. The block and nvdimm
drivers wait for a hotplug uevent or an ACPI NFIT, and the VM provides
neither for a cold-plugged virtio-pmem device.
Attach each image layer to the guest as a read-only EROFS image on
virtio-pmem with DAX, and let the guest mount the overlay over an upper
on the virtio-fs share. Image pages stay in the node's page cache,
shared by actors and out of guest RAM and the memory snapshot. Layers
get PCI segments of their own, 31 per segment, up to 155 per actor.
Snapshots record the layer digests, and a restore checks them first.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/microVM area/storage kind/feature An enhancement / feature request or implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

serve container image lower layers as EROFS to microVM (DAX) and gVisor

4 participants