it’s moving


The most important thing in a data center move, whether it is from OnPrem to/from the Public Cloud and/or from OnPrem to OnPrem, is the data, the applications and their accessibility by internal and external users. One of the interests of a data center move is to take advantage of it to make it more modern, more agile, faster in order to host modern container-based applications next to existing ones.

In this article, I will not cover applications running directly on physical servers, this is a scope I do not usually cover for my customers, and it represents only a small percentage of applications, around less than 10%. On the other hand, I will deal with those running on virtualized environments, which represents about 90% of the applications. The majority of customers have vSphere hypervisors, some may still have some Hyper-V or KVM but it is really very rare.

One of the advantages of server virtualization is the ability to represent a physical machine and everything it contains (operating system, applications, configurations, data, …) in virtual machines (VMs) that are stored in files. Moving these VMs consists in copying these files to the new destination. To access the copied VMs, they need to be connected to a network if possible without having to modify the initial IP addressing plan because it is a tedious and risky work, indeed some applications were not designed to take into account this type of change. To simplify this, it is necessary to virtualize the network and thus be able to reproduce at the destination what there was on the initial site allowing to avoid the change of IP addressing of all the servers and the applications. Note that it is not necessary to virtualize the network at the source only at the destination. Another method would have been to physically replicate the network at the destination, even if this is possible in a move from OnPrem to OnPrem, it is impossible to achieve in a move to the Public Cloud. As you can see, applying virtualization to the network in the same way as it was done for the physical servers allows you to be completely decoupled from the physical infrastructure and to be free to choose your hardware providers and/or hyperscaler.


The first step of the move is to have a virtualized destination environment, at least the hypervisor and the network to receive the VMs, then you have to transfer them, we saw that the VMs were files and that you just had to copy them to the destination. It’s as simple as that, however to have a coherent state of the data, the copy will have to be done cold, i.e. VMs switched off, this will cause an unavailability of access to the applications during the whole copy. Once the copy is complete and the VMs are associated with the right networks, they must be started at the destination. The copies will be long and therefore the unavailability will also be long because there will be a large volume of data. Another method is to use replication tools that will replicate the VMs while they are running, and only once the replication is complete will you stop the VMs at the source, finish replicating the residues and then start the VMs at the destination. Both of these manual methods may be fine for small environments but not if there are hundreds or thousands of VMs, it quickly becomes tedious. To further simplify this process, it is possible to orchestrate everything using a tool such as VMware HCX which I will describe and which helps answer some important questions, how do I do a move if my applications don’t allow me downtime? How do I do it if I want a sequential move? Or how do I prepare everything and do it at a specific time?

VMware HCX offers different types of migration:

  • Bulk Migration: For massive VMs migrations, they will be done in parallel by batches.
  • Mobility Groups: to migrate VMs grouped by theme.
  • vMotion Migration: to move VMs in a unitary manner.
  • OS Assisted Migration: to migrate non-vSphere VMs, i.e. KVM or Hyper-v

In addition to the types of migration, there are interesting network features in the context of migration:

  • Interconnect: Deploys a secure virtual network interconnect between HCX appliances on both sides for added security
  • Wan Optimization: Reduce the amount of data that travels between sites through deduplication and compression mechanisms.
  • Network Extension: Extend the Layer 2 network between sites and keep the same addressing scheme on both sides.
  • Application Path Resiliency: Allows you to multiply the communication paths between sites and no longer use those that have failed.
  • Mobility Optimized Networking (MON): Allows the use of the nearest gateway (i.e. on the same site) for applications that wish to reach a different network.

Other use cases are offered by VMware HCX :


  • Upgrade vSphere versions: migrate VMs hosted on one version of vSphere to a different version.
  • Workload Rebalancing: Move workloads between different Clouds based on resource availability and/or cost seasonality.
  • Business continuity and protection: to protect workloads on another site and benefit from network advantages. It can also be coupled with SRM (Site Recovery Manager).


Let’s go back to the initial topic of moving with other important points of current interest, which are the increasing demands to move to the Public Cloud, these demands lead to these questions, how do I move to a Public Cloud without having to transform my VMs? How do I do it if tomorrow I decide to change to a Public Cloud and avoid being locked in?

To be as independent as possible, you need to have a multi-cloud VM format, i.e. a VM format that is available in OnPrem data centers but also in almost all hyperscalers such as Alibaba Cloud, Amazon Web Services, Google Cloud Platform, Microsoft Azure, Oracle Cloud Infrastructure, OVH, …, and this with a local and/or global geographical presence. This is the case of VMs used by VMware, indeed hyperscalers have decided to offer their customers to use their hypervisor and / or the hypervisor developed by VMware that customers are used to use. This decision is not anodyne because it allows to drastically simplify migrations by eliminating the need for transformation and thus consume their resources more quickly. The largest of them even offer the full VMware SDDC by adding network and storage virtualization coupled with VMware HCX to further accelerate the migration. This gives customers more freedom to choose their cloud provider whether it is OnPrem or Public.


In summary, the most important thing in a move is to have a destination that is the most virtualized as possible (servers, network (including security) and storage). Have an orchestrator that will simplify the migration and choose a VM format that is multi-cloud to avoid being locked in with a hyperscaler.

Add a self-signed certificat on TKG cluster

If you want to deploy pods on a Kubernetes cluster that does not know the certificate of the registry containing the images (this is generally the case for labs with self-signed certificates that are not known by an authority), you risk to not be able to deploy your images, let’s see an example by deploying a Kuard image from my private Harbor registry:

(Warning, WordPress replaces the two dashes with one: cry:)

