Kubernetes and KubeVirt from Baremetal to Private Cloud

Ahmad Sayed Hassan
13 min readApr 21, 2020

I always believe the best way to understand specific technology, is to try to build myself, One of technologies we used in our daily life is Cloud, almost all of us use it, either we know it or no,so what it takes to build a Cloud?

What we are going to Build

  • A platform to handle both VM, and Containers.
  • Software Defined Networking and Software Defined Storage
  • Provide a single Unified command line to manage Network, Storage, Virtual Machines, and Containers.

To raise the challenge even more

  • I am going to build on my 8GB Personal Laptop
  • I do not have any previous knowledge to the common technology such as VMWARE, OpenStack or Apache Stack
  • Using only my Kubernetes Knowledge.
  • I am going to put in Single Article
  • Using only Opensource documentation from the Project documentation with Zero Coding.
  • Nothing Pre-installed will start with Empty VM or Empty Machine

Sounds Impossible!!!!! will explain in the upcoming section, unless you know KubeVirt you can skip to the implementation steps

If we can start by defining the cloud

Cloud used be defined in very simple way as follow XaaS, X as a Service’

  • IaaS Infrastructure as a Service
  • PaaS Platform as a Service
  • SaaS Software as a Service

Ingredient

  • Few Baremetal machines.
  • Few Networking device.
  • Few Flash Disk.
  • Few Master Minds in networking, storage and virtualization to assemble all of that together.

Lot of product exist as commercial and Opensource to provide this capabilities such OpenStack, ApacheStack, vSphere, … etc

So it is a matter of installing those tool and configuring it right … !!! Unfortunately those tools is either commercially very expensive, or come with its own challenge and learning curves and their own definition.

What it take to build a modern Private Cloud?

One word the SDX family with the Rise of Cloud native more and more SDX come to play but what is SDX it Software Defined X The most commonly known components are

  • SDN : Software Defined Networking
  • SDS : Software Defined Storage

SDN Software Defined Network

Controlling the traffic between your Virtual machines, is no longer configuration in your physical switch or router, with SDN we build some sort of Overlay across all your physical host, and start rebuild the whole network and security policies around it.

SDS Software Defined Storage

Providing a shared storage pool, is by bringing up All Flash appliance, or spinning Desks, The SDS way of doing it is different bring some commodity machines with SSD or NVME and install SDS software on top of them, so you will your Storage As A Service ready to serve ;).

Kubernetes is more than just Container Orchestrator

Kubernetes is the most used platform for container orchestration, a closer look at Kubernetes reveal that is is more than Just Container Orchestrator, as It provides the CXI where X is N or S

  • CNI : Container Network Interface
  • CSI : Container Storage Interface

CNI and CSI in Kubernetes

Which are standard specs and libraries to be adopted by the industry and community to integrate Kubernetes with SDN such as NSX-T, Cisco ACI, Calico, …. .Also CSI which give provide a standardized interface for Storage as well as Software Defined Storage.

Custom Resource Definition

In Addition to standard Object, Kubernetes is extended with custom resource definition which introduce additional custom object, such as VM along side containers.

Building a Private cloud that handle Both Virtual Machines and Containers

Kubernetes to be installed on top Baremetal machine, or Virtual Machine, on top of Hypervisor

Advantage of this approach

The products that implement this design is mature as Opensource(Openstack, Kubernetes, Cloudfoundry, Mesos, Apache Stack, oVirt, ..) , or as commercial (VMware, Redhat Openstack, Redhat Virtualization, …).

Disadvantage

IT IS COMPLEX !!!!!!!!!!!! two different products , different types of APIs, different dashboard Different Highly trained and skilled engineer. Compatibility needs to insure between all different components Two types of Control plan, that consume additional resources. Cross network policies between Virtual Machines and Containers, required additional product such as NSX-T, contrail, … .

Kubevirt when Kubernetes meets LibVirt.

Let us put everything together, CNI, CSI , CRD, which can be used to define and create a complete Virtual Machine not only Containers, or customized light weight VM, It is full VM and can be created, managed by Kubernetes as well as all Networking, Storage, provided by the Kubernetes API.

With that we have a complete Solution that will provide the following IaaS (Virtual Machine, Storage, Networking), PaaS (Kubernetes Operators)

