Guided Exercise: Expose a Virtual Machine Over the Network

We will work with the cirros testvm again:

kubectl apply -f https://kubevirt.io/labs/manifests/vm.yaml


Remember to start the vm after creation:

virtctl start testvm

Create an impromptu web server on the VM

Once it is running, use virtctl to access the VM by ssh. Specify the username “cirros” on the virtctl ssh command:

virtctl ssh testvm --username=cirros


Use “gocubsgo” as the password. The Cirros image is a compact linux host running busybox and no package management, so providing a service from it can be tricky. Here is a simple shell script to create a rudimentary web server:

#!/bin/bash

while true
do
    ( echo "HTTP/1.0 200 Ok"; echo; echo "netcat webserver on testvm port 80" ) | nc -l -p 80
done


Write that to a file on the server and execute it in the background:

vi webserver.sh
sudo sh ./webserver.sh &
exit


Alternatively, the while loop may be run at the $ prompt, and the remaining commands run on a separate terminal.

Expose the web server using virtctl

Expose port 80 on the VM using a NodePort:

virtctl expose vmi testvm --name=testvm-http --port=80 --type=NodePort

Service testvm-http successfully exposed for vmi testvm

Check the service created by the previous virtctl command:

kubectl get service testvm-http

NAME          TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
testvm-http   NodePort   172.30.191.93   <none>        80:31656/TCP     17s

In this example, 31656 is the NodePort that will correspond to the testvm-http Service’s 8080. Knowing the address of a node in the cluster (wkr0) we can construct a curl command to read the webserver’s output:

curl http://wkr0:31656

netcat webserver on testvm port 80

Mix VM and Pod web servers using a Service

Looking at the testvm-http Service's spec.selector field, we see that it is keying on labels found in the VMI:

kubectl get svc testvm-http -o jsonpath='{.spec.selector}' | jq

{
  "kubevirt.io/domain": "testvm",
  "kubevirt.io/size": "small"
}

If we wanted to try migrating the service to a Pod from the VM, we could use a more general label:

kubectl get pods

NAME                         READY   STATUS    RESTARTS   AGE
virt-launcher-testvm-zf9jx   2/2     Running   0          2m

kubectl label pod/virt-launcher-testvm-zf9jx app=http

Now we can create a Pod also serving http on port 80 and label it with the same label:

kubectl run testhttp --image=httpd -l app=http

A quick kubectl get will verify that both the testhttp and virt-launcher pods are labeled as expected:

kubectl get po -l app=http

NAME                         READY   STATUS    RESTARTS   AGE
testhttp                     1/1     Running   0          29s
virt-launcher-testvm-zf9jx   2/2     Running   0          3m

Create a service that will target the app=http label:

kubectl apply -f - <<END
apiVersion: v1
kind: Service
metadata:
  name: http
spec:
  ports:
    - port: 8080
      targetPort: 80
  selector:
    app: http
  type: NodePort
END

Find the NodePort port:

kubectl get svc http
NAME   TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
http   NodePort   172.30.132.28   <none>        8080:32434/TCP   50s

Access the website a few times:

for i in {0..10}; do curl wkr0:32434; done

netcat webserver on testvm port 80
netcat webserver on testvm port 80
<html><body><h1>It works!</h1></body></html>
netcat webserver on testvm port 80
netcat webserver on testvm port 80
netcat webserver on testvm port 80
netcat webserver on testvm port 80
<html><body><h1>It works!</h1></body></html>
netcat webserver on testvm port 80
<html><body><h1>It works!</h1></body></html>
netcat webserver on testvm port 80

As you can see, this gives a mix of the two services.