We discussed Containers in the previous post, how they are different and the advantages it has over Virtual Machines. In this post, we will see how containers became a reality and how Docker became an inevitable name in the world of containers.
Containers operate in isolation. This isolation is aided by few kernel features one of them being Linux Cgroups. This feature allows you to group a set of process which can run as a related unit. This group of processes can be controlled in terms of how much memory, CPU utilization, I/O both disk and network it can use. Cgroups give fine grained control over allocation, monitoring and managing of system resources. Hardware resources can be divided among tasks and users increasing efficiency.
Another kernel feature which allows a restricted view of the system. When you login into a Linux machine, you will see it’s file systems, processes, network interfaces. Once you create a container, you will enter into a namespace which will render a restricted view and the container will have its own file systems, processes and network interfaces different from the host machine its running on. There are multiple namespaces that are used by containers and primarily Docker and each process is in one namespace of each type.
- Pid – (Process isolation) Processes within a PID namespace only see processes in the same PID namespace. Each PID namespace has its own numbering and starts with 1. When the PID 1 goes away, the whole namespace is killed. In the below example, an ubuntu container is spun up with an interactive bash terminal. The process table of this container has PID 1 which is the command /bin/bash. To summarise it, the container lifecycle is tied to the PID 1 in the container process table and when the PID 1 is killed, the container stops.
- Net – (Network isolation) Processes within the network namespace get their own network stack. This includes network interfaces, routing tables, iptables and sockets.
- Mnt – ( Filesystem mount points isolation) Processes can have their own root filesystem and this will be different from the host filesystem.
- UTS – (Nodename and Domainname isolation) This is to set the different hostname for each container. When you login to one of the containers and type uname -a, it will be different form the other
- IPC – (Inter Process Communication resource isolation) – To put it in simple terms, if two containers are trying to access something common, it could be a shared memory, message queues and there occurs a conflict as they are trying to access the same name or construct. This namespace will allow the containers to use the same constructs. It could have a private, shareable or even utilise the host system’s namespace. Below is an example of shared IPC namespace.
- User – User name space will allow you to be a privileged user with in the container when you are a non privileged user outside of it. You might be UID 1000 outside but UID 0 (root) inside. You will see a different user table on host and containers
Linux Containers (LXC) came in around 10 years back, but then how did Docker become the talking point in the world of containers ? The key differentiator was the Docker image. Never before could you actually encapsulate an application, it’s dependencies and configuration files into a lightweight portable bundle. This along with the Docker Engine API triggered the adoption of containers at a large scale.
A Docker image will comprise of multiple layers. You will see this when you do a “docker pull <image>” where it pulls different layers of the image from the registry. Each layer of the image is a image on its own and any changes made to the image will be saved as layers on top of the base image layer creating a nesting relationship as shown below. The base image is read-only and the top layers are read-write.
Docker Image is a not a filesystem or a virtual hard disk. It is more like a tar file with some additional metadata. If you want to copy a docker image from one host to another, you can save the docker image as a tar file and import it to the other host. Its is as simple as that.
Docker Host hosts the docker daemon which interacts with Docker images and Containers. It also hosts the Docker Images and Containers themselves. It publishes an API which other programs like orchestrators can contact images and containers. All the command line that we use to work with docker is handled by a docker client called Docker CLI which contacts the docker daemon running on the docker host.
Docker Registry store docker images. The official docker registry is called the Docker Hub. It has a lot of official images published by docker and also has private images published by individuals which you can make use off. If you have worked with git, docker registry uses similar semantics. It allows you to push, pull and commit and also follows a hierarchical structure.
As an individual, once you create an account on the docker hub you can do a push or pull of images. You can also create a private registry on your host and use that to store images.
Each Docker container has its own network stack and the NET namespace that we discussed above helps in achieving this. By default docker creates three networks (bridge, host and none) as shown below out of which docker bridge (appears as docker0 on host) is the default networking type unless explicitly specified.
You can create your custom bridges as well or use other network driver plugins available on docker. We plan to cover Docker Networking in more detail in one of our next posts.