A single Unified API to manage the environments, Additionally the core functionalities Kubernetes itself of managing Containers, which will become more attractive when it managing the networking between containers and VM by the standard Kubernetes configurations.

Re imagining The Architecture with Kubevirt and Kubernetes only,

Advantage of this approach

Simpler at least on the diagram Single control plan Mainly Kubernetes Skills is required Storage and Networking will be handled as Kubernetes resources.

Disadvantage

Still new technology as opensource, or Commercial ( Still Commercial support will be provided by Redhat CNV still in Tech preview as per the time of writing this article UI and Dashboard not available, Redhat CNV will introduce it as part of their OpenShift Console.

Without Further Ado let’s build a Private Cloud based Kubernetes

Ingredients:

  • 8 GB laptop Windows, Mac or Linux
  • Home Switch any brand
  • Hypervisor layer unless you want to run on your machine directly (I am using Windows Hyper-v)

Product lists:

Step 1: Create the VM or If you additional machine skip this.

I am using Hyper-V, and connecting my connection to host network directly Machine Specs I used 4 Cores 4 GB RAM

Step 2: Mount the Host OS iso, I am using Ubuntu 8.10

In Hyper-V disable secure boot, in order to be to install Linux

Step 3: Install the Operating System

Few steps nothing special just follow the default settings all configuration will be done as post installation, the key aspect is to insure that your IP address is taken from your home switch by configuring the machine as host network.

The installer will prompt to restart the machine

Step 4: Install Docker

4.A : Open the machine from the console to get the IP as well as ping any public web site to insure there public internet connectivity

4.B : At this step no longer need to access the machine directly, you can just ssh it, I started it in headless mode, do the usual apt update and install docker Or just curl this ready made script

curl https://raw.githubusercontent.com/ahmadsayed/useful-tools/master/install-docker-ubuntu-1804.sh |  sh -

to check if installation is successful

sudo docker run hello-world

Step 5: Install Kubernetes

Yes, we will use the kubeadm provided from kubernetes documentation (https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/)

5.A Install required Tools and Post configure the OS

Letting iptables see bridged traffic

cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl — system

Installing kubeadm, kubelet and kubectl

Installing kubeadm, kubelet and kubectl

sudo apt-get update && sudo apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

Turning off swap this configuration is not persistent.

sudo swapoff -a

5.B Install Kubernetes

sudo kubeadm init --pod-network-cidr=192.168.0.0/16

If you notice the node will remain Not Ready because we need to install Calico, Kubernets by default does not have capability to assign IP to the pods for example

5.c Install Deploy Calico

No more need for sudo :), we already admin now on kubernetes itself.

kubectl apply -f https://docs.projectcalico.org/v3.11/manifests/calico.yaml
## In my case I have single machine, so I will make master schedulable by removing the master label
kubectl taint nodes --all node-role.kubernetes.io/master-

Step 6: Configure Dynamically provision Software Defined Block Storage

For storage we have many options, which require quiet resources, in our cloud we will pick only block storage, by using Rancher Project Longhorn as it is the easiest to install.

Longhorn can be installed as helm or using kubectl only, following our minimalist approach we are going to use kubectl only

kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml
kubectl edit svc longhorn-frontend -n longhorn-system

change ClusterIP to NodePort and open longhorn front end dashboard of the longhorn-frontend service

step6

Install KubeVirt.

As of now we built a functional Kubernetes Cluster with only opensource technologies, time to give it capabilities to provision a virtual machine

Step 7: Install libvirt on the all host machines, in my case one machine

sudo apt install libvirt-clients
virt-host-validate qemu

expected response in case running on VM, all PASS in case you are running on your machine directly

ahmed@mycloud:~$ virt-host-validate qemu
QEMU: Checking for hardware virtualization : FAIL (Only emulated CPUs are available, performance will be significantly limited)
QEMU: Checking if device /dev/vhost-net exists : PASS
QEMU: Checking if device /dev/net/tun exists : PASS
QEMU: Checking for cgroup 'memory' controller support : PASS
QEMU: Checking for cgroup 'memory' controller mount-point : PASS
QEMU: Checking for cgroup 'cpu' controller support : PASS
QEMU: Checking for cgroup 'cpu' controller mount-point : PASS
QEMU: Checking for cgroup 'cpuacct' controller support : PASS
QEMU: Checking for cgroup 'cpuacct' controller mount-point : PASS
QEMU: Checking for cgroup 'cpuset' controller support : PASS
QEMU: Checking for cgroup 'cpuset' controller mount-point : PASS
QEMU: Checking for cgroup 'devices' controller support : PASS
QEMU: Checking for cgroup 'devices' controller mount-point : PASS
QEMU: Checking for cgroup 'blkio' controller support : PASS
QEMU: Checking for cgroup 'blkio' controller mount-point : PASS
WARN (Unknown if this platform has IOMMU support)

Create kubevirt namespace

kubectl create namespace kubevirt

in case you are using Virtual Machine as Host, you need to apply this configuration

kubectl create configmap -n kubevirt kubevirt-config \
--from-literal debug.useEmulation=true

Step 8: Installing kubevirt

export VERSION=v0.27.0
kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-operator.yaml
kubectl apply -f https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/kubevirt-cr.yaml
kubectl -n kubevirt wait kv kubevirt --for condition=Available

if last statement give timeout ignore it use the following command to monitor kubevirt installation

kubectl get po -n kubevirt

At this stage we have kubevirt ready to serve, detailed installation guide found in here https://kubevirt.io/user-guide/#/installation/installation

Step 9: Install virt client tool

To manage our minimalist cloud we need a single command line like ibmcloud in case of IBM, az in case Azure, aws in case of Amazong, in our case our global command is not surprise kubectl

we will use kubectl to create vm , container, setup the network, create the storage.

but additional extention needed

export VERSION=v0.27.0
wget https://github.com/kubevirt/kubevirt/releases/download/${VERSION}/virtctl-${VERSION}-linux-x86_64

install krew kubectl plugin manager

(
set -x; cd "$(mktemp -d)" &&
curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/krew.{tar.gz,yaml}" &&
tar zxvf krew.tar.gz &&
KREW=./krew-"$(uname | tr '[:upper:]' '[:lower:]')_amd64" &&
"$KREW" install --manifest=krew.yaml --archive=krew.tar.gz &&
"$KREW" update
)

finally install virtctl plugin

export PATH="${PATH}:${HOME}/.krew/bin"
kubectl krew install virt

Creating the first Virtual machine

Time to create a virtual machine, and we will not get simple OS such busybox, or cirrOS, we will bring up a real fedora machine

nano testvm.yaml

Past the following Contents

apiVersion: kubevirt.io/v1alpha3
kind: VirtualMachineInstance
metadata:
name: testvmi-nocloud
spec:
terminationGracePeriodSeconds: 30
domain:
resources:
requests:
memory: 1024M
devices:
disks:
- name: containerdisk
disk:
bus: virtio
- name: emptydisk
disk:
bus: virtio
- disk:
bus: virtio
name: cloudinitdisk
volumes:
- name: containerdisk
containerDisk:
image: kubevirt/fedora-cloud-container-disk-demo:latest
- name: emptydisk
emptyDisk:
capacity: "2Gi"
- name: cloudinitdisk
cloudInitNoCloud:
userData: |-
#cloud-config
password: fedora
chpasswd: { expire: False }

To Access the VM try

kubectl virt console  testvmi-nocloud

Networking Time

Step 11: Accessing the Internet

login the machine via fedora/fedora

try to access internet e.g.

curl https://www.google.com

if you received it depends on the environment, this may happens in case of you are using Windows and Hyper-v, I do not think if you run on baremetal you will face the same issue

[fedora@testvmi-nocloud ~]$ curl https://www.google.com
curl: (6) Could not resolve host: www.google.com

It means the DNS is not configured properly but how we can fix it ??!!!

Looks no far it is all Kubernetes check your coredns configure

kubectl edit cm  coredns -n kube-system

if you received something like that

Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}

The problem in this line

forward . /etc/resolv.conf

replace it with

forward . 8.8.8.8

Explanation: In Cloud native world we need Service Discovery, kubernetes used to implement it via DNS using coredns, which as it sounds a dns the corefile, is configuration which tell kubernetes for none cluster.local fall back to the host dns which is configured in /etc/resolve.conf

For the sake of simplicity we will replace it with the legendary 8.8.8.8

Now Delete the vm and recreate it

kubectl delete -f testvm.yaml
kubectl create -f testvm.yaml

Try to access Google again

curl https://www.google.com

Step 13: VM to Pod Communication

Access the machine via ssh from another pod

Install Pod in order to test the connectivity

kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.5/samples/sleep/sleep.yaml

pod can simply access the vm via its cluster IP

kubectl get vmi #get the Cluster IP of the Virtual Machine
kubectl get po
kubectl exec -ti sleep-8f795f47d-zj7bx -- sh #Replace it with your Pod name
ping 192.168.188.34

Step 14 : Add the VM to service discover a.k.a kubernetes DNS coredns, the sameway we define service to Kubernetes we need to modify the VM yaml file as well as create a service

apiVersion: kubevirt.io/v1alpha3
kind: VirtualMachineInstance
metadata:
name: testvmi-nocloud
labels:
vm: fedora
spec:
terminationGracePeriodSeconds: 30
domain:
resources:
requests:
memory: 1024M
devices:
disks:
- name: containerdisk
disk:
bus: virtio
- name: emptydisk
disk:
bus: virtio
- disk:
bus: virtio
name: cloudinitdisk
volumes:
- name: containerdisk
containerDisk:
image: kubevirt/fedora-cloud-container-disk-demo:latest
- name: emptydisk
emptyDisk:
capacity: "2Gi"
- name: cloudinitdisk
cloudInitNoCloud:
userData: |-
#cloud-config
password: fedora
chpasswd: { expire: False }

Then add Service

apiVersion: v1
kind: Service
metadata:
name: vmservice
spec:
selector:
vm: fedora
clusterIP: None
ports:
- name: port-22 # this is just dns entry no port needed as per documentation
port: 22
targetPort: 22

Try to run the ping within sleep pod

kubectl get po 
kubectl exec -ti sleep-8f795f47d-zj7bx -- sh
ping vmservices

Notice the IP it is the same IP of the VM Machine, We successfully created the DNS for this VM using Kubernetes Service.

The Storage

Step 16: Empty disk the ephemeral storage.

How the storage will be handled recall the longhorn storage solution the current specification uses empty dir as second storage

- name: emptydisk
emptyDisk:
capacity: "2Gi"

So there is 2 Gi Disk Mounted let us explore it via accessing the machine directly

The Previous Demo show creating a disk and add file on it, but this ephemeral device if you delete this VM the disk will be raw again and need to be recreated Instead of using emtpy dir we will longhorn as our persistent volume by modifying the machine specs one more time

Step 17: Using shared storage provided by SDS instead of empty dir container storage.

First Create Persistence Volume

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: longhornpvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
storageClassName: longhorn

The delete the vm and recreate it with the below configuration

kubectl delete -f testvm.yaml
kubectl create -f testvm.yaml
apiVersion: kubevirt.io/v1alpha3
kind: VirtualMachineInstance
metadata:
name: testvmi-nocloud
spec:
terminationGracePeriodSeconds: 30
domain:
resources:
requests:
memory: 1024M
devices:
disks:
- name: containerdisk
disk:
bus: virtio
- name: blockdisk
disk:
bus: virtio
- disk:
bus: virtio
name: cloudinitdisk
volumes:
- name: containerdisk
containerDisk:
image: kubevirt/fedora-cloud-container-disk-demo:latest
- name: blockdisk
persistentVolumeClaim:
claimName: longhornpvc
- name: cloudinitdisk
cloudInitNoCloud:
userData: |-
#cloud-config
password: fedora
chpasswd: { expire: False }

Now time to boot the machine check the disk with lsblk

-bash-4.4$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
vda 252:0 0 4G 0 disk
└─vda1 252:1 0 4G 0 part /
vdb 252:16 0 7.8G 0 disk
vdc 252:32 0 366K 0 disk

Now create ext4 file system, and mount it

sudo mkfs.ext4 /dev/vdb
sudo mkdir /mnt/disk1
sudo mount /dev/vdb /mnt/disk1

Final words

IT Virtualization and Containers, used to be referred as OR relation, should I deploy my workload as VM or as containers, but from Infrastructure this relation always an AND relation, because it still Kubernetes most probably deployed on top of hypervisor and worker nodes is just a virtual machines.

Meanwhile with Kuberntes and Kubevirt, this gap is bridged, by the maturity of Kubevirt, this will be a paradigm shift, in the design for private cloud environments, and automation.

Imagine everything described as kubernetes Yaml file and managed by Kubernetes APIs, with its well established ecosystem, and already many skilled Engineer.

--

--