VM Operator is an extension to Kubernetes that implements VM management through Kubernetes. It was released officially at end of April 2021 with vCenter Server 7.0 U2a. This is a small feature pushed through a vCenter Server patch that is bringing a huge shift in the paradigm of VM management. It changes the way we are looking at VMs and at the way we are using virtualization. One could argue that Kubernetes already did that. I would say that unifying resource consumption through VMs and pods is actually a huge step forward. VM Operator brings to play not only Infrastructure as Code (IaC), but it also enables GitOps for VMs.
Let's look briefly at the two concepts. IaC represents the capability to define your infrastructure in a human readable language. A lot of tools exist that enable IaC - Puppet, Chef, Ansible, Terraform and so on. They are complex and powerful tools, some of them used in conjunction with others. All these tools have a particularity: they have their own language - Ruby, Python, HCL. GitOps expands the IaC concept. In this case, Git repository is the only source of truth. Manifests (configuration files that describe the resource to be provisioned) are pushed to a Git repository monitored by a continuous deployment (CD) tool that ensures that changes in the repository are applied in the real world. Kubernetes enables GitOps. Kubernetes manifests are written in YAML. With introduction of VM Operator the two concepts can be used in conjunction. For example you could have a GitOps pipeline that deploys the VMs using Kubernetes manifests and then configuration management tools could actually make sure the VMs are customized to suit their purpose - deploying an application server, monitoring agents and so on.
In the current post we will only look at the basics of deploying a VM through VM Operator. Once these concepts are clear then you can add other tools such as Git repositories, CD tools, configuration management.
So, what do we need to be able to provision a VM through VM Operator?
We need vCenter Server updated to U2a and a running Supervisor cluster.
At namespace level a storage policies needs to be configured. It is needed for both VM deployment and persistent volumes
We need a content library uploaded with a supported VMware template (we will follow soon with a post on how to create unsupported VMware templates for VM operator). At the time of writing CentOS 8 and Ubuntu images are distributed through VMware Marketplace (https://marketplace.cloud.vmware.com/ search for "VM service Image")
The images are installed with cloud-init and configured to transport user data using OVF environment variables to cloud-init process which in turn customizes the VM operating system.
In Workload Management, VM Service allows the configuration of additional VM classes and content libraries. VM classes and content library are assigned to the namespace to be able to provision the VMs.
VM classes selected for a particular namespace:
Content library selected for a particular namespace:
Once or the prerequisites are in place, connect to supervisor cluster, and select the namespace you want to deploy the VM
kubectl vsphere login --verbose 5 --server=https://192.168.2.1 --insecure-skip-tls-verify -u cloudadmin@my.lab
kubectl config use-context my-app-namespace
Check that the VM images in the content library are available
kubectl get virtualmachineimages
Create the VM manifest file - centos-db-2.yaml
apiVersion: vmoperator.vmware.com/v1alpha1
kind: VirtualMachine
metadata:
name: centos-db-2
labels:
app: my-app-db
spec:
imageName: centos-stream-8-vmservice-v1alpha1.20210222.8
className: best-effort-xsmall
powerState: poweredOn
storageClass: tanzu-gold
networkInterfaces:
- networkType: nsx-t
vmMetadata:
configMapName: my-app-db-config
transport: OvfEnv
---
apiVersion: v1
kind: ConfigMap
metadata:
name: my-app-db-config
data:
user-data: |
I2Nsb3VkL6CiAgICBlbnMxNjA6CiAgICAgIGRoY3A0OiB0cnVlCg==
hostname: centos-db-2
In the manifest file we've added 2 resources:
- VirtualMachine: where we specify the VM template to use, the VM class. storage policy, network type and also how to send variables to the cloud-init inside the VM (using a config map resource to keep the date in Kubernetes and OVF environment variables to transport it to the VM)
- ConfigMap: contains in our case user data (Base64 encoded user data - this is a SSH key) and the hostname of the VM; Base64 output in this post is trunked
To create the VM, apply the manifest. Then check its state.
kubectl apply -f centos-db-2.yaml
kubectl get virtualmachine
Once the VM has been provisioned, it has been assigned an IP from the POD CIDR
POD CIDRs are private subnets used for inter-pod communication. To access the VM, it needs an Ingress CIDR IP. This is a routable IP and it is implemented in NSX-T as a VIP on the load balancer. The Egress CIDR is used for communication from VM to outside world and it is implemented as SNAT rule. To define an ingress IP, we need to create a virtual machiner service resource of type load balancer:
Create the manifest file - service-ssh-centos-db-2.yaml
apiVersion: vmoperator.vmware.com/v1alpha1
kind: VirtualMachineService
metadata:
name: lb-centos-db-2
spec:
selector:
app: my-app-db
type: LoadBalancer
ports:
- name: ssh
port: 22
protocol: TCP
targetPort: 22
We are using the selector app: my-app-db to match the VM resource for this service. The service will be assigned an IP from Ingress network and it will forward all requests coming to that IP on SSH port to the VM IP on SSH port.
To create the service, apply the manifest. Then check its state.
kubectl apply -f service-ssh-centos-db-2.yaml
kubectl get service lb-centos-db-2
The External IP displayed in the above listing is the ingress IP that you can use now to ssh to the VM:
ssh cloud-user@external_ip
Please note the user used to SSH. From it, you can then sudo and gain root privileges.
A VM provisioned via VM Operator can only be managed through the Supervisor cluster API (Kubernetes API). In this regard, the VM cannot be any longer managed directly from the UI or other management tools. Looking at the picture below you will notice that the VM is marked in UI as "Developer Managed" and that there are no actions that can be taken on the VM
If you are this far, then well done, you've just provisioned you first VM using Kubernetes API. Now put those manifests in a git repo, install and configure a CD tool (such as ArgoCD) to monitor the repo and apply the manifests on the Supervisor cluster and you don't even need to touch kubectl command line or vCenter Server :-)