# kubectl run kuard –image=harbor.cpod-velocity.az-fkd.cloud-garage.net/library/kuard-amd64:blue
# kubectl get pods
kuard    0/1         ImagePullBackOff  0                     7s

# kubectl describe pods kuard
Type Reason Age From Message
—- —— —- —- ——-
Normal Scheduled 33s default-scheduler Successfully assigned default/kuard to test-md-0-5d6756b7fd-b9kwl
Normal Pulling 19s (x2 over 32s) kubelet Pulling image “harbor.cpod-velocity.az-fkd.cloud-garage.net/library/kuard-amd64:blue”
Warning Failed 19s (x2 over 32s) kubelet Failed to pull image “harbor.cpod-velocity.az-fkd.cloud-garage.net/library/kuard-amd64:blue”: rpc error: code = Unknown desc = failed to pull and unpack image “harbor.cpod-velocity.az-fkd.cloud-garage.net/library/kuard-amd64:blue”: failed to resolve reference “harbor.cpod-velocity.az-fkd.cloud-garage.net/library/kuard-amd64:blue”: failed to do request: Head “https://harbor.cpod-velocity.az-fkd.cloud-garage.net/v2/library/kuard-amd64/manifests/blue”: x509: certificate signed by unknown authority
Warning Failed 19s (x2 over 32s) kubelet Error: ErrImagePull
Normal BackOff 4s (x2 over 31s) kubelet Back-off pulling image “harbor.cpod-velocity.az-fkd.cloud-garage.net/library/kuard-amd64:blue”
Warning Failed 4s (x2 over 31s) kubelet Error: ImagePullBackOff

In this example, I tried from my Tanzu Kubernetes Grid (TKG) cluster to deploy the Kuard image which is located on my private Harbor registry hosted on my lab. This self-signed certificate is not recognized by an authority. For this to work anyway, each worker node in my cluster would need to know this certificate. I can copy it to each of them but the principle of TKG is to have a cluster whose life cycle can evolve easily and automatically, add worker nodes, remove them, replace them during the update , …. This would require each time a node is added to include the certificate.

Jesse Hu wrote a tkg-ytt-overlay-additional-ca-certs · GitHub procedure that I tested in TKG 1.4. It works great, it makes sense for those who use Kubernetes every day but may seem complicated to others. I will try to clarify it. This consists of obtaining the certificate, encoded in base64, of executing the commands to have the certificate taken into account by all existing and future Kubernetes nodes.

Encrypt the certificate with base64 and copy it

# base64 -w0 ca.crt (le résultat a volontairement été modifié)


Find the context of your TKG management cluster and the name of the TKG workload cluster on which you will copy the certificate:

# kubectl get-contexts

CURRENT                                     NAME CLUSTER      AUTHINFO                  NAMESPACE
my-cluster-admin@my-cluster                 my-cluster        my-cluster-admin
* test-admin@test                           test              test-admin
tkg-mgmt-vsphere-admin@tkg-mgmt-vsphere     tkg-mgmt-vsphere  tkg-mgmt-vsphere-admin

Edit the configuration of the controller and worker template to add the content of the certificate and the command to take this certificate into account. So each time a node is added, the certificate will be taken into account. The content of the file is to be added in the files: part and the command in the preKubeadmCommands: part as below (I have a configuration based on Photon OS, if you have another OS you must use another command. Copied / pasted does not work very well it is better to type the words again):

To edit the control plane template : kubectl edit KubeadmControlPlane test-control-plane –context tkg-mgmt-vsphere-admin@tkg-mgmt-vsphere

(the syntax just after  content shloud be like thist : – content: <base64 certificate>)


    encoding: base64
    owner: root:root
    permissions: “0644”
    path: /etc/ssl/certs/tkg-custom-ca.pem



  – ! which rehash_ca_certificates.sh 2>/dev/null || rehash_ca_certificates.sh


It is taken into account immediately and a new controller is deployed to replace the old one. (or several depending on the plan chosen at the time of creation). Wait while the new controller is deploying and replaces the old one. The same for the worker: kubectl edit KubeadmConfigTemplate test-md-0 –context tkg-mgmt-vsphere-admin@tkg-mgmt-vsphere. The consideration for workers is not immediate, you have to run the following command:

# kubectl patch machinedeployment test-md-0 –type merge -p “{\”spec\”:{\”template\”:{\”metadata\”:{\”annotations\”:{\”date\”:\”`date +’%s’`\”}}}}}” –context tkg-mgmt-vsphere-admin@tkg-mgmt
patched Wait a bit for the worker nodes to be replaced by new ones then we can retry deploying a new kuard image

# kubectl get node NAME STATUS ROLES AGE VERSION test-control-plane-gsmqz Ready control-plane,master 34m v1.21.2+vmware.1 test-md-0-698857566f-8pvt7 Ready <none> 118s v1.21.2+vmware.1 Now we can redeploy the Kuard image to check that it will run # kubectl run kuard –image=harbor.cpod-velocity.az-fkd.cloud-garage.net/library/kuard-amd64:blue pod/kuard created

# kg pods
kuard 1/1 Running 0 17s

This procedure is valid for taking certificates into account if the cluster is already deployed. if it has not yet been created, it is preferable to have the certificate taken into account from the start using the procedure described in the installation documentation.

Optimizing the cost of using GPUs

