Since their inception, we’ve been told several times that Containers are better than Virtual Machines. Now, I’m here to tell you they don’t. Docker Containers aren’t better or worse than Virtual Machines(VMs) but in my experience, the latter is much better. Let me tell you why.
Why Are They Useful?
First of all, let’s recall why these two virtualization tools are very convenient for developers and IT.
In software development teams, many times happens that a team member needs to install the software in a different OS than the application is run.
Software running on an Ubuntu Server might be develop in a MacOS computer. Installing stuff in MacOS is way different than in Ubuntu and this can cause trouble for developers when trying to run their applications in development mode.
This is where virtualization comes in play. You setup a virtual machine with all necessary dependencies for your software to run and then give the configuration files to developers and with a few commands you can have a proper development environment regardless the computer or operating system.
You now have portable and reproducible environment for many OS.
Docker Containers serve this purpose as well but they do it in a different and more performant way.
Docker Containers vs Virtual Machines
One of the main differences between these two kind of virtualization tools is that virtual machines might need more HDD space upfront, are slower to build up, and can be slower to boot up.
Containers consume less disk space (it depends), are faster to build up, and are faster to launch.
One could say containers are the best of the best. Tools such as Kubernetes might prove it right and definitely, they have a solid ecosystem and use case.
But what I see is that from the IT or DevOps perspective, they might be awesome but in Developer Experience, they’re not.
The following are the reasons why I believe Virtual Machines are better than Docker Containers. They’re mostly based on good personal experience in projects where servers were Virtual Machines and not so good projects deployed in a container solution.
Find Host or Container
With Virtual Machines, whenever you need to debug or test something in a cloud environment, you just need to ssh into a given IP address and that’s it.
When using Docker Containers you’d have to first get the IP address of the Docker Host and then find the specific container the application was deployed to.
If you have several Docker Hosts, and your app is deployed to several containers, then good luck finding the right container in the right host.
Of course, this can be solved with a script. Code (or have someone to do it for you) a script that:
- Loops through the IP addresses
- Run docker ps
- Grep the output and look for the container ID
- Continue until there’s a match
- If there’s a match, run docker container exec
- Do your debugging
What a PITA. Now, you have this script and you think you won’t have any more problems. Well, what about rotating IP addresses for security reasons?
This is why I think Virtual Machines are better than Docker Containers. In a cloud environment, accessing a virtual machine is WAY easier than just finding a given container.
Imagine you are handed a new feature to build. You have to upload files to a file storage. You think about your users so you set the upload to be run in the background because uploading a file might take some time.
You build it, test it, and try it locally. It works on your machine.
If you push this feature to a Virtual Machine deployed web app, well, it’s going to work fine. If you deploy it to a Docker Containers deployed web app, it won’t.
Why? Keep reading.
In the containerization world, one container regards one task, so that they’re small, reproducible, and fast.
With this in mind, your application container only regards running your code NOT running background jobs. So, if you have background jobs running in a different container, the file upload feature won’t work because the uploaded file won’t exist in the background job container.
Let me slow down:
- App container: runs code and has its own file system.
- Background job container: runs code in background way and has its own file system.
- App container: receives uploaded file via form in web browser.
- Background job container: picks file in the expected folder but it won’t exists because…
The key here is file system differences. The App container file system is not the same as the Background job container file system. When the App container receives the file, it is stored in a temporal path. As this path or folder is not in the Background job directories, the file won’t be found nor uploaded.
And now we have a situation. We either use some kind of Docker Volume or leave the feature as a synchronous one. In my case, I left the feature to work in a synchronous mode.
This is another reason I think Virtual Machines are better than Docker Containers.
Previously, I mentioned Docker Containers are a good alternative to Virtual Machines because they consume less disk space. Well, this might not be 100% true.
It happens that before a container you need a Docker Image. A Docker Image is like a base artifact that describes all the things the container will have when is run. With a Docker Image you indicate the container operating system, installed software, environment variables, configured files, and command to execute.
Similar to Virtual Machines images, Docker Images consume disk space. If you’re not careful enough you’ll end up using all your HDD space. You have be mindful about the base image you use to build your images, what dependencies are downloaded, and know a few key points to use less disk space as possible when building your images.
In the end, for the developer who only wants a portable environment, this make no difference than using a virtual machine.
This is when Docker Alpine steps in. What’s Docker Alpine? It’s a Docker Image that has all important stuff to run Linux and leaves out everything that is no strictly required.
By using Alpine, you can really create slim Docker Images. It brings the benefit that your images will build faster, your containers will be build much faster, and you’ll use less disk space.
Of course, it comes with its own set of problems. I experienced one of them.
Generating PDF Files with WKHTMLTOPDF
WKHTMLTOPDF is a tool to generate PDF file out of HTML content. It’s really useful because you can reuse HTML files and their styling. PDF generation is a complicated domain and WKHTMLTOPDF helps a lot to simplify.
If your software is installed in a Ubuntu server, you’ll be more than fine as many of the WKHTMLTOPDF dependencies are already available in the OS. However, when your software is being deployed to a Docker Container based on Docker Alpine, you’ll run into problems.
In this situation, Docker Alpine is going to be troublesome. Nothing big but it’s bothersome.
In the end, to solve this issue with Docker Alpine I had to read through GitHub issues, read more, and try many options to see what worked and what didn’t. In order to make the tool work in the Docker Containers I had to installed several missing dependencies, more dependencies, and finally WKHTMLTOPDF.
All that trouble could’ve been avoided with Virtual Machines. It wasn’t the first time I used WKHTMLTOPDF. I even have a set of scripts to installed it. They’ve always worked in Ubuntu operating systems. They didn’t work in Docker Alpine.
Not that I’m saying that Docker Alpine is bad. It’s just very different and might cause trouble. But this is also a point I think Virtual Machines are better than Docker Containers.
You might think to yourselves these points are only valid to me because they’re personal experiences. You’re right.
I’m not saying Virtual Machines will always be better than Docker Containers. The message here is that VMs have been great all those times I used and needed them. Containers are cool, of course, are performant, and small. Sure. But in regards of Developer Experience, Virtual Machines have still many good stuff to offer.
From my viewpoint Virtual Machines > Docker Containers.