Docker 101: Things to Know Before You Begin

Docker 101: Things to Know Before You Begin

Hey everyone, in this blog, we are going to learn some important things you need to know before diving into Docker. I know many folks watch tutorials, read documents, or blogs and directly jump into the installation and start using containers in their development environment. However, to be honest, they are missing some crucial pieces of the puzzle.

For instance, if you're unfamiliar with container engines, OCI (Open Container Initiative), runC, and similar concepts, you might panic when someone asks or talks about them. This is when you'll realize that you lack a foundational understanding. Most beginners know how to run and manage containers, but they often lack knowledge about the fundamentals. That's why I am writing this blog to help you understand the fundamentals. After reading this blog, you will have plenty of knowledge related to containers, and you will be able to work with different containers, not just Docker, as there are many other options out there. So, let's get started!

Containers 101

Let's first understand what a container is. You can imagine a container as a box where you place your application along with its proper dependencies. If you want to run your application on your friend's machine, you can simply give them the container, and this way, many problems can be solved, such as missing or incorrect application dependencies, hardware and VM issues. Of course, containers solves other problems as well, which you can research further by reading numerous blogs on Hashnode about it. However, that's not the focus of our discussion here.

The point is that a container is like any other process on Linux. I am saying this because containers have a state before running, which is the image. You can imagine the image as a file that contains all the data needed to run a container. Once you run that image, you will have a container running in your environment. We can get those images from registries.

Now that you know what containers are, and we can create containers using images, let's see what process happens when you run an image. It's not like containers directly come out of the image; there is a process behind it. Let's talk about that process and understand each concept.

Open Container Initiative (OCI)

In the world of cloud-native computing, we encounter various container image formats such as Docker, Appc, and LXD. However, does it truly make sense to have similar technologies with different names and add-ons? Rather than this approach, we could consolidate these technologies into one and focus on their enhancement. By doing so, we can fully leverage the advantages of containers. This is precisely why the Open Container Initiative (OCI) exists. Moreover, OCI is an open-source project under the Linux Foundation.

OCI includes a Container image format specification that defines both the on-disk structure for container images and the metadata that specifies details such as hardware architecture and the operating system (Linux, Windows, etc.). Now that we have an industry-wide container image format, various individual contributors, projects, and vendors can create interoperable images and tooling.

Container Image

A container image is just a file that contains the data required to run a container. it is a file that is pulled down from a Registry and used locally as a mount point when starting Containers.

Repository

When using the docker command, a repository is what is specified on the command line, not an image. In the following command, “rhel7” is the repository.

docker pull rhel7

This is actually expanded automatically to:

docker pull registry.access.redhat.com/rhel7:latest

When we specify the repository on the command line, the Container Engine is doing some extra work for you. In this case, the docker daemon is configured with a list of servers to search. In our example above, the daemon will search for the rhel7 repository on each of the configured servers.

Registry Server

A registry is a location where you can search or upload repositories. Typically, the registry server is specified as a regular DNS name and optionally a port number for the connection. One popular registry is Docker Hub. Much of the value in the Docker ecosystem comes from the capability to push and pull repositories from registry servers.

Image Layer

Repositories are often referred to as container images, but actually, they are made up of one or more layers. Docker images consist of many layers. Each layer is built on top of another layer to form a series of intermediate images. Layers allow you to work with Docker images faster. This is because the builds avoid unnecessary steps, and the pulling and pushing of images skips the transfer of a large unchanged amount of data already available in the intended destination. The use of the copy-on-write file system saves disk space for future containers and images.

Container Engine

A container engine is a piece of software that listens to user requests, such as docker pull and docker run. From the end user's perspective, it manages the execution of containers. There are several container engines available, including Docker, RKT, CRI-O, and LXD. Additionally, many cloud providers and Platform as a Service (PaaS) offerings have built-in container engines. These engines consume OCI-compliant container images.

Most container engines don't actually run the containers, they rely on an OCI-compliant runtime like runC. We will look into runC later, but you may ask, if runC is running the container, then what is the role of the container engine? There are a few things that the container engine does. let's look at them,

  • Handling user commands, such as docker pull or docker run.

  • Preparing a container mount point, typically on copy-on-write storage.

  • Preparing the metadata which will be passed to the Container Runtime to start the Container correctly.

  • Calling the Container Runtime.

Container Runtime

The container runtime plays an important role in starting containers and performing various actions on container images to create functional containers. Just for your information, when the Docker engine was initially developed, it relied on LXC as its container runtime. Later, the Docker team created their own library called libcontainer to manage container startup. This library was crafted using Golang and integrated into the original Docker engine. Finally, when the OCI was created, Docker donated the libcontainer code and turned it into a standalone utility called runC. Now, runC is the reference implementation and is used by other Container Engines such as CRI-O. At the lowest level, this provides the ability to start a container consistently, no matter the container engine. runC is a simple tool. You just need to give it a folder and some data (config.json) to make it work.

Containerd

Containerd is a tool that helps manage containers. It takes care of tasks like moving container images, running containers, and handling networking for them. It makes things simpler for runC, the tool that actually creates the containers. In short, containerd acts like a supervisor, handling all the extra tasks related to creating the containers. Containerd calls runC with the right parameters to run containers.

Docker daemon

Docker daemon is a persistent background process that manages Docker images, containers, networks, and storage volumes. The Docker daemon constantly listens to Docker API requests and processes them. Docker daemon prepares the image as an Open Container Image (OCI) bundle and makes an API call to containerd to start the OCI bundle. containerd then starts the container using runC.

runC

I think in this post, I mention runC a lot. By this time, you should have an idea of what runC is, runC is just a component that actually interacts with the kernel and runs images. There is great documentation on runC and its features. You can go there and learn more about it.

I believe these are the most crucial things you need to know in order to get started with containers. If I have forgotten something, please share it in the comments so that I can also learn about it. Lastly, I encourage you to do some research on your own you might discover things that I am not aware of.

I hope you learned something from this blog. If you have, don't forget to drop a like, follow me on Hashnode, and subscribe to my Hashnode newsletter so that you don't miss any future posts. You can also reach out to me on Twitter or LinkedIn. If you have any questions or feedback, feel free to leave a comment below. Thanks for reading and have a great day!

Did you find this article valuable?

Support Aditya's Blog by becoming a sponsor. Any amount is appreciated!