A first look at the vctl utility in VMware Fusion
Last week I updated my version of VMware Fusion to 11.5.6. If you don’t know about VMware Fusion, it is a VMware product that gives Mac users the ability to run guest virtual machines. One of the new features that I noticed was the inclusion of a new vctl utility (IIRC, it became available first in v11.5.5.). This is a command line utility for the Nautilus Container Engine which is now part of VMware Fusion. It allows you to work on OCI (Open Container Initiative) containers from your desktop. I decided to take a closer look, and do a few simple container tasks just to see it in action. From what I understand, starting the Nautilus Container Engine will launch a very lightweight virtual machine (CRX) based on VMware Photon OS. This is very similar to how PodVMs are constructed in vSphere with Kubernetes (You can find more CRX details in this earlier blog post on Project Pacific). Once that is up and running, you will be able to use vctl to do various container operations. Let’s have a look.
Getting started
vctl is installed with VMware Fusion. You don’t need to do anything else. Simply open a terminal on your host system and run vctl without any arguments to display the help output.
chogan@chogan-a01 ~ % vctl vctl - A CLI tool for the Nautilus Container Engine powered by VMware Fusion vctl Highlights: • Build and run OCI containers. • Push and pull container images between remote registries & local storage. • Use a lightweight virtual machine (CRX VM) based on VMware Photon OS to host a container. Use 'vctl system config -h' to learn more. • Easy shell access into virtual machine that hosts container. See 'vctl execvm’. USAGE: vctl COMMAND [OPTIONS] COMMANDS: build Build a container image from a Dockerfile. create Create a new container from a container image. describe Show details of a container. exec Execute a command within a running container. execvm Execute a command within a running virtual machine that hosts container. help Help about any command. images List container images. ps List containers. pull Pull a container image from a registry. push Push a container image to a registry. rm Remove one or more containers. rmi Remove one or more container images. run Run a new container from a container image. start Start an existing container. stop Stop a container. system Manage the Nautilus Container Engine. tag Tag container images. version Print the version of vctl. Run 'vctl COMMAND --help' for more information on a command. OPTIONS: -h, --help Help for vctl chogan@chogan-a01 ~ %
First we need to start the Container runtime using the vctl system start command.
chogan@chogan-a01 ~ % vctl system start Preparing storage... Container storage has been prepared successfully under /Users/chogan/.vctl/storage Preparing container network, you may be prompted to input password for administrative operations... Password:************* Container network has been prepared successfully using vmnet: vmnet9 Launching container runtime... Container runtime has been started. chogan@chogan-a01 ~ %
Starting the Container Runtime mounts a volume called “Fusion Container Storage” to the host’s desktop. Now that the runtime is started, let’s try to use vctl to deploy a container image.
Pull & Run a Container Image
For the purpose of this test, I’ll use everyone’s favorite stateless application, nginx. I will first pull it down from the docker hub/registry, and then I will run it using the —name option to give it a unique name, and the -t (tty) option to open a terminal to it, and finally a -d (detach) option so that it runs in the background . Finally I’ll connect the the IP address associated with the image and we should see the default nginx landing page. The container’s rootfs is also mounted to the host so you will observe another volume related to nginx container appear on your desktop.
chogan@chogan-a01 ~ % vctl pull nginx INFO Pulling from index.docker.io/library/nginx:latest ─── ────── ──────── REF STATUS PROGRESS ─── ────── ──────── index-sha256:b0ad43f7ee5edbc0effbc14645ae7055e21bc1973aee5150745632a24a752661 Done 100% (1862/1862) manifest-sha256:179412c42fe3336e7cdc253ad4a2e03d32f50e3037a860cf5edbeb1aaddb915c Done 100% (1362/1362) layer-sha256:a5e4a503d449887a0be28a2969149e647460aa6013f9ca90e88491aedf84f24e Done 100% (666/666) layer-sha256:cb9a6de05e5a22241e25960c7914f11b56c9070ce28b8f69e899236e0d265c50 Done 100% (26401375/26401375) layer-sha256:bf59529304463f62efa7179fa1a32718a611528cc4ce9f30c0d1bbc6724ec3fb Done 100% (27092121/27092121) config-sha256:4bb46517cac397bdb0bab6eba09b0e1f8e90ddd17cf99662997c3253531136f8 Done 100% (7512/7512) layer-sha256:9513ea0afb9372e5cabc4070c7adda0e8fc4728e0ad362b349fe233480f2e7d8 Done 100% (600/600) layer-sha256:b49ea07d2e9310b387436861db613a0a26a98855372e9d5e207660b0de7975a7 Done 100% (899/899) INFO Unpacking nginx:latest... INFO done chogan@chogan-a01 ~ % vctl images ──── ───────────── ──── NAME CREATION TIME SIZE ──── ───────────── ──── nginx:latest 2020-09-03T10:08:16+01:00 51.0 MiB chogan@chogan-a01 ~ % vctl run --name cornginx -t -d nginx INFO container cornginx started and detached from current session chogan@chogan-a01 ~ % vctl ps ──── ───── ─────── ── ───── ────── ───────────── NAME IMAGE COMMAND IP PORTS STATUS CREATION TIME ──── ───── ─────── ── ───── ────── ───────────── cornginx nginx:latest /docker-entrypoint.s... 192.168.143.128 n/a running 2020-09-03T10:11:07+01:00
And now if we connect to the IP address assigned to our nginx container, we should see the nginx landing page.
Looks good. Now, since the rootfs has been mounted to the desktop, you can very easily modify some of the container’s file contents. For example, if I wanted to change the landing page above, I could open the nginx volume for the container cornginx on my host desktop and navigate to /usr/share/nginx/html and modify the index.html file:
And now if I modify that index.html to something slightly different to the default, simply adding a newline of text to the bottom of the landing page, and saving it:
I have now updated my landing page in the easiest way possible:
Push an image using vctl to a remote container repository
Let’s now look at how we can use vctl to push images to a remote container repository. I have my own personal repo on docker hub that I will use for this exercise. The steps you will see below are the tagging of my local image with the name of the image I want in my repository. I will then push the image to my repo, and then we will examine my repo to see that the image is indeed there.
chogan@chogan-a01 ~ % vctl ps ──── ───── ─────── ── ───── ────── ───────────── NAME IMAGE COMMAND IP PORTS STATUS CREATION TIME ──── ───── ─────── ── ───── ────── ───────────── cornginx nginx:latest /docker-entrypoint.s... 192.168.143.128 n/a running 2020-09-03T10:11:07+01:00 chogan@chogan-a01 ~ % vctl tag nginx cormachogan/nginx chogan@chogan-a01 ~ % vctl push cormachogan/nginx -u cormachogan Password for cormachogan:********** INFO pushing image: cormachogan/nginx:latest to index.docker.io/cormachogan/nginx:latest ─── ────── ──────── REF STATUS PROGRESS ─── ────── ──────── manifest-sha256:179412c42fe3336e7cdc253ad4a2e03d32f50e3037a860cf5edbeb1aaddb915c Done 100% (1362/1362) layer-sha256:a5e4a503d449887a0be28a2969149e647460aa6013f9ca90e88491aedf84f24e Done 100% (666/666) layer-sha256:9513ea0afb9372e5cabc4070c7adda0e8fc4728e0ad362b349fe233480f2e7d8 Done 100% (600/600) layer-sha256:cb9a6de05e5a22241e25960c7914f11b56c9070ce28b8f69e899236e0d265c50 Done 100% (26401375/26401375) config-sha256:4bb46517cac397bdb0bab6eba09b0e1f8e90ddd17cf99662997c3253531136f8 Done 100% (7512/7512) layer-sha256:bf59529304463f62efa7179fa1a32718a611528cc4ce9f30c0d1bbc6724ec3fb Done 100% (27092121/27092121) layer-sha256:b49ea07d2e9310b387436861db613a0a26a98855372e9d5e207660b0de7975a7 Done 100% (899/899) chogan@chogan-a01 ~ %
And the final check to see that the image made it to my docker hub repo:
Looks good to me.
Pull an image using vctl from a remote container repository
OK – let’s now use the image I just pushed to my personal docker hub repo and use that as a container image rather than pulling directly from docker registry. First, lets remove the current container and the 2 images that currently exist, one locally and one on docker hub.
chogan@chogan-a01 ~ % vctl images ──── ───────────── ──── NAME CREATION TIME SIZE ──── ───────────── ──── cormachogan/nginx:latest 2020-09-03T10:08:16+01:00 51.0 MiB nginx:latest 2020-09-03T10:08:16+01:00 51.0 MiB chogan@chogan-a01 ~ % vctl ps -a ──── ───── ─────── ── ───── ────── ───────────── NAME IMAGE COMMAND IP PORTS STATUS CREATION TIME ──── ───── ─────── ── ───── ────── ───────────── cornginx nginx:latest /docker-entrypoint.s... n/a n/a stopped 2020-09-03T10:11:07+01:00 chogan@chogan-a01 ~ % vctl rm cornginx ──── ────── ────── NAME RESULT REASON ──── ────── ────── cornginx REMOVED chogan@chogan-a01 ~ % vctl ps -a ──── ───── ─────── ── ───── ────── ───────────── NAME IMAGE COMMAND IP PORTS STATUS CREATION TIME ──── ───── ─────── ── ───── ────── ───────────── chogan@chogan-a01 ~ % vctl images ──── ───────────── ──── NAME CREATION TIME SIZE ──── ───────────── ──── cormachogan/nginx:latest 2020-09-03T10:08:16+01:00 51.0 MiB nginx:latest 2020-09-03T10:08:16+01:00 51.0 MiB chogan@chogan-a01 ~ % vctl rmi nginx:latest ──── ────── ────── NAME RESULT REASON ──── ────── ────── nginx:latest REMOVED chogan@chogan-a01 ~ % vctl images ──── ───────────── ──── NAME CREATION TIME SIZE ──── ───────────── ──── cormachogan/nginx:latest 2020-09-03T10:08:16+01:00 51.0 MiB chogan@chogan-a01 ~ % vctl rmi cormachogan/nginx:latest ──── ────── ────── NAME RESULT REASON ──── ────── ────── cormachogan/nginx:latest REMOVED chogan@chogan-a01 ~ % vctl images ──── ───────────── ──── NAME CREATION TIME SIZE ──── ───────────── ──── chogan@chogan-a01 ~ %
At this point, we have no running containers, and the only image that is available is on my personal docker hub repo. Let’s now pull the image from my docker hub, and run it.
chogan@chogan-a01 ~ % vctl pull cormachogan/nginx:latest INFO Pulling from index.docker.io/cormachogan/nginx:latest ─── ────── ──────── REF STATUS PROGRESS ─── ────── ──────── manifest-sha256:179412c42fe3336e7cdc253ad4a2e03d32f50e3037a860cf5edbeb1aaddb915c Done 100% (1362/1362) layer-sha256:a5e4a503d449887a0be28a2969149e647460aa6013f9ca90e88491aedf84f24e Done 100% (666/666) layer-sha256:cb9a6de05e5a22241e25960c7914f11b56c9070ce28b8f69e899236e0d265c50 Done 100% (26401375/26401375) config-sha256:4bb46517cac397bdb0bab6eba09b0e1f8e90ddd17cf99662997c3253531136f8 Done 100% (7512/7512) layer-sha256:bf59529304463f62efa7179fa1a32718a611528cc4ce9f30c0d1bbc6724ec3fb Done 100% (27092121/27092121) layer-sha256:9513ea0afb9372e5cabc4070c7adda0e8fc4728e0ad362b349fe233480f2e7d8 Done 100% (600/600) layer-sha256:b49ea07d2e9310b387436861db613a0a26a98855372e9d5e207660b0de7975a7 Done 100% (899/899) INFO Unpacking cormachogan/nginx:latest... INFO done chogan@chogan-a01 ~ % vctl images ──── ───────────── ──── NAME CREATION TIME SIZE ──── ───────────── ──── cormachogan/nginx:latest 2020-09-03T14:31:57+01:00 51.0 MiB chogan@chogan-a01 ~ % vctl run --name mynewnginx -t -d cormachogan/nginx:latest INFO container mynewnginx started and detached from current session chogan@chogan-a01 ~ % vctl ps -a ──── ───── ─────── ── ───── ────── ───────────── NAME IMAGE COMMAND IP PORTS STATUS CREATION TIME ──── ───── ─────── ── ───── ────── ───────────── mynewnginx cormachogan/nginx:latest /docker-entrypoint.s... 192.168.143.129 n/a running 2020-09-03T14:37:54+01:00
Again, this all looks good. A nice addition to VMware Fusion for container workloads.
Running a command on a running container
Previously we saw how a container’s volume appears on the desktop when launch via vctl, and how easy it was to access the container’s file and make changes. But let’s say we wanted to run a command on a container. How would we do that? Well, vctl also comes with an exec argument that allows us to run commands on the selected container. Let’s once again use our nginx container, and use the exec argument run a bash shell on it. In this example I include the -i (interactive) option as well as the -t (tty) option. Check out the help on exec for more details. Like before, I am navigating to the location of the index.html nginx landing page, and displaying the contents.
chogan@chogan-a01 ~ % vctl exec -it mynewnginx /bin/bash root@mynewnginx:/# cd /usr/share/nginx/html root@mynewnginx:/usr/share/nginx/html# ls 50x.html index.html root@mynewnginx:/usr/share/nginx/html# cat index.html <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html> root@mynewnginx:/usr/share/nginx/html#
One other nice feature that I have read about is in the Fusion 12 Announcement is an extension to vctl which will allow the deployment of a Kind cluster, which is in essence Kubernetes cluster running in containers. I’m very much looking forward to trying that out when I get a chance.
Hi Cormac,
Your article is inspiring and was a good start point to me.
Thank you for that.
I started using vctl on VMWare Fusion 12. It always runs on vmnet8.
Do you know if it runs on other vmnets
Kind regards
Correct – it only supports vmnet8 today.
The team has discussed making that more user-definable – do you have a requirement for such a feature?
I found out that it can be changed.
For networking, we make it a fixed one (vmnet8) so the user does not need to input sudo crendential to create a new one (create new vmnets needs admin priviledge), but user still can use other vmnet by configuring ~/.vctl/config.yaml
vmnet: vmnet2
vmnet2’s nat and dhcp should be enabled to make it work.