A beginner’s guide to Docker and containerisation

I joined Resolver in February 2020 and was put into the Accord team, working on a brand new project (they had literally typed the words ‘rails new’ the week before I joined). This meant I would be involved from the ground up, that I would see the app grow and develop from the very start, and that I wouldn’t need to get to grapple with years of legacy code.

As we had decided to build the app with Docker, which I haven’t used before, it also meant I was about to get to grips with it very quickly! This whale of a task initially felt quite daunting, but thanks to the gloriously comprehensive Docker documentation, Linux Academy courses about Orchestration, Containers and Docker, Rob Isenberg’s invaluable Docker for Rails Developers book, and, of course, a lot of Googling, I’m now happy to share my understanding of containers and Docker in my next few blog posts. I hope they can help make the topic a little less confusing!

What is containerisation?

As I’m sure all developers can verify, there is nothing more frustrating than when your beautifully crafted code works perfectly on your machine, but not on your colleague’s. Or when code that works perfectly in development or staging suddenly no longer works when it deploys to production.

Containerisation is designed to stop that happening, and to ensure that code can run consistently and reliably in any environment. The container provides the specific environment that the code needs to run, regardless of the machine it’s running on.

Think of your codebase as an awkwardly shaped jigsaw piece that you’ve built on your own machine and fits perfectly, but might not fit so well into someone else’s. A container wraps that awkward piece in a neat square box so that it has everything it needs and will fit everywhere.

A container is sometimes referred to as an “isolated execution environment” to run your software in. It is an “execution environment” because the container holds everything your code needs to run – i.e., the file system, the environment variables, even scripts. And it is “isolated” because as long as all these things are inside the container, they don’t need to exist on your computer. For example, if you install Python inside a container, you can run Python scripts inside that container, even if Python isn’t installed on your machine.

Where do containers come from?

Containers are built from images. Every container has a unique environment because of a unique combination of all the things mentioned above – the file system, commands, dependencies, scripts etc. The image is the file that provides the instructions to build this unique environment for a container. The image is like the template instructions to build containers in that design.

I find it helpful (with my Ruby mind) to think of an image like a class, and every container built from that image as an instance of that class.

It might sound daunting to set up an entire working environment for your code from scratch. Thankfully, there are base images available, which are beautifully crafted and publicly available images that are pre-configured with a basic environment you might need for your project. You can tweak and enhance them with specific additions for your project, taking a lot of the scary and time-consuming preparation work out of setting up your Docker environment.

What are containers used for?

As if ensuring your code runs reliably and consistently across machines and environments wasn’t enough, containers are useful for various scenarios, including:

Microservices, or splitting an application into multiple individual applications that can be built, updated and scaled separately. For example, you could even use different languages for each microservice, and therefore across your application.

Moving existing software into the cloud. It is relatively easy to wrap existing software in containers. Containers also use fewer resources so you can save money on cloud space.

Automated scaling, or automatically increasing or decreasing resources as the number of visitors to (part of) your application changes. Containers can be set up and taken down very quickly in response to change in traffic, which reduces costs.

Advantages of using containers

As mentioned, the biggest and most obvious advantage of using containers is that it allows you to run your software across machines and systems reliably, meaning no “well, it works on my machine” conversations with your colleagues or nail-biting deployments to make sure nothing has broken.

Containers are sometimes compared to virtual machines. However, unlike virtual machines, containers don’t need a copy of the whole operating system – just your application and a definition of all the required binaries and libraries. They therefore use fewer resources and less disk space than virtual machines, meaning they start up faster and you can fit more on your computer. The only downside is that containers are slightly less flexible than virtual machines – for example, you can’t run a Windows container on a Linux machine, although you can run a Linux container on a Windows machine (but who uses Windows anyway?).

Other benefits of using containers include being able to share libraries across containers, run different programs in isolation, and faster and simpler automation.

What is Docker and Docker Compose?

So, I’ve managed to write nearly a whole Docker blog post without mentioning Docker. Docker is arguably the industry leader in container technology.

Docker builds images by reading the instructions in your Dockerfile (a text document that contains all the command line commands needed to assemble an image). You can build images from a Dockerfile with the command `docker build`.

You can store all your Docker images in Docker Hub. Docker Hub hosts repositories, which hold images, in the same way that GitHub hosts repositories of code. A Docker Hub repository includes useful information about the image, such as a short and full description, a pull link, and documentation. You can copy Docker images from Docker Hub onto your machine with the command docker pull.

While a Dockerfile builds single images and containers, a Docker Compose file is a higher level definition used by the Docker Compose tool, which describes how to build multiple containers that fit together to form an entire application.

I will be writing more blog posts in future about what I’ve learnt so far about Docker, exploring topics like the Dockerfile and Docker Compose file, useful Docker commands, and specifically, setting up a Rails application in Docker.

By Elise Aston

Software Developer at Resolver