Skip to content

cloud-hypervisor: container with more than one block device fails to start (repeated --disk flags) #988

Description

@CapThunder19

Description

A Linux container on cloud-hypervisor never starts if it has more than one block device.

CloudHypervisor.BuildExecCmd adds a separate --disk flag per block device:
https://github.com/urunc-dev/urunc/blob/main/pkg/unikontainers/hypervisors/cloud_hypervisor.go#L111-L121

But cloud-hypervisor expects all disks after a single --disk. Repeating the flag is a
hard error, not an override:

error: the argument '--disk <disk>...' cannot be used multiple times

So a block rootfs plus one block-backed volume produces two flags and the VM is never
created. Nothing filters the block list per monitor, so N devices always give N flags
(blockRootfs.getBlockDevs() -> monitorResources.BlockArgs -> Linux.MonitorBlockCli()
cloud-hypervisor case).

A single disk works fine, which is probably why this hasn't come up: all four
cloud-hypervisor e2e cases use one disk.

There the two --initramfs appends could never both fire.
Here they always do

QEMU and firecracker aren't affected (qemu concatenates into one string, firecracker
builds a JSON drives array)

System info

  • Urunc version: 3395292 (main)
  • Arch: x86_64, kernel 6.6.87.2-microsoft-standard-WSL2
  • VMM: cloud-hypervisor v50.0.0 (same version pinned in ci.yml)
  • Unikernel: linux

Steps to reproduce

No containerd, docker or devmapper needed. As root:

  1. CGO_ENABLED=1 go build -o /root/urunc ./cmd/urunc, install cloud-hypervisor v50.0

  2. Loop-backed ext4 filesystem, which becomes the second disk:

    truncate -s 64M data.img && mkfs.ext4 -q -F data.img
    mount -o loop data.img /root/uruncdemo/datamnt
    
  3. Bundle with rootfs.img and kernel in its rootfs, plus:

    "mounts": [{"destination":"/data","type":"bind",
                "source":"/root/uruncdemo/datamnt","options":["rbind","rw"]}],
    "annotations": {
      "com.urunc.unikernel.unikernelType": "linux",
      "com.urunc.unikernel.hypervisor": "cloud-hypervisor",
      "com.urunc.unikernel.binary": "/kernel",
      "com.urunc.unikernel.block": "/rootfs.img",
      "com.urunc.unikernel.blkMntPoint": "/"
    }
    
  4. urunc --debug --root /run/urunc create -b <bundle> demoA, then urunc --root /run/urunc start demoA

  5. Repeat with "mounts": [] as a control

I used the block annotation instead of devmapper to get a block rootfs. Both end up in
the same blockRootfs builder and generate the same argv

Logs / output

Two disks:

command="[/usr/local/bin/cloud-hypervisor --memory size=268M --cpus boot=1
 --kernel /kernel --console off --serial tty --seccomp false
 --disk path=/rootfs.img,id=rootfs --disk path=/dev/loop0,id=vol0
 --cmdline panic=-1 console=ttyS0 root=/dev/vda rw ... init=/sbin/init -- ]"

error: the argument '--disk <disk>...' cannot be used multiple times
Usage: cloud-hypervisor [OPTIONS]

Same container with the volume removed, one disk:

Fatal error: VmBoot(VmBoot(KernelLoad(Bzimage(InvalidBzImage))))
  0: Error booting VM
  1: The VM could not boot
  2: Cannot load the kernel into memory
  3: Kernel Loader: failed to load bzImage kernel image

With one disk it clears argument parsing and only stops on my placeholder /kernel
The argv is built and rejected before the kernel is
read, so the placeholder isn't what breaks the two-disk case. Full logs attached.

Possible fix

Collect the specs and emit one flag:

var disks []string
for _, blockArg := range blockArgs { ...; disks = append(disks, diskArg) }
if len(disks) > 0 {
    exArgs = append(exArgs, "--disk")
    exArgs = append(exArgs, disks...)
}

Happy to send a patch if you want it.
There's no cloud_hypervisor_test.go right now, so
I could add one asserting a single --disk for N devices.

Related to #160.

Metadata

Metadata

Assignees

No one assigned

    Labels

    MonitorsMonitors and their executionStoragebugSomething isn't workingenhancementNew feature or request

    Type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions