Virtualization

Virtualization #

Highly-customized QEMU VM with Buildroot #

See buildroot

Boot minimal Alpine VM #

Download Alpine’s netboot images, which provides multiple flavors of vmlinuz and initramfs.

qemu-system-x86_64 -enable-kvm \
    -m 1G -kernel vmlinuz-virt \
    -initrd initramfs-virt \
    -append "console=ttyS0 ip=dhcp alpine_repo=http://dl-cdn.alpinelinux.org/alpine/edge/main/" \
    -nographic -nic user,model=e1000

Install minimal Alpine VM #

Download Alpine’s virt image.

qemu-img create -f qcow2 alpine.qcow2 8G
qemu-system-x86_64 -enable-kvm \
    -m 1G -nographic -nic user,model=e1000
    -boot d -cdrom alpine-virt-3.10.2-x86_64.iso -hda alpine.qcow2

# Run alpine-setup in the VM and reboot
# Boot with SSH port forwarding
qemu-system-x86_64 -enable-kvm \
    -m 4G -smp 4 \
    -hda alpine.qcow2
    -device e1000,netdev=net0 \
    -nographic -netdev user,id=net0,hostfwd=tcp::5557-:22

Vagrant’s Quick Ubuntu #

  1. Install and start libvirtd:
sudo apt install libvirtd
sudo systemctl start libvirtd
  1. Create a file named Vagrantfile:
Vagrant.configure("2") do |config|
	# check here: https://app.vagrantup.com/generic/boxes/ubuntu2004
	config.vm.box = "generic/ubuntu1804"
	config.vm.provider "libvirt" do |v|
		v.memory = 16384  # 16G
		v.cpus = 4
		v.cpu_mode = "host-model"
	end
end
  1. Create the VM:
vagrant up
vagrant ssh

Ubuntu Cloud-init #

Mark Manis’s guide: Setting up a QEMU VM with a custom kernel and Ubuntu

Libguestfs #

Libguestfs has many useful utilities to deal with VM image:

guestmount: mount the root file-system of a guest image read-only:

sudo guestmount -a guest.img -i --ro /mnt 

virt-builder with custom mirror #

virt-builder is useful for quickly building a VM image of arbitrary Linux distribution.

# If you are dropped into initramfs shell, press F10 to enter grub menu
# and replace /dev/vda1 with /dev/sda1.  Then, modify /etc/default/grub,
# and run grub-mkconfig -o /boot/grub.d/grub.cfg
sudo apt install libguestfs-tools

virt-builder debian-10 \
        --no-check-signature --format qcow2 --size 30G \
        --root-password password:rootpass \
        --output debian-10.qcow2

virt-builder fedora-26 \
	--source http://libguestfs.thycat.com/index \
	--no-check-signature --format qcow2 --size 30G \
	--output /vm/images/fedora-26.qcow2


# Boot it
MY_IMG=debian-10.qcow2
qemu-system-x86_64 \
    -enable-kvm -cpu host -smp 4 -m 1G \
    -device virtio-scsi-pci,id=scsi0 -device scsi-hd,drive=hd0 \
    -drive file=$MY_IMG,if=none,format=qcow2,id=hd0 \
    -net user,hostfwd=tcp::8080-:22 \
    -net nic,model=virtio \
    -nographic

Quickly set up Debian:

echo << EOF > /tmp/run-install-debian.sh
#!/bin/sh
ssh-keygen -A
sed -i 's/deb\..*org/free\.nchc\.org\.tw/g' /etc/apt/sources.list
apt update
apt install -y build-essential sudo
useradd -G sudo -m yunchih
echo 'yunchih ALL=(ALL) NOPASSWD: ALL' >> /tmp/yunchih
chown root:root /tmp/yunchih
mkdir -p /etc/sudoers.d/
mv /tmp/yunchih /etc/sudoers.d/
EOF

virt-builder debian-10 \
        --no-check-signature --format qcow2 --size 30G \
        --root-password password:rootpassword \
        --run /tmp/run-install-debian.sh \
        --ssh-inject yunchih:file:yunchih-ssh-key \
        --output debian-10.qcow2
rm /tmp/run-install-debian.sh

List all available distributions: virt-builder --list

Run commands on VM: guestfish #

guestfish -a /vm/images/fedora-26.qcow2
# in the fish shell:
<fs> run    # start the VM
<fs> ! id   # prefix by !: arbitrary commands sent to shell
<fs> ! ls
<fs> list-filesystem

Other patterns: here

Debootstrap #

debootstrap is useful for quickly testing various Debian versions, the following commands create a minimal Ubuntu 16.04 raw image:

dd if=/dev/zero of=ubuntu.img bs=1M count=2000 
mkfs.ext4 ubuntu.img
mount -o loop ubuntu.img /mnt
debootstrap --arch=amd64 xenial /mnt
umount /mnt

For aarch64:

sudo debootstrap --arch=arm64 \
	--variant=minbase --foreign \
	testing /mnt \
	http://ftp.tw.debian.org/debian/

To mount again to add package:

sudo losetup -Pf debian.img
sudo mount /dev/loop0p1 /mnt
sudo debootstrap --arch=arm64 --include=vim --variant=minbase --foreign testing /mnt http://ftp.debian.org/debian/

Boot with QEMU for x86:

dd if=/dev/zero of=debian.img bs=2M count=2000
sudo mkfs.ext2 debian.img
sudo mount debian.img mnt
sudo debootstrap --arch=amd64 stretch mnt http://ftp.tw.debian.org/debian/

# chroot
sudo mount --rbind /dev mnt/dev
sudo mount --make-rslave mnt/dev
sudo mount --rbind /sys mnt/sys
sudo mount --make-rslave mnt/sys
sudo mount -t proc /proc mnt/proc
sudo chroot mnt

# in the chroot
passwd # change root password
apt-get install wget sudo openssh-server # install package is fast
useradd -m -G sudo yunchih

# boot my Linux 4.19 image on Debian stretch, whose default kernel is 4.9
qemu-system-x86_64 -M pc -smp 8 -m 400 -enable-kvm \
    -kernel /tmp/dac_imgs/linux-4.19.91/arch/x86_64/boot/bzImage \
    -drive file=/tmp/debian-img/debian.img,if=virtio,format=raw \
    -append "rootwait rw root=/dev/vda console=tty1 console=ttyS0" \
    -device virtio-net-pci,netdev=n0 \
    -netdev user,id=n0,hostfwd=tcp::5555-:22 \
    -nographic

Rescue a broken image #

A newly-installed kernel might be unbootable. We need to enter the image to delete it

sudo guestmount -a /tmp/mnt/.yunchih/images/for-zns.qcow2   -m /dev/sda1 /mnt
sudo mount -o bind /dev /mnt/dev
sudo mount -o bind /dev/pts /mnt/pts
sudo mount -o bind /proc /mnt/proc
sudo mount -o bind /run /mnt/run
sudo mount -o bind /sys /mnt/sys
sudo chroot /mnt /bin/bash

# inside the chroot
apt-get remove linux-image ...
grub-mkconfig -o /boot/grub/grub.cfg
exit

umount /mnt/run /mnt/sys /mnt/dev/pts
umount /mnt/dev
umount -lf /mnt

Libvirt/QEMU #

Boot an image with KVM enabled (do you have /dev/kvm permission?):

# or /usr/bin/qemu-system-x86_64
/usr/libexec/qemu-kvm -smp 4 \
	-m 4096 -kernel vmlinuz-3.13.0-133-generic \
	-initrd initrd.img-3.13.0-133-generic \
	-drive format=qcow2,file=ubuntu-14.04-server-cloudimg-amd64-disk1.img \
	-append "console=ttyS0 root=/dev/sda" \
	-nographic -display none -serial mon:stdio

View non-graphical console of a Libvirt guest #

Enable the serial terminal in guest:

systemctl enable serial-getty@ttyS0.service
systemctl start serial-getty@ttyS0.service

Add to /etc/default/grub:

GRUB_CMDLINE_LINUX_DEFAULT="console=tty0 console=ttyS0"
GRUB_TERMINAL="serial console"

Make sure the serial ID matches (edit with virsh edit):

<serial type='pty'>
  <target port='0'/>
</serial>
<console type='pty'>
  <target type='serial' port='0'/>
</console>

virt-install script for PXE installation #

  • Assign more than 2G memory to the VM because dracut (initramfs), one of the most critical part of OS bootstrapping, will mount RAM-based filesystem which requires that much space.
  • br0 is the public interface bridge, br1 is the private interface bridge.
  • boot_order=1 tells QEMU to boot on the desired interface, which contains a PXE service in the VLAN.
sudo virt-install \
        --connect=qemu:///system \
        --name [name of your VM] \
        --vcpus=2 \
        --memory=4096 \
        --network bridge=br0,model=virtio,mac=52:83:3d:f1:9b:d9 \
        --network bridge=br1,model=virtio,mac=52:54:00:f2:8c:ae,boot_order=1 \
        --disk path=/imgs/[disk].qcow2 \
        --pxe \
        --os-type=linux \
        --os-variant=rhel7 \
        --graphics=vnc,password=meowmeow,listen=7788 \
        --noautoconsole

Query the os-variant parameter with: osinfo-query os.

Broken network interface #

Check if the bridge is intact:

sudo brctl show
sudo domiflist [guest]

If their output disagrees, add manually:

sudo brctl addif br1 vnet10

Somtimes, restarting the libvirtd.service service also fixes the problem (this will not kill the QEMU processes it manages, thus is almost harmless to your service; it will restart the dnsmasq instance it manages for VM NAT).

Resize small console window #

When you enter a serial console via virsh console, the console size doesn’t expand to real size of your terminal. The resize command, a part of the xterm package, solve this problem:

eval `/usr/bin/resize`

If installing xterm is undesirable, manually adjust the console size:

stty rows $HEIGHT cols $WIDTH
stty rows 56 cols 132

Docker #

SELinux issue when sharing volume #

# With newer docker version
docker run -v /var/db:/var/db:Z rhel7 /bin/sh
# Manual
chcon -Rt svirt_sandbox_file_t /path/to/volume

Keep a running Docker #

This runs the interactive mode in background:

docker run --name ubuntu-1 -dit ubuntu

Run a shell:

docker exec -it ubuntu-1 /bin/bash

Or attach:

docker attach ubuntu-1

And exit with ^P^Q.

Calendar Last modified: February 26, 2022