info_tfgrid/collections/documentation/developers/internals/zos/manual/zmachine/zmachine.md
2024-04-04 00:47:48 +00:00

14 KiB

Zmachine

A Zmachine is an instance of virtual compute capacity. There are 2 kinds of Zmachines. One is a VM, standard in cloud environments. Next to this it can also be a container. On the Zos level, both of these are implemented as virtual machines. Depending on the context, it will be considered to be either a VM or a container. In either scenario, the Zmachine is started from an Flist.

Note, both VM and Container on ZOS are actually served as Virtual Machines. The only difference is that if you are running in VM mode, you only need to provide a raw disk image (image.raw) in your flist.

Container

A container is meant to host microservice. The microservice architecture generally dictates that each service should be run in it's own container (therefore providing a level of isolation), and communicate with other containers it depends on over the network.

Similar to docker. In Zos, a container is actually also run in a virtualized environment. Similar to containers, some setup is done on behalf of the user. After setup this is done, the users entrypoint is started.

It should be noted that a container has no control over the kernel used to run it, if this is required, a VM should be used instead. Furthermore, a container should ideally only have 1 process running. A container can be a single binary, or a complete filesystem. In general, the first should be preferred, and if you need the latter, it might be an indication that you actually want a VM.

For containers, the network setup will be created for you. Your init process can assume that it will be fully set up (according to the config you provided) by the time it is started. Mountpoints will also be setup for you. The environment variables passed will be available inside the container.

VM

In container mode, zos provide a minimal kernel that is used to run a light weight VM and then run your app from your flist. If you need control over the kernel you can actually still provide it inside the flist as follows:

  • /boot/vmlinuz
  • /boot/initrd.img [optional]

NOTE: the vmlinuz MUST be an EFI kernel (not compressed) if building your own kernel, or you can use the extract-vmlinux script to extract the EFI kernel. To test if your kernel is a valid elf kernel run command readelf -n <path/to/vmlinuz>

Any of those files can be a symlink to another file in the flist.

If ZOS found the /boot/vmlinuz file, it will use this with the initrd.img if also exists. otherwise zos will use the built-in minimal kernel and run in container mode.

Building an ubuntu VM flist

This is a guide to help you build a working VM flist.

This guide is for ubuntu jammy

prepare rootfs

mkdir ubuntu:jammy

bootstrap ubuntu

sudo debootstrap jammy ubuntu:jammy  http://archive.ubuntu.com/ubuntu

this will create and download the basic rootfs for ubuntu jammy in the directory ubuntu:jammy. After its done we can then chroot into this directory to continue installing the necessary packages needed and configure few things.

I am using script called arch-chroot which is available by default on arch but you can also install on ubuntu to continue the following steps

sudo arch-chroot ubuntu:jammy

This script (similar to the chroot command) switch root to that given directory but also takes care of mounting /dev /sys, etc.. for you and clean it up on exit.

Next just remove this link and re-create the file with a valid name to be able to continue

# make sure to set the path correctly
export PATH=/usr/local/sbin/:/usr/local/bin/:/usr/sbin/:/usr/bin/:/sbin:/bin

rm /etc/resolv.conf
echo 'nameserver 1.1.1.1' > /etc/resolv.conf

Install cloud-init

apt-get update
apt-get install cloud-init openssh-server curl
# to make sure we have clean setup
cloud-init clean

Also really important that we install a kernel

apt-get install linux-modules-extra-5.15.0-25-generic

I choose this package because it will also install extra modules for us and a generic kernel

Next make sure that virtiofs is part of the initramfs image

echo 'fs-virtiofs' >> /etc/initramfs-tools/modules
update-initramfs -c -k all

clean up cache

apt-get clean

Last thing we do inside the container before we actually upload the flist is to make sure the kernel is in the correct format

This step does not require that we stay in the chroot so hit ctr+d or type exit

you should be out of the arch-chroot now

curl -O https://raw.githubusercontent.com/torvalds/linux/master/scripts/extract-vmlinux
chmod +x extract-vmlinux

sudo ./extract-vmlinux ubuntu:jammy/boot/vmlinuz | sudo tee ubuntu:jammy/boot/vmlinuz-5.15.0-25-generic.elf > /dev/null
# then replace original kernel
sudo mv ubuntu:jammy/boot/vmlinuz-5.15.0-25-generic.elf ubuntu:jammy/boot/vmlinuz-5.15.0-25-generic