GPUs (Graphical Processing Unit) were created in the 70s to speed up the creation and manipulation of images. They were quickly adopted by game console manufacturers to improve the fluidity of graphics. It was in the 2000s that the use of GPUs for computing outside of graphics began. Today the popular use of GPUs concerns the AI (artificial intelligence) and ML (Machine Learning). More history on the Wikipedia site: Graphics processing unit – Wikipedia. This is why these GPUs are found in datacenter servers because IA and ML type applications require many calculations that are executed in parallel, which a conventional processor (CPU) would have difficulty doing this function. Indeed a CPU is the central element of a server which is there to execute lots of small sequential tasks, very fast and at low latency, for that it can count on about sixty cores per CPU (at the date of writing this article). A GPU on the other hand, is made to perform thousands of tasks in parallel thanks to its thousands of cores which compose it. The downside to a GPU is its cost, it can run into tens of thousands of dollars, so you have to make sure it is being used properly all the time. The ideal is to be able to share it so that it can be used simultaneously by several applications in order to be close to consuming all the resources. This is the credo of Bitfusion solution acquired by VMware in 2019 and which is now available as an add-on to vSphere. The GPUs are installed on the hypervisors and form a pool of GPUs which will be accessible by the applications directly hosted on these hypervisors or via the IP network if the applications are hosted on other hypervisors, the applications can even be hosted on physical servers or be based on Kubernetes container. The use is reserved for Artificial Intelligence or Machine Learning type applications using CUDA routines. CUDA was developed by NVDIA to allow direct access to GPUs for non-graphics applications. Thank to Bitfusion, Applications can consume a GPU, several GPUs or just a portion of a GPU (in reality it is the memory of the GPU that is shared). Once consumption is complete, the applications release the allocated GPU resources which then return to the pool for future requests.

From a technical point of view, Bitfusion requires the installation of components on both sides. On the hypervisors side that have GPUs, a virtual appliance must be deployed on each one that can be downloaded from the VMware site. For clients (VM, Bare Metal or Kubernetes containers / PODs) who will consume GPUs resources, a Bitfusion client must be installed, which will allow the interception of CUDA calls made by the application to transfer them to the Bitfusion appliances via the IP network. It is transparent to the application.

Since the exchanges between clients and Bitfusion appliances go through the IP network, it is preferable to have at least a 10 Gb/s network, you generally need 1 10 Gb/s network for 4 GPUs.

Create a LDAPS self-signed certificates for Pinniped

In order to simplify the authentication of Kubernetes clusters operating on different clouds, VMware has developed the Pinniped project accessible in Opensource. Pinniped has been integrated by default into the VMware Tanzu Kubernetes Grid (TKG) offering since version 1.3, replacing the Gangway. inniped allows authentication from OIDC or LDAP sources. In the case of LDAP source, Pinniped does not connect directly to LDAP but currently relies on the Dex component as Gangway already did.

When a user runs a Kubernetes command for the first time or after a certain period of inactivity, they are prompted to authenticate only once with their corporate credentials and can then consume multiple Kubernetes clusters.

I wanted to test this functionality in my lab with an LDAPS / Active Directory server running Windows 2019 and I quickly encountered the eternal problem of certificates not signed by a known authority. So I had to create a certificate that is recognized by the Active Directory server. Searching for hours on the internet, I ended up finding an article by Peter Mescalchin that worked on the first try: Enable LDAP over SSL (LDAPS) for Microsoft Active Directory servers. – bl.ocks.org.

However, when I wanted to use this procedure with Pinniped, it did not work because the SAN (Subject Alternative Name) information was not present in the certificate. By crossing several articles on the subject, I was able to adapt Peter Mescalchin’s solution so that the certificates integrate the SAN information. It gives this:

Creation of the Root certificate

Via OpenSSL (I used a Linux Ubuntu) create a private key (ca.key in my example) to be able to then create the root certificate (ca.crt in my example). The first command will ask you for a password and the second for your organization information.

$ openssl genrsa -aes256 -out ca.key 4096
$ openssl req -new -x509 -days 3650 -key ca.key -out ca.crt


Import the Root certificate on the AD server

From the AD server, type the command certlm or via Control Pannel, type computer certificates in the search bar:

Be careful to choose “Manage computer certificates” and not “Manage user certificates”

Import the previously generated ca.crt in the “Trusted Root Certification Authorities \ Certificates” section


Creation of the Client certificate

Still from the Active Directory server, create a file, in our example it has the name request.inf. In red, I have made changes from the original procedure to add the SAN information. Be careful to put the FQDN of the AD server in CN. The values ​​of _continue_ = “dns” and _continue_ = “ip-address” correspond to the SAN values, the other possible values ​​to reference the AD server.

Signature=”$Windows NT$”

Subject = “CN=ad-server.cpod-velocity.az-fkd.cloud-garage.net
KeySpec = 1
KeyLength = 2048
Exportable = TRUE
MachineKeySet = TRUE
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
ProviderName = “Microsoft RSA SChannel Cryptographic Provider”
ProviderType = 12
RequestType = PKCS10
KeyUsage = 0xa0

OID = ; Server Authentication

; SANs can be included in the Extensions section by using the following text format. Note is the OID for a SAN extension. = “{text}”
_continue_ = “dns=ad-server&”
_continue_ = “dns=ad-server.cpod-velocity.az-fkd.cloud-garage.net&”
_continue_ = “dns=cloud-garage.net&”
_continue_ = “ipaddress=”

Generate the client.csr file with the command below:

c:\> certreq -new request.inf client.csr

From the Linux machine:

Create an extension file, in our example it has the name v3ext.txt. In red, I have made the changes from the initial procedure to add the SAN information under the heading v3_ca which will be referenced in the next order.


# These extensions are added when ‘ca’ signs a request.
[ v3_ca ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = ad-server.cpod-velocity.az-fkd.cloud-garage.net
DNS.2 = ad-server
IP.1 =

Still from the Linux machine, create the client.crt certificate from the files generated in the previous steps ca.crt, ca.key, client.csr and v3ext.txt, in red which has been added compared to the command outcome of the initial procedure

$ openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -extfile v3ext.txt -set_serial 01 -out client.crt -extensions v3_ca


To verify the presence of SAN information

$ openssl x509 -in client.crt -text
X509v3 extensions:
      X509v3 Subject Alternative Name:
          DNS:ad-server.cpod-velocity.az-fkd.cloud-garage.net, DNS:ad-server, IP Address:


Import the Client certificate

From the AD server

C:\> certreq -accept client.crt

The certificate should appear in “Personal \ Certificates”

For the certificate to be taken into account, you must either restart the AD server or force LDAPS to load the certificate with the procedure below:

Still from the AD server, create a text file, in our example it is called ldap-renewservercert.txt with the content below (note the end of the file includes a line with a – (a dash):

changetype: modify
add: renewServerCertificate
renewServerCertificate: 1

Then type the command below:

c:\> ldifde -i -f ldap-renewservercert.txt

To test the taking into account, use the ldp.exe utility by selecting port 636 (or another if specific) and checking the SSL box.

Once all the procedure is done, you have to recover the ca.crt generated in the first step to give it to Pinniped. This can be done either when the TKG management cluster is created or subsequently.

If the management cluster has not yet been created:

$ tanzu management-cluster create –ui
(there are two dashes before the ui argument but WordPress only displays one)

In my test I chose vSphere as the platform, at the identity manager step it will be necessary to copy the certificate in the ROOT CA part

If the TKG management cluster has already been created and you want to update it:

From the Kubernetes context of the manager cluster, encrypt the Root certificate of the AD server with the base64 command and get the result:

$ base64 -w 0 ca.crt

Modify the certificate in the dex configmap by the result of the previous command:

$ kubectl edit configmap -n tanzu-system-auth dex
# Please edit the object below. Lines beginning with a ‘#’ will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
apiVersion: v1
  config.yaml: |
        theme: tkg
        tlsCert: /etc/dex/tls/tls.crt
        tlsKey: /etc/dex/tls/tls.key
        signingKeys: 90m
        idTokens: 5m
        level: info
        format: json
        – id: pinniped-client-id
          name: pinniped-client-id
          secret: 089db7e23b19cb628ba841b17cc32ea4
        – type: ldap
          id: ldap
          name: LDAP
            host: ad-server.cpod-velocity.az-fkd.cloud-garage.net:636
            insecureSkipVerify: false
bindDN: cn=administrator,cn=Users,dc=velocity,dc=local
            bindPW: $BIND_PW_ENV_VAR
            usernamePrompt: LDAP Username

Relaunch the dex pod in the tanzu-system-auth namespace to take the change into account.


Once the management cluster has the right certificate

From there, create a workload cluster:

$ tanzu cluster create my-cluster -f <fichier-environnement>

Import the administration Kubeconfig of the created workload cluster:

$ tanzu cluster kubeconfig get my-cluster –admin
(there are two dashes before the admin argument but WordPress only displays one)

Connect to the workload cluster with the admin context, as admin no need for an account:

$ kubectl use-context my-cluster-admin@my-cluster

Create a cluster role binding with the role that interests you (here cluster-admin) for the desired users, this will allow the user to use this cluster once authenticated:

$ kubectl create clusterrolebinding admin-fbenrejdal  –clusterrole cluster-admin –user fbe@velocity.local
(there are two dashes before the clusterrole and user arguments but WordPress only displays one)

Export the workload cluster kubeconfig, this is the kubeconfig that will need to be passed to users, it has no admin context and will require user authentication. The user will consume this cluster according to the rights defined in clusterrolebinding from the previous step:

$ tanzu cluster kubeconfig get my-cluster –export-file my-cluster-kubeconfig
(there are two dashes before the export argument but WordPress only displays one)

Issue a kubernetes command with the generated kubeconfig file, which will launch the browser for authentication:

$ kubectl get pods -A –kubeconfig my-cluster-kubeconfig
(there are two dashes before the kubeconfig argument but WordPress only displays one)

You should be redirected to a browser with a web page asking for your username and password:

Once entered, you will get the result of your last command:

The result of the previously command should be displayed:

kube-system antrea-agent-q9xpg 2/2 Running 0 7d15h
kube-system antrea-agent-qlmj8 2/2 Running 0 7d15h
kube-system antrea-controller-6bb57bd84-6cj58 1/1 Running 0 7d15h
kube-system coredns-68d49685bd-bjcps 1/1 Running 0 7d15h
kube-system coredns-68d49685bd-vttdw 1/1 Running 0 7d15h
kube-system etcd-my-cluster-control-plane-48n9f 1/1 Running 0 7d15h
kube-system kube-apiserver-my-cluster-control-plane-48n9f 1/1 Running 0 7d15h
kube-system kube-controller-manager-my-cluster-control-plane-48n9f 1/1 Running 0 7d15h
kube-system kube-proxy-dntrc 1/1 Running 0 7d15h
kube-system kube-proxy-k5m9g 1/1 Running 0 7d15h
kube-system kube-scheduler-my-cluster-control-plane-48n9f 1/1 Running 0 7d15h
kube-system kube-vip-my-cluster-control-plane-48n9f 1/1 Running 0 7d15h
kube-system metrics-server-66cb4fb659-xlprc 1/1 Running 0 7d15h
kube-system vsphere-cloud-controller-manager-vmfwl 1/1 Running 1 7d15h
kube-system vsphere-csi-controller-bd8b6cc8c-8ljl8 6/6 Running 0 7d15h
kube-system vsphere-csi-node-6xqf5 3/3 Running 0 7d15h
kube-system vsphere-csi-node-vmbmq 3/3 Running 0 7d15h
pinniped-concierge pinniped-concierge-dcd587f97-lk9n5 1/1 Running 0 7d15h
pinniped-concierge pinniped-concierge-dcd587f97-zrnb7 1/1 Running 0 7d15h
pinniped-concierge pinniped-concierge-kube-cert-agent-8a8e3e38 1/1 Running 0 7d15h
pinniped-supervisor pinniped-post-deploy-job-4ldt7 0/1 Completed 0 7d15h
pinniped-supervisor pinniped-post-deploy-job-m74gz 0/1 Error 0 7d15h
tkg-system kapp-controller-69c4d4bbb4-kwk5l 1/1 Running 0 7d15h

Customize an Ubuntu VM with Cloud-Init on vSphere

Cloud-Init seems to be the favorite customization tool for major OSs aiming to be installed on different cloud environments (AWS, Azure, GCP, vSphere, …), it is very powerful, heterogeneous but at the beginning, it is difficult to understand how it works.

Personalization information are stored in a file called user-data. This file is transmitted to Cloud-Init using a mechanism specific to each cloud. In our case, it is the VMtools that will transmit the user-data file, once received, Cloud-Init will execute it.

I wasted a tremendous amount of time finding the minimum steps to customize Ubuntu OS with Cloud-Init in a vSphere environment. I was looking for the possibility of customizing the OS when cloning from an OVF template.

Below is the procedure I use for an Ubuntu 20.04.2 LTS, freshly installed and after its first reboot. I kept the default values ​​except for the French keyboard and the installation of the OpenServer SSH option.

Cloud-Init must be told to retrieve the user-data customization file via the OVF parameters of the VM, there is a step to be done on the VM side and an OS side.

OS side:

  • Delete the file that sets the Datasource to none instead of OVF:
    • sudo rm /etc/cloud/cloud.cfg.d/99-installer.cfg
  • If you want the network configuration to be done, delete the file that disables it:
    • sudo rm /etc/cloud/cloud.cfg.d/subiquity-disable-cloudinit-networking.cfg
  • If you are in DHCP, the VM will always retrieve the same IP because it will keep the same machine ID. To avoid this, you must reset the identity of the OS:
    • sudo truncate -s 0 /etc/machine-id

VM side:

  • Get the contents of your cloud-init user-data file and encoded it with base64:
    • Base64 user-data.yaml
  • From the vSphere client, when the VM is stopped, activate the properties of the OVF :
    • Select the VM => Configure => vApp Options => vApp Options are disabled … Edit => Click on “Enable vApp options”
    • In the OVF details tab => => check the VMware Tools case
  • From the vSphere client, when the VM is stopped, add the user-data field in the properties of the OVF:
    • => Select the VM => Configure => vApp Options => Properties => ADD enter the Label and the Key id with the user-data value.
  • From the vSphere client, when the VM is stopped, add the value to the user-data field in the properties of the OVF:

    • Select the VM => Configure => vApp Options => Properties => SET VALUE, a popup appear and set it with the base64 key comming from the user-data file retrieved in the step early

Now from this VM, directly make a clone to make another VM or to make a template. If you want to change the user-data file, when deploying the VM, change only the base64 key to the new key

Deploy VM in and via Kubernetes

Applications are often made up of Kubernetes PODs and VMs. The most common example that we find is, a database in the form of a VM and the rest of the application in the form of PODs. By reflex, rightly or wrongly, what requires data persistence is put in the form of VMs.

The vSphere with Tanzu platform is also a platform that allows simultaneous and native hosting of Kubernetes PODs and VMs.

Until now, VMs and PODs were deployed using different methods and connected to different networks, which could cause developers to delay development environment provisioning and the risk of connection failures. Indeed, developers had to ask the team that manages the infrastructure, the deployment of a VM with an expression of need.

To reduce the time impact and the risk of errors, the infrastructure teams have implemented automation tools via a ticketing system or via a self-service portal to give a certain autonomy. Deployment is much simpler but it is not yet sufficient because it involves the developer learning and using additional tools and retrieving the details of connections to the deployed VM. The self-service portal is not obsolete though, it has many other values ​​such as governance management, I hope I will have the opportunity to write an article on it for details.


Diagram showing a developer who clicks on his portal to deploy a VM that will be connected to a network.
This same developer uses the Kubernetes kubectl command to deploy their PODs. Kubernetes uses its own network.


Since vSphere 7U2a it is now possible to provision VMs in the same way as one deploys PODs, using the Kubernetes kubectl command. To be more precise, since the beginning of vSphere with Tanzu (originally it was called Project Pacific) it was possible to deploy Virtual Machines from Kubernetes, they were however reserved for internal Kubernetes use as for creation by Tanzu Kubernetes Cluster.

Now the developer can also deploy his own virtual machines, they will also be connected to the same network as the pods. The waste of time and the risk of error are thus eliminated. I did the test on my demo environment which is shared with my other colleagues, it takes less than 3 minutes to have a freshly installed MongoDB database from a completely virgin Linux Ubuntu.


Diagram showing a developer who uses both the Kubernetes kubectl command to deploy their PODs and VMs.
Everything will be connected to the same Kubernetes network.
What are the perimeters of each persona?
There are two, the resource provider and the consumer. The resource provider is the infrastructure administrator who will present the resources to be consumed and, if necessary, cap them. The consumer is the developer who will use these resources through Kubernetes to develop their application.
The person of the infrastructure with his usual tool (vSphere client), creates a namespace of resources, grants access rights to the developer, defines the classes of service (number of CPU, amount of RAM) to which the developer will have the right to use and the VM image library that he will be able to use.
The developer connects via his account to the Namespace provided and thus creates his YAML files in order to define his resource needs for his virtual machine (s) and if they wish, he can customize it or them in order to install his tools and the services he needs.
In summary, vSphere with Tanzu leaves the choice to the developer to have its application components developed and hosted on PODs or on VMs using the same tool, the same network and the same platform. This saves time for deployment, development and offer more agility.
If you want to lift the hood, I invite you to read this article: Steps for creating VM through Kubectl

Steps for creating VM through Kubectl


To create a virtual machine with vSphere with Tanzu via the kubectl command, there are steps to follow for the administrator and for the developer, they are very simple but that did not prevent me from wasting a little time on the customization side regarding the OS part.

I recommend this article to you to understand the interest of deploying VMs through Kubernetes: Deploy VM in and via Kubernetes. My colleague’s blog: Introducing Virtual Machine Provisioning, via Kubernetes with VM service | VMware is also very well detailed.

In the last part of this article, I will provide some details on the Content Library part and on the YAML part. But first, let’s review the parts to be done on the administrator side and the developer side.



Regarding the administrator

The first step is to download the VMs images that are different from those used for TKC (Tanzu Kubernetes Cluster aka Guest Cluster). The images are available in the VMware marketplace, at the time of writing this article there are 2 (Ubuntu and Centos), the current Ubuntu version does not allow the use of persistent volume (PVC) because it is based on a virtual hardware version 10 and at least version 12 is required, this problem will be soon corrected.

You have to go to the marketplace and do a search with the keyword “vm service”, this allows you to filter (a little) the compatible images => VMware Marketplace.

Then click on the desired image, connect with your MyVMware account.

You have two options, download it then upload it to a local content library

or retrieve the subscription url to create a content library that will synchronize with the one hosted by VMware.

Once the image is loaded or the link is filled in, you should have a content library like this:

Still from the vSphere interface, we must now create a namespace, grant the rights to the users so that they can connect to it, assign the VM class, the content library and the storage class, which should give this

The example above shows once the namespace has been created, how to assign a VM class, a content library, authorize the developers who can consume this namespace, which storage class to use and finally if necessary cap the resources, CPU, memory and storage.

That’s all there is to it on the infrastructure administrator side.

Regarding the developer

You need a YAML description for:

  • The configmap which contains the customization of the VM
  • The creation of the VM
  • The Network service if you want to connect to it from an outside network (optional) PVC if you want to use persistent volumes (optional)

Via the Kubernetes command, the developer connects with his account to the Namespace provided, he will be able to list the classes of services that he can use as well as the images that he can deploy.

kubectl get virtualmachineclass
NAME                  CPU   MEMORY   AGE
best-effort-2xlarge   8     64Gi     22d
best-effort-4xlarge   16    128Gi    22d
best-effort-8xlarge   32    128Gi    22d
best-effort-large     4     16Gi     22d
best-effort-medium    2     8Gi      31d
best-effort-small     2     4Gi      31d
best-effort-xlarge    4     32Gi     22d
best-effort-xsmall    2     2Gi      22d
guaranteed-xsmall     2     2Gi      22d


kubectl get virtualmachineimage
NAME                                                         VERSION                           OSTYPE                FORMAT   AGE

centos-stream-8-vmservice-v1alpha1-1619529007339                                               centos8_64Guest       ovf      4h8m
ob-15957779-photon-3-k8s-v1.16.8—vmware.1-tkg.3.60d2ffd    v1.16.8+vmware.1-tkg.3.60d2ffd    vmwarePhoton64Guest   ovf      2d19h
ob-16466772-photon-3-k8s-v1.17.7—vmware.1-tkg.1.154236c    v1.17.7+vmware.1-tkg.1.154236c    vmwarePhoton64Guest   ovf      2d19h
ob-16545581-photon-3-k8s-v1.16.12—vmware.1-tkg.1.da7afe7   v1.16.12+vmware.1-tkg.1.da7afe7   vmwarePhoton64Guest   ovf      2d19h
ubuntu-20-1621373774638                                                                        ubuntu64Guest         ovf      4h8m

He can thus create his YAML descriptive files in order to define his resource requirements for his virtual machine (s) and if they wish, he can customize it or them in order to install his tools.

The configmap descriptive file, includes the customization of the VM. The 3 important fields to fill in for personalization are:

  • The hostname which contains the OS hostname
  • The public-keys, which contains the public key of a computer from which we will connect to the OS in ssh.
  • The user-data part is, if you wish, the place where we put the contents of the Cloud Init configuration file, it will have to be encrypted with the base64 command

cat loeil-du-se-vm-configmap.yaml
apiVersion: v1
kind: ConfigMap
    name: loeil-du-se-vm-configmap # The name of the ConfigMap, must be the same as in the VirtualMachine
    namespace: loeil-du-se
  # OVF Keys values required by the VM at provision time
  hostname: loeil-du-se
  public-keys: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDC4Cclh3rN/l70lBNlwQyK6ZtugxqT/7HeerHZKPSO0hcl5ZWLvz2+7QG5FqvYbkPP6EomvyDhE2MPnQ0kWaIrumVxYXAbVdpBBKKdTI3xJpewWB2syxgVOXP2ZOrw4cRLFv18rnESGHfsohedyaSB1qvubPWAqBFa+PSS4xh3zKalUknwc7Bs14fci8tEwEg8cpvNsqvrPZliJ6qTYFGqKuG6Ct+y449JNW6k6itTepgSYvUdJfjBTxk5tDzBdWz28km5N7lxgUB0rIWgSDl1XLCBrmm+H6bkHtD59MxAuxwLjih4tS4PzspcVjwWiJhd0HH7u2wbsPLCrrAX7am4EP40zphu9IR+fVxk+2jp7eD2uXPS6p9sDPEWHl6wGclI7pnfuoyvcn+CIwCtMweLuUw5MPj2eIIXcBhqUffeVAXVHrx8+e7+yHvqfyhqm2J9Ay3yt3zvAcXW0VqDxfvnfmv8sc9VNUW+8fUeyoo4b4uZRLLSf2DHM8= root@fbenrejdal-z01 # the public key to be able to do ssh without password from my laptop
  user-data: | # optional, enter the base64 cloud init encodded file, the result key could be a one line key or a multiple lines key. watch out the indentation, the line should be start under the “r” of user-data

The base64 is obtained as follows:

base64  loeil-du-se-vm-cloud-init.yaml

Its content in clear:

cat  loeil-du-se-vm-cloud-init.yaml

# WATCHOUT the first line must start with #cloud-config
  – devops
  – default # Create the default user for the OS
  – name: fbe
  ssh-authorized-keys: # the public key of my laptop, it could also be filled in the OVF property
    – ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDC4Cclh3rN/l70lBNlwQyK6ZtugxqTG7HeerHZKPSO0hcl5ZWLvz2+7QG5FqvYbkPP6EomvyDhE2MPnQ0kWaIrumVxYXAbVdpBBKKdTI3xJpewWB2syxgVOXP2ZOrw4cRLFv18rnESGHf+sohedyaSB1qvubPWAqBFa+PSS4xh6D3zKalUknwc7Bs14fci8tEwEg8cpvNsqvrPZliJ6qTYFGqKuG6Ct+y449JNW6k6itTepgSYvUdJfjBTxk5tDzBdWz28km5N7lxgUB0rIWgSDl1XLCBrmm+H6bkHtD59MxAuxwLjih4tS4PzspcVjwWiJhd0HH7u2wbsPLCrrAX7am4EP40zphu9IR+fVxk+2jp7eD2uXPS6p9sDPEWHl6wGclI7pnfuoyvcn+CIwCtMweLuUw5MPj2eIIXcBhqUffeVAXVHrx8+e7+yHvqfyhqm2J9Ay3yt3zvAcXW0VqDxfvnfmv8sc9VNUW+8fUeyoo4b4uZRLLSf2DHM8= root@fbenrejdal-z01
      groups: sudo, devops
    shell: /bin/bash
    passwd: VMware1!
    sudo: [‘ALL=(ALL) NOPASSWD:ALL’] # the user fbe will not need to enter password when using sudo
  ssh_pwauth: true
  list: |
    fbe:VMware1! # in case you want to change password of users
    expire: false  # if you don’t want your password to expire
runcmd: # Example of runcmd to install MongoDB. Cloud Init has also APT keyword to do installation
  – echo “deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse” | tee /etc/apt/sources.list.d/mongodb-org-4.4.list
  – wget -qO – https://www.mongodb.org/static/pgp/server-4.4.asc | apt-key add –
  – echo “deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse” | tee /etc/apt/sources.list.d/mongodb-org-4.4.list
  – apt-get update
  – apt-get install -y mongodb-org
  – echo “mongodb-org hold” | dpkg –set-selections
  – echo “mongodb-org-server hold” | dpkg –set-selections
  – echo “mongodb-org-shell hold” | dpkg –set-selections
  – echo “mongodb-org-mongos hold” | dpkg –set-selections
  – echo “mongodb-org-tools hold” | dpkg –set-selections
  – sed -i ‘s/’ /etc/mongod.conf
  – ufw allow from any to any port 27017 proto tcp
  – sleep 2
  – systemctl start mongod

Very very important, the file must absolutely start with # cloud-config and nothing else. It’s still a classic Cloud Init file. If you’re not too familiar with Cloud Init, I’ve put some comments there to make it a bit more readable.

The VM description file

cat loeil-du-se-vm-deployment.yaml
apiVersion: vmoperator.vmware.com/v1alpha1

kind: VirtualMachine
  name: loeil-du-se-vm
  namespace: loeil-du-se
  vm: loeil-du-se-vm
  imageName: ubuntu-20-1621373774638 #the image must exist on the content library and must be listed with the command kubectl get virtualmachineimage
  className: best-effort-xsmall
  powerState: poweredOn
  storageClass: silver-storage-policy
  – networkType: nsx-t #must be nsx-t or vsphere-distributed depending on your install.
    # networkName: if vsphere-distributed you must specify the name of the network portgroup
    configMapName: loeil-du-se-vm-configmap # The K8s configmap where personalization is stored
    transport: OvfEnv
#  when writing this article, the available image ubuntu (ubuntu-20-1621373774638) is not able to use volume because is using virtual hardware version 10
#  Instead you can use the centos image (centos-stream-8-vmservice-v1alpha1-1619529007339)
#  volumes: #when writing this article, the volume mount parameter is not use, the volume is seen in the guest but should be formated and mounted manualy
#    – name: loeil-du-se-volume
#      persistentVolumeClaim:
#      claimName: loeil-du-se-pvc
#      readOnly: false

Optional, the description file of the network service In my example, I created a service of type LoadBalancer to connect in ssh from an external network to that of the PODs.

Please note, the kind is not Service as usual but VirtualMachineService

cat loeil-du-se-vm-service.yaml
apiVersion: vmoperator.vmware.com/v1alpha1

kind: VirtualMachineService
  name: loeil-du-se-vm
    vm: loeil-du-se-vm
  type: LoadBalancer
    – name: ssh
      port: 22
      protocol: TCP
      targetPort: 22

Once the YAML files are created, all that remains is to have them taken into account by Kubernetes.

Kubectl create -f loeil-du-se-vm-configmap.yaml

Kubectl create -f loeil-du-se-vm-deployment.yaml

Kubectl create -f loeil-du-se-vm-service.yaml

To verify the creation of the VM:

Kubectl get vm

To know more about it:

Kubectl describe vm loeil-du-se-vm

It remains a classic VM, so it will benefit from HA and vMotion (via DRS or host maintenance mode). On the other hand, it is “Developer Managed”, that is to say that it cannot be managed via vCenter, you will not see the contents of the console for example.

One tip though, check which ESXi the VM is running on, then connect directly to the ESXi through a browser and there you will have access to the console.

To connect in ssh, if you have access via a loadbalancer like me, you can connect to it directly, otherwise you will have to go through a bounce POD (like busybox, alpine or other) and do an ssh with the IP address on the POD network. You can find it as follows:

kubectl get vm loeil-du-se-vm -o jsonpath='{.status.vmIp}’; echo

The ssh must be done via the user entered in the Cloud Init, I had set fbe, it looks like this:

kubectl get svc loeil-du-se-vm
NAME             TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)        AGE
loeil-du-se-vm   LoadBalancer   22:32148/TCP   2d22h

ssh fbe@

To run a command as administrator (user “root”), use “sudo <command>”.
See “man sudo_root” for details.

If the ssh does not work, it is because the user was not taken into account by Cloud Init, try with root to obtain the default user, generally ubuntu for Ubuntu and cloud-user for CentOS:

ssh roo@
Please login as the user “ubuntu” rather than the user “root”.

If you have the error below, it is because the laptop from which you are connecting does not have the public ssh key entered or there is an error in it, so you must check the key appearing in the configmap file:

fbe@ Permission denied (publickey,password).

To debug Cloud Init, you must connect to the vm os via ssh or via the console and look at the log /var/log/cloud-init-output.log

There you go, feel free to ping me if you need more information.

Which platform for « Share Nothing Architecture » applications?

Organizations are seeking more agility to accelerate their business growth. Developing applications for internal or external usage can directly or indirectly impact that growth. It is important to provide agility to developers for them to write these applications. That’s why public cloud services are attractive. Developers can consume services right away by deploy a data service (e.g database) and connect it to their applications. They don’t have to worry about the infrastructure but instead focus only on developing the application. Bringing that flexibility into the datacenter will allow organizations to provide agility while maintaining security.

VMware Cloud Foundation with Tanzu (previously vSphere with Kubernetes or Projet Pacific) is a platform capable of hosting applications running in virtual machines and applications running in Kubernetes Pods (containers). It also provides networking services, storage,  registry, backup and restore services for those applications. Now, it also incorporates data services.

At the time of writing, two solutions were added: Minio and Cloudian. They are two object storage solutions compatible with S3 API. Two other are currently being integrated: Dell EMC ObjectScale, a object storage compatible with S3 and Datastax, a NoSQL database based on Cassandra. There are more integrations to come.


How is it revolutionary?

Unlike the majority of traditional/classic/monolith applications, modern applications also called Cloud Native or Scalable apps do not rely on the infrastructure to optimize their performance and to provide resiliency. They use their own mechanisms for availability, performance and no matter what infrastructure they’re running on. Of course, the infrastructure is essential but only to consume resources like processors, memory or I/O. These applications are often SNA (Shared Nothing Architecture). Each instance of an application uses its own resources on a distinct server and the application distributes the data between these servers. Reading and writing data is distributed for better performances and resilience while taking in consideration a potential loss of a server or a site.

On a physical infrastructure (without virtualization), it’s easy, each instance has its own server and its own resources. However, it creates a financial issue as the servers are dedicated to that usage. It’s not optimal unless all the resources always being consumed. It’s rarely the case.

On a virtual infrastructure, the resources are shared hence not used resources can be use by other applications. It also allows eliminate hardware compatibility issues and to take advantage of other benefits brought by virtualization. Nevertheless, there’s a constraint for SNA applications as the instances are virtualized. We need to ensure these instances and the generated data are distributed on different virtualised servers in case of of server failure.

VMware Cloud Foundation with Tanzu coupled with vSAN Data Persistence platform module (vDPp) is the answer to this problem. Partner editors are able to take advantage of the platform to provide “as a Service” solutions. They can do so by developing an operator to automate the installation, the configuration and simplify keeping it operational.


The service is up and running in one click


vDPp is aware of the infrastructure, the application knows how to get the best performances and availability. The operator thereby distributes the required number of instances on different virtualized servers.

This vSAN storage policy ensures data protection and keeps the application instance and its data on the same  virtualization host


During maintenance operations, the application is informed about the decommission of a virtualization server. vDPp also proactively communicates with the application if the disks start showing signs of failure.

Developers consume these services via APIs and stick to only developing their application. They can use an resilient and performant on-demand data service.


In Conclusion,

VMware Cloud Foundation with Tanzu platform coupled with vSAN Data persistence provide great agility to keep the data services operational. Thanks to that, developers can focus solely on application development while keeping on using their traditional tools. They have a cloud platform as it exists on public cloud.

VMware Cloud Foundation with Tanzu should be seen as a complete platform designed for the development and hosting of traditional and modern applications with integrated on-demand services.