Skip to main content

Installing Ubuntu 20 on a VM with KVM and cloud init

Prerequisites

First thing to do when preparing to install KVM on a new machine is to make sure that the machine supports virtualization. This is typically controlled from BIOS. For instance with my Intel NUC, the following settings need to be turned on or maximized:

Performance >> Processor >> Hyper-Threading: enabled

Performance >> Processor >> Intel Turbo Boost Technology: enabled

Performance >> Processor >> Active Processor Cores: all

Security >> Intel Virtualization Technology: enabled

Security >> Intel VT for Directed I/O (VT-d): enabled

Installation

Fist install QEmu+kvm, which is the emulation for the KVM supervisor.
Run the following bash command to install the command line KVM:

sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virtinst

Next, quickly try it by running the command 
virsh. It should look like this:
$virsh
Welcome to virsh, the virtualization interactive terminal.

Type:  'help' for help with commands
'quit' to quit

virsh #

Typing help will show all the available commands, which is somewhat overwhelming. I am not going to get into the details here, leaving it to the documentation.

Creating a VM

My goal is to show how to create a VM from scratch from command line scripts. I am going to use the Ubuntu cloud image "Focal Fossa" from the official site: https://cloud-images.ubuntu.com
A cloud image requires preparation in order to be used. Why? Because it typically requires some  networking setup, along with provisioning users at the very minimum. Typically the cloud image does not have GUI and once installed on a VM, the only way to work with the VM is by ssh or, if the user is so provisioned, by typing the user name and password.
Ubuntu cloud images are made so that they can be configured using cloud init. For reference the cloud init documentation can be found here.
For the purpose of this demo, let us use the following user date template, which is a yaml file that cloud init understands. We are provisioning a user with an ssh key

#cloud-config
hostname: [your-machine-name-here]
users:
  - default
  - name: [user-name-goes-here]
    sudo: "ALL=(ALL) NOPASSWD:ALL"
    shell: "/bin/bash"
    ssh-authorized-keys:
      - "your public ssh key goes here"

There is a lot more that can be configured with cloud init, not only users. For instance, one can specify how to partition the disk where the OS will run, or to mount some NFS volume, or to execute some commands at boot. Please check the documentation for more information.

By default, if no network config is provided, the VM starts with DHCP enabled and will get an IP address automatically from the local DHCP server. Let us provide a network config template so that we assign a static IP address to this VM.

version: 2
ethernets:
    enp1s0:
        dhcp4: false
        dhcp6: false
        addresses:
          - [your-static-ip-here]/24
        gateway4: 192.168.1.1
        nameservers:
          addresses:
            - 8.8.8.8
            - 8.8.4.4

The above configuration files cannot be used directly with the vm creation command. They need to be added to a disk so that cloud-init finds when when booting the VM. For this, we are going to use the cloud-localds command:

cloud-localds -v --network-config=$NET_CONFIG_FILE $USER_DATA_FILE

The above command creates an iso9660 filesystem with the configuration files. Let us call this the "config disk".

A new VM is created by running the command virt-install. This is a somewhat low level command requiring some fine tuning via command line arguments. 
Here is an example that deploys a VM:

virt-install \
                --name $VM_NAME \
                --memory $VM_MEMORY \
                --vcpus $VM_VCPUS \
                --disk $DISK_IMAGE,device=disk,bus=virtio \
                --disk $CONFIG_DISK,device=cdrom \
                --boot hd \
                --os-type linux \
                --os-variant ubuntu20.04 \
                --virt-type kvm \
                --graphics none \
                --noautoconsole \
                --network bridge=br0

Configuring the cloud image with cloud init can be challenging. Small errors in the config can cause the VM to boot with default config, leaving us puzzled as to why the configuration was not applied. I spent many hours debugging broken configs. How to check if the created VM has the expected config? On real machines, when they boot, one can see on the screen prints from the OS as it boots, followed by the login prompt. Turns out, we can do something like that here as well. All we really need is to remove the argument --noautoconsole from virt-install command.

Comments

Popular posts from this blog

Create VMs with Ansible and KVM on multiple Linux hosts

This is a quick presentation of a simple way to automate creation of VMs on multiple Linux hosts. At the end of this article there is a link to GitHub containing this Ansible project. Prerequisites - few host machines running Linux.  - KVM installed on each host. - an SSH key provisioned on each host. - Ansible installed on the machine that performs the installation, which can be one of the hosts. - The Ansible inventory file containing the hosts to be used in installation. Currently all the hosts under vmhosts will be used. How to create a VM with KVM Creating a VM with KVM is done using a cloud image because it is relatively easy to configure it. At the very minimum, we need to be able to set the VM name, specify some default packages to install, then configure at least one user so we can ssh into that VM. Many distros provide cloud images. Here are some: - Ubuntu - Debian - Fedora The above sites host images for multiple cloud providers and in various different formats. For the pur

Intel NUC dev machine for home

Here is my newest Linux dev machine I put together recently. It is an  Intel NUC BXNUC10I7FNK1 ,  2 x Samsung 32GB DDR4 2666MHz    and a fast Samsung 970 EVO Plus 500GB NVMe M.2 Internal SSD.  I do not need a monitor or a keyboard, since this machine will run headless and I will only SSH into it from my Mac. Took very little time to put it together. Just opened the NUC case, installed the SSD and the memory sticks. I installed Ubuntu server on it. Downloaded the image from ubuntu.com , then created a boot image on a memory stick and used it to install the OS. This new machine is powerful enough to run few VMs, many Docker containers and Kubernetes. And all was about $900 which is quite affordable.