To verify you can do this:

ls -l ubuntu:jammy/boot

and it should show something like

total 101476
-rw-r--r-- 1 root root   260489 Mar 30  2022 config-5.15.0-25-generic
drwxr-xr-x 1 root root       54 Jun 28 15:35 grub
lrwxrwxrwx 1 root root       28 Jun 28 15:35 initrd.img -> initrd.img-5.15.0-25-generic
-rw-r--r-- 1 root root 41392462 Jun 28 15:39 initrd.img-5.15.0-25-generic
lrwxrwxrwx 1 root root       28 Jun 28 15:35 initrd.img.old -> initrd.img-5.15.0-25-generic
-rw------- 1 root root  6246119 Mar 30  2022 System.map-5.15.0-25-generic
lrwxrwxrwx 1 root root       25 Jun 28 15:35 vmlinuz -> vmlinuz-5.15.0-25-generic
-rw-r--r-- 1 root root 55988436 Jun 28 15:50 vmlinuz-5.15.0-25-generic
lrwxrwxrwx 1 root root       25 Jun 28 15:35 vmlinuz.old -> vmlinuz-5.15.0-25-generic

Now package the tar for upload

sudo rm -rf ubuntu:jammy/dev/*
sudo tar -czf ubuntu-jammy.tar.gz -C ubuntu:jammy .

Upload to the hub, and use it to create a Zmachine

VM Image [deprecated]

In a VM image mode, you run your own operating system (for now only linux is supported) The image provided must be

  • EFI bootable
  • Cloud-init enabled.

You can find later in this document how to create your own bootable image.

A VM reservations must also have at least 1 volume, as the boot image will be copied to this volume. The size of the root disk will be the size of this volume.

The image used to the boot the VM must has cloud-init enabled on boot. Cloud-init receive its config over the NoCloud source. This takes care of setting up networking, hostname , root authorized_keys.

This method of building a full VM from a raw image is not recommended and will get phased out in the future. It's better to use either the container method to run containerized Apps. Another option is to run your own kernel from an flist (explained below)

Expected Flist structure

An Zmachine will be considered a VM if it contains an /image.raw file.

/image.raw is used as "boot disk". This /image.raw is copied to the first attached volume of the VM. Cloud-init will take care of resizing the filesystem on the image to take the full disk size allocated in the deployment.

Note if the image.raw size is larger than the allocated disk. the workload for the VM will fail.

Expected Flist structure

Any Flist will boot as a container, UNLESS is has a /image.raw file. There is no need to specify a kernel yourself (it will be provided).

Known issues

  • We need to do proper performance testing for virtio-fs. There seems to be some suboptimal performance right now.
  • It's not currently possible to get container logs.
  • TODO: more testing

Creating VM image

This is a simple tutorial on how to create your own VM image

Note: Please consider checking the official vm images repo on the hub before building your own image. this can save you a lot of time (and network traffic) here https://hub.grid.tf/tf-official-vms

Use one of ubuntu cloud-images

If the ubuntu images in the official repo are not enough, you can simply upload one of the official images as follows

qemu-img convert -p -f qcow2 -O raw bionic-server-cloudimg-amd64.img image.raw
  • now we have the raw image (image.raw) time to compress and upload to the hub
tar -czf ubuntu-18.04-lts.tar.gz image.raw
  • now visit the hub https://hub.grid.tf/ and login or create your own account, then click on upload my file button
  • Select the newly created tar.gz file
  • Now you should be able to use this flist to create Zmachine workloads

Create an image from scratch

This is an advanced scenario and you will require some prior knowledge of how to create local VMs and how to prepare the installation medium, and installing your OS of choice.

Before we continue you need to have some hypervisor that you can use locally. Libvirt/Qemu are good choices. Hence we skip on what you need to do to install and configure your system correctly not how to create the VM

VM Requirements

Create a VM with enough CPU and Memory to handle the installation process note that this does not relate on what your choices for CPU and Memory are going to be for the actual VM running on the grid.

We going to install arch linux image. So we will have to create a VM with

  • Disk of about 2GB (note this also is not related to the final VM running on the grid, on installation the OS image will expand to use the entire allocated disk attached to the VM eventually). The smaller the disk is better this can be different for each OS.
  • Add the arch installation iso or any other installation medium

Boot the VM (locally)

Boot the VM to start installation. The boot must support EFI booting because ZOS only support images with esp partition. So make sure that both your hypervisor and boot/installation medium supports this.

For example in Libvirt Manager make sure you are using the right firmware (UEFI)

Installation

We going to follow the installation manual for Arch linux but with slight tweaks:

  • Make sure VM is booted with UEFI, run efivar -l command see if you get any output. Otherwise the machine is probably booted in BIOS mode.
  • With parted create 2 partitions
    • an esp (boot) partition of 100M
    • a root partition that spans the remaining of the disk
DISK=/dev/vda
# First, create a gpt partition table
parted $DISK mklabel gpt
# Secondly, create the esp partition of 100M
parted $DISK mkpart primary 1 100M
# Mark first part as esp
parted $DISK set 1 esp on
# Use the remaining part as root that takes the remaining
# space on disk
parted $DISK mkpart primary 100M 100%

# To verify everything is correct do
parted $DISK print

# this should 2 partitions the first one is slightly less that 100M and has flags (boot, esp), the second one takes the remaining space

We need to format the partitions as follows:

# this one has to be vfat of size 32 as follows
mkfs.vfat -F 32 /dev/vda1
# This one can be anything based on your preference as long as it's supported by you OS kernel. we going with ext4 in this tutorial
mkfs.ext4 -L cloud-root /dev/vda2

Note the label assigned to the /dev/vda2 (root) partition this can be anything but it's needed to configure the boot later when installing the boot loader. Otherwise you can use the partition UUID.

Next, we need to mount the disks

mount /dev/vda2 /mnt
mkdir /mnt/boot
mount /dev/vda1 /mnt/boot

After disks are mounted as above, we need to start the installation

pacstrap /mnt base linux linux-firmware vim openssh cloud-init cloud-guest-utils

This will install basic arch linux but will also include cloud-init, cloud-guest-utils, openssh, and vim for convenience.

Following the installation guid to generate fstab file

genfstab -U /mnt >> /mnt/etc/fstab

And arch-chroot into /mnt arch-chroot /mnt to continue the setup. please follow all steps in the installation guide to set timezone, and locales as needed.

  • You don't have to set the hostname, this will be setup later on zos when the VM is deployed via cloud-init
  • let's drop the root password all together since login to the VM over ssh will require key authentication only, you can do this by running
passwd -d root

We make sure required services are enabled

systemctl enable sshd
systemctl enable systemd-networkd
systemctl enable systemd-resolved
systemctl enable cloud-init
systemctl enable cloud-final

# make sure we using resolved
rm /etc/resolv.conf
ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

Finally installing the boot loader as follows

Only grub2 has been tested and known to work.

pacman -S grub

Then we need to install grub

grub-install --target=x86_64-efi --efi-directory=esp --removable

Change default values as follows

vim /etc/default/grub

And make sure to change GRUB_CMDLINE_LINUX_DEFAULT as follows

GRUB_CMDLINE_LINUX_DEFAULT="loglevel=3 console=tty console=ttyS0"

Note: we removed the quiet and add the console flags.

Also set the GRUB_TIMEOUT to 0 for a faster boot

GRUB_TIMEOUT=0

Then finally generating the config

grub-mkconfig -o /boot/grub/grub.cfg

Last thing we need to do is clean up

  • pacman cache by running rm -rf /var/cache/pacman/pkg
  • cloud-init state by running cloud-init clean

Click Ctrl+D to exit the change root, then power off by running poweroff command.

NOTE: if you booted the machine again you always need to do cloud-init clean as long as it's not yet deployed on ZOS this to make sure the image has a clean state

Converting the disk

Based on your hypervisor of choice you might need to convert the disk to a raw image same way we did with ubuntu image.

# this is an optional step in case you used a qcoq disk for the installation. If the disk is already `raw` you can skip this
qemu-img convert -p -f qcow2 -O raw /path/to/vm/disk.img image.raw

Compress and tar the image.raw as before, and upload to the hub.

tar -czf arch-linux.tar.gz image.raw