{"id":882,"date":"2020-03-24T16:35:54","date_gmt":"2020-03-24T23:35:54","guid":{"rendered":"http:\/\/blog.nillsf.com\/?p=882"},"modified":"2020-03-24T16:36:02","modified_gmt":"2020-03-24T23:36:02","slug":"creating-nested-vm-using-kvm-on-azure","status":"publish","type":"post","link":"https:\/\/nillsf.com\/index.php\/2020\/03\/24\/creating-nested-vm-using-kvm-on-azure\/","title":{"rendered":"Creating nested VM using KVM on Azure"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">I am working with a customer that has the use case of running nested virtual machines on Azure. They&#8217;re using KVM and QEMU today, and I wanted to prove out that it is possible to run VMs using KVM and QEMU on Azure.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/www.linux-kvm.org\/page\/Main_Page\">KVM <\/a>is a technology that allows you to run full virtual machines on top of Linux. KVM can access hardware virtualization technologies, meaning Intel VT or AMD-V. Without going in to much depth, Intel VT and AMD-V make it a lot more performant to run virtual machines, by offloading some of the virtualization translation steps to actual hardware.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">KVM is typically used in combination with <a href=\"https:\/\/www.qemu.org\/\">QEMU<\/a>. QEMU is an open-source emulator that enables virtualization. QEMU makes use of KVM to access the Intel VT or AMD-V technologies.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To make working with KVM and QEMU easier, you can use <a href=\"https:\/\/libvirt.org\/\">libvirt<\/a>. Libvirt is a toolkit to manage virtual machines. It interfaces with KVM and QEMU (or other platforms) to create and manage virtual machines. Rather than having to issue commands against the KVM or QEMU API, you typically use libvirt to issue the commands. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To make all of this work in Azure, you&#8217;ll need to use a machine type that supports nested virtualization. The Dv3 and Ev3 support nested virtualization.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this post I&#8217;ll describe how to create a VM using nested virtualization, and how to get access to that VM. Let&#8217;s get started.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting up the VM and installing all the required tools.<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To start, we&#8217;ll need to create a new VM. I decided to go for a D4sv3, and I attached an additional 128GB premium disk to this new VM. I&#8217;ll use this additional disk to store the virtual disk for the target VM.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"557\" height=\"1024\" src=\"\/wp-content\/uploads\/2020\/03\/image-34-557x1024.png\" alt=\"\" class=\"wp-image-885\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-34-557x1024.png 557w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-34-163x300.png 163w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-34-768x1411.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-34.png 826w\" sizes=\"auto, (max-width: 557px) 100vw, 557px\" \/><figcaption>Details of the VM that will turn into a hypervisor<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Once the VM is up and running, SSH into it and run the following commands to install all the required tools:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt-get update\nsudo apt-get install -y qemu qemu-kvm libvirt-bin  bridge-utils  virt-manager\nsudo service libvirtd start\nsudo update-rc.d libvirtd enable<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">To verify that everything is running as expected, you can run the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>service libvirtd status<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Which should show you something similar to:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"403\" src=\"\/wp-content\/uploads\/2020\/03\/image-35-1024x403.png\" alt=\"\" class=\"wp-image-886\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-35-1024x403.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-35-300x118.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-35-768x302.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-35.png 1224w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Libvirt is up and running<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Setting up storage<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To set up storage, we&#8217;ll have to do two tasks:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Mount the data disk<\/li><li>Create a pool in KVM<\/li><\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">To mount the disk, we&#8217;ll have to follow a couple of steps. First let&#8217;s create a partition table on the disk by using <code>fdisk<\/code>. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo fdisk -l #note device name, \/dev\/sdc in my case\nsudo fdisk \/dev\/sdc\n# n to create new partition\n# p to make primary\n# 1 to create first partition\n# default\n# default\n# w to write and quit<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"796\" height=\"490\" src=\"\/wp-content\/uploads\/2020\/03\/image-36.png\" alt=\"\" class=\"wp-image-887\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-36.png 796w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-36-300x185.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-36-768x473.png 768w\" sizes=\"auto, (max-width: 796px) 100vw, 796px\" \/><figcaption>Partitioning the hard drive.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Next we&#8217;ll setup a file system using the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mkfs.ext4 \/dev\/sdc1<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And finally mounting the drive:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mkdir \/vms\nsudo mount \/dev\/sdc1 \/vms<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">To make our mount persist across reboots, we&#8217;ll also add an entry in the file system table (<code>\/etc\/fstab<\/code>)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#first make a backup of \/etc\/fstab\nsudo cp \/etc\/fstab \/etc\/fstab.backup\nsudo bash -c 'echo \"\/dev\/sdc1 \/vms ext4\" >> \/etc\/fstab'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Next up, we have to create the storage pool that KVM\/Qemu will use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo virsh pool-define-as vms-store --type dir --target \/vms\nsudo virsh pool-start vms-store\nsudo virsh pool-autostart vms-store<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Networking<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The default installation created both a DHCP server and a virtual network bridge. To confirm the network bridge was setup, run an <code>ifconfig<\/code>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"771\" height=\"680\" src=\"\/wp-content\/uploads\/2020\/03\/image-37.png\" alt=\"\" class=\"wp-image-888\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-37.png 771w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-37-300x265.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-37-768x677.png 768w\" sizes=\"auto, (max-width: 771px) 100vw, 771px\" \/><figcaption>The installation caused a virtual bridge to be created.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">KVM\/QEMU will use that network interface. To verify that configuration, run the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo virsh net-dumpxml default<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Which will show you something like:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"605\" height=\"359\" src=\"\/wp-content\/uploads\/2020\/03\/image-38.png\" alt=\"\" class=\"wp-image-889\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-38.png 605w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-38-300x178.png 300w\" sizes=\"auto, (max-width: 605px) 100vw, 605px\" \/><figcaption>The network configuration of our virtual network.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll revisit networking later on in this post to enable an external IP for the hosted VM. But for now, we can go ahead and create a first VM.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Creating a nested VM from an ISO<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">With storage and networking, setup, we can go ahead and create a first virtual machine. To start, let&#8217;s download an ISO to install. In my case, I&#8217;ll run Ubuntu 18.04, and I&#8217;ll download the ISO on the temp drive of the VM.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo wget http:\/\/releases.ubuntu.com\/18.04.4\/ubuntu-18.04.4-live-server-am\nd64.iso?_ga=2.17338928.809071564.1585082124-624042363.1585082124 -O \/mnt\/ubuntu.iso<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">With the ISO present, we can go ahead and create a VM. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo virt-install --virt-type=kvm --name=ubuntu-iso \\\n  --ram 1024 --vcpus=1 --virt-type=kvm --hvm \\\n  --cdrom \/mnt\/ubuntu.iso --network network=default \\\n  --disk pool=vms-store,size=20,bus=virtio,format=qcow2 \\\n  --graphics vnc<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This will start the VM creation process. We will need to finish the installation process by connecting to the console. To connect to the console, we&#8217;ll need to get the VNC port that our VM is using, tunnel VNC traffic over SSH, and then run a <a href=\"https:\/\/www.realvnc.com\/en\/connect\/download\/viewer\/\">VNC viewer<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To start, get the VNC port. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo virsh dumpxml ubuntu-iso | grep vnc<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then, open a new SSH connection and tunnel that port of the connection. In my case, I&#8217;m running this on WSL, so I&#8217;ll tunnel over the SSH connection in a new tab. If you&#8217;re using putty, this can also be configured. In WSL use the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh nilfranadmin@52.191.133.169 -L 5900:127.0.0.1:5900<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"452\" height=\"442\" src=\"\/wp-content\/uploads\/2020\/03\/image-39.png\" alt=\"\" class=\"wp-image-890\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-39.png 452w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-39-300x293.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-39-60x60.png 60w\" sizes=\"auto, (max-width: 452px) 100vw, 452px\" \/><figcaption>Set up a tunnel in Putty<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Once the tunnel is up, you need to install <a href=\"https:\/\/www.realvnc.com\/en\/connect\/download\/viewer\/\">VNC viewer<\/a>. Then you open VNC viewer and connect to 127.0.0.1:5900. This will show you the remote connection:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"798\" src=\"\/wp-content\/uploads\/2020\/03\/image-40-1024x798.png\" alt=\"\" class=\"wp-image-891\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-40-1024x798.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-40-300x234.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-40-768x599.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-40.png 1026w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>The Ubuntu installation wizard<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">We will use this terminal to finish creating the VM, and complete the installation.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"798\" src=\"\/wp-content\/uploads\/2020\/03\/image-41-1024x798.png\" alt=\"\" class=\"wp-image-892\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-41-1024x798.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-41-300x234.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-41-768x599.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-41.png 1026w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Installing ubuntu<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Once the installation is complete, you&#8217;ll have to manually start the VM again, using the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo virsh start ubuntu-iso<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, we will connect to the VM again using VNC and install an apache web server.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt-get update\nsudo apt-get install apache2 -y<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In the next step we will direct a public IP address on Azure to the virtual machine. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Mapping an Azure Public IP to the nested VM<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">As a final step, I want to map a public IP in Azure to the VM in the nested virtual machine. I don&#8217;t want to use the VMs primary public IP address, I want to use a different address. This can be achieved by adding a secondary ip configuration to the NIC of the hypervisor.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"337\" src=\"\/wp-content\/uploads\/2020\/03\/image-42-1024x337.png\" alt=\"\" class=\"wp-image-893\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-42-1024x337.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-42-300x99.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-42-768x253.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-42.png 1463w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Adding a secondary ip config with a public IP<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Next, we&#8217;ll want to configure this IP address on the network interface. If you reboot, you don&#8217;t have to execute this command again, because Azure will send the secondary IP via DHCP as well.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ip addr add 172.16.2.5 dev eth0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And finally add a iptables route to forward any traffic coming into our VM on that IP to the IP of the VM. (to get the IP of the VM, do a <code>ifconfig <\/code>in the VM). (in the below, 172.16.2.5 is the secondary IP of my NIC, and 192.168.122.215 is the IP of the nested VM).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo iptables -I FORWARD 1 -o virbr0 -m state -s 0.0.0.0\/0 -d 192.168.122.0\/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT\nsudo iptables -A PREROUTING -t nat -d 172.16.2.5 -p tcp --dport 1:65535 -j DNAT --to-destination 192.168.122.215:1-65535<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When this is complete, you should be able to connect to the public IP on the secondary IP config of the NIC:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"939\" height=\"370\" src=\"\/wp-content\/uploads\/2020\/03\/image-43.png\" alt=\"\" class=\"wp-image-894\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-43.png 939w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-43-300x118.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-43-768x303.png 768w\" sizes=\"auto, (max-width: 939px) 100vw, 939px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And that&#8217;s it for now. We now have a nested VM running using KVM and Qemu.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In this post, we used KVM, Qemu and libvirt to deploy a VM in a VM. We changed the networking configuration so we could expose the VM running in the VM using a public IP. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>I am working with a customer that has the use case of running nested virtual machines on Azure. They&#8217;re using KVM and QEMU today, and I wanted to prove out that it is possible to run VMs using KVM and QEMU on Azure. KVM is a technology that allows you to run full virtual machines [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":889,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,4,5],"tags":[8,97,16],"class_list":["post-882","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure","category-management","category-open-source","tag-azure","tag-nested-virtualization","tag-open-source"],"jetpack_featured_media_url":"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/03\/image-38.png","_links":{"self":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/882","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/comments?post=882"}],"version-history":[{"count":2,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/882\/revisions"}],"predecessor-version":[{"id":895,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/882\/revisions\/895"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/media\/889"}],"wp:attachment":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/media?parent=882"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/categories?post=882"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/tags?post=882"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}