I am working with containers for a very long time now. It started back when I worked as a Developer Advocate at Gitpod. All I have been doing ever since is creating containers and running them using docker. Nothing really special. So, I have decided to dive into the internals of the container ecosystem and break some of the assumptions I had about them.
My initial assumptions
- I used docker and containers synonymously even though I knew Docker is just a containerization tool.
- I thought different containerization tools are very different from each other.
- I thought Kubernetes only works with Docker.
- I never knew why docker had two modes: 1. Windows containers 2. Linux containers
What are containers?
Containers are packaged units of executable software that contain the application code, the libraries and other dependencies that are required to run the code. Containers run in an isolated environment like the Virtual Machines. One main difference between a Virtual Machine and a Container is that A virtual machine runs a complete Guest operating system on top of the Host operating system on the other hand a container packs the bare minimum that is just enough to run the software, they do not ship with a kernel and are considered to be lightweight.
The container ecosystem
The concept of containers existed since 1979, but the capabilities of the concept back then were so limited compared to what we have now. It was not until the March of 2013 when Docker was released as an open-source project that the technology has become popular.
Since the launch of Docker, we have come a long with more containerization tools and standard ways of developing containerization tools.
- The Open Container Initiative (OCI).
- Container Runtime Interface (CRI).
The Open Container Initiative
The Open Container Initiative was established in 2015 by Docker and other leaders in the container industry. The official website defines it as below:
The Open Container Initiative is an open governance structure for the express purpose of creating open industry standards around container formats and runtimes.
The OCI is basically the governing body which published the specifications for the container runtime and images. If a tool wants to be widely accepted then it makes sense to follow the specifications laid out by OCI.
Container Runtime Interface
Container Runtime interface was introduced by Kubernetes. This API basically allows Kubernetes with any container runtime that implements it irrespective of the internal implementation.
Container Images contain the packaged application, the dependencies it requires and all the underlying libraries and also contain information about what processes need to be invoked when the image is run as a container.
- Container images are immutable, which means once they are created they cannot be changed which helps maintain consistency in any environment. When these images are run a small writable layer is added on top of the image to allow modification of files.
- Container images are made up of many layers, layers make it possible to reuse components across images. If the images are built with care by sharing and utilizing shared components, one can utilize the disk space optimally.
A container runtime is a piece of software that can run containers on a host machine. The container runtimes can be broadly classified into two:
- Low-level Container Runtimes
- High-level Container Runtimes
Low-level Container Runtimes
Low-level container runtimes generally deal with how the containers are created and run in an isolated environment of the host operating system. It is the responsibility of these runtimes to deal with namespaces to achieve process, network, disk isolation and cgroups to control the usage of resources. These runtimes are generally built in compliance with the OCI (Open Container Initiative) runtime-spec. The following are some of the low-level runtimes available:
- runc: This was the first runtime to be created which completely implements the OCI spec. Docker uses runc as its runtime. It is written in go.
- crun: This was created by RedHat and is written in c.
High-level Container Runtimes
High-level container runtimes are responsible for the packaging of a container image, their transport, unpacking on the host and passing the image to the low-level runtime. These runtimes are built in compliance with the CRI (Container Runtime Interface). Implementing support for CRI will make the runtime compatible with Kubernetes. The following are some of the High-level container runtimes:
- containerd: containerd was developed by Docker and was later separated from docker and is now a graduated project under CNCF. It implements the CRI spec and provides a whole range of capabilities from creating images, pushing and pulling images, and managing the entire life cycle of containers at a higher level.
- CRI-O: This is another open-source container runtime that provides all major functionalities as containerd and implements Kubernetes CRI. It is developed as an light weight alternative for docker as the Kubernetes runtime.
Docker is a tool which makes it easier to work with containers. Docker became popular because it was the first to offer the entire ecosystem of container management. Docker has the following components:
- Docker-cli: Many of us are very familiar with this component of Docker. This is the command-line utility to interact with images, containers, networks and volumes.
- Docker daemon: Docker daemon is a daemon process which listens to the commands issued from the Docker-cli.
- containerd: This is where the actual magic happens. containerd is a daemon process that manages the container lifecycle. containerd is responsible for managing the images(pull/push), managing storage, network and supervises the running of containers.
- runc: Run is the underlying low-level container runtime written in go. This is responsible for creating and running the containers.
Kubernetes is an open-source container orchestration tool developed by Google but was later made independent. This tool makes it easier to deploy, scale and manage container based applications.
CRI exists because of Kubernetes. CRI helps abstract out the container runtime from Kubernetes and it makes Kubernetes capable of swapping out one CRI compliant runtime for another.
The Big Picture
The following image should give us a fair understanding of the container ecosystem.