Block Image

What is a container

A container can be defined as an isolated process.
Linux kernel features such as cgroup and namespace are used to allow these processes (the containers) to be independent with other processes and with other containers themselves even if they are running on the same machine.

Virtual Machine vs Container

A virtual machine (VM) is a virtualization of the hardware of the host computer so that a new operating system (a new kernel) can be run.
If you choose to use this technology to isolate your various applications in production, you will need to create multiple VMs.
Virtual machines are created thanks to a software in the host operating system, called hypervisor or also virtual machine monitor (VVM).
The hypervisor then has the task of creating and running VMs.

A container, on the other hand, is simply an isolated process that shares its kernel with the host system. Consequently, a container is much lighter than a VM (order of MB vs order of GB). Another consequence is that the process of importing/exporting a container on a new machine is much faster than that on a VM.
Basically, each of our applications will be a container.

Here is a drawing that summarizes the concept written above:

Block Image

Virtual Machine and Container: does one exclude the other?

No, actually, containers are often run inside VMs in production.

What is Docker

Docker is a technology that allows the creation of containers and the management of their lifecycle (create them, start them, shut them down, destroy them...).

The engine that runs Docker is called the Docker Engine.
It is based on the client-server architecture, where the server is the Docker daemon called dockerd, which allows to perform the various operations on containers, while the client is the CLI, which through the API of Docker interacts with the Docker daemon, through a channel of communication type socket Unix (the Docker daemon is listening on /var/run/docker.sock).

You can have clients and servers on the same machine, but also on different machines. In addition to the CLI, we can also have graphical interfaces (e.g. Portainer) as Docker clients.

Images to build containers

An image is a read-only template that contains instructions to build containers.

Comparing this with object-oriented programming, we could associate images with classes, and containers with objects. From the same image you can build more containers (just as from the same class you can create more objects).

An image is based on several read-only layers. Creating a container, creates an additional layer, called the "container layer", which is the only writable layer (and is not persistent).
Also, often an image is based on one or more images. For example, you might create an image of application from a ubuntu image.

You can create custom images via a file called Dockerfile (we'll see in the next article how to use it), or you can download images from a registry, such as the Docker Hub, which is the default registry.

In the next article, talking about the Dockerfile, we'll take a closer look at the layers part.

Installing Docker

We mentioned that containers take advantage of Linux kernel features, so I recommend trying Docker on a Linux distro, even in VM.
Otherwise you can install Docker Desktop for Windows and MacOS, which allows you to run Docker in a virtualized way.

At this link you will find the official guide to install Docker on all operating systems: https://docs.docker.com/engine/install/.

If you use Docker Desktop for Windows, I recommend to download also from the Store the Ubuntu application (or similar), and then enable the integration on Docker Desktop from the options of the latter, so you can use the commands as if you were on Linux.

Block Image

Let's create an Ubuntu container: pull and run commands

To verify that the installation was successful, we run from the terminal:
docker version
In response we should get the info about the Docker client and the Docker server.
Let's now try to download the Ubuntu image by running this command:
docker pull ubuntu
we should have a similar answer:

Using default tag: latest
latest: Pulling from library/ubuntu
35807b77a593: Pull complete 
Digest: sha256:9d6a8699fb5c9c39cf08a0871bd6219f0400981c570894cd8cbea30d3424a31f
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest

The image, not being present locally, is downloaded from the default registry, the Docker Hub (Downloaded newer image). Also note the latest tag; if you don't specify a tag, Docker by default downloads the one with the latest tag.

The tag indicates the version of the image. It does not necessarily correspond to the version of the application. I recommend not to use the latest tag, since it points to the latest version (but it is not obvious, it is the responsibility of the image developer) of an image at that precise moment. It is always a good idea to download the image with a precise version, to avoid compatibility problems.

To see the list of downloaded images, we can run the command:
docker images

Block Image

To build a container from an image, we must use the run command followed by the image name or image id:
docker run ubuntu

Let's now look at the list of active containers:
docker ps

The list is empty! So let's see the list of all containers, even the inactive ones:
docker ps -a

Block Image

The container status is Exited; this is because containers execute a main command (with PID 1), in this case "bash", then when the command is finished, they stop. We delete the newly created container with the rm command followed by the container name or container id:
docker rm priceless_hellman

The container name, not having specified it during the run command, is chosen by Docker randomly.

Now let's create the container again like this:
docker run --name ubuntu-container -it -d ubuntu bash

Let's analyze the command:

  1. With --name we specify the name of the container.
  2. With -it we are merging the -i and -t options. With -i, we keep the STDIN even if we are not attached to the container (we'll see what that means in a moment). With -t we attach a terminal to the container.
  3. With -d we indicate to Docker that we want to run the container in detach mode, that is in the background.
  4. The last command, after the image name, is the command we want to execute, in this case bash.

We run docker ps again to see the list of active containers:

Block Image

This time the container is UP & Running!

Stop and Start of a container

To stop a container, simply run the stop command followed by the container name or container id:
docker stop ubuntu-container

To start it, just run the start command:
docker start ubuntu-container

Let's enter inside the container: the attach command

To get inside a container, we can use the attach command, followed by the name of the container:

docker attach ubuntu-container

Now we are inside the container, with the root user! We run the command ps aux to see which processes are active in the container:

Block Image

There are two active processes, the first, with PID 1, is the command we used when creating the container, the second is the command we just executed to see the list of processes.

We now exit the container by executing the exit command.
Let's see the list of active containers again. We have a surprise: ubuntu container is no longer active!

This is because, by default, running the exit command, Docker kills the main process, with PID 1. The container is active only if the process with PID 1 is active.

To make sure that when we are attached to the container, it does not terminate when we exit, we need to press CTRL+p and then CTRL+q.

Execute commands on a booted container: the exec command

We can execute commands on a container without needing to enter it.

For example, we could write something like this:

Block Image

We could also enter a container by running another bash command, thus avoiding the attach command:

Block Image

Note that there are now two bash processes.When we exit the container with the command exit, we will stop the second bash process, so the container will continue to be UP and Running.

Conclusions

In this introductory article on Docker, we quickly saw what a container is and some differences with VMs.
We then saw Docker's basic operations to manage the lifecycle of a container and other commands such as attach and exec.
In the next article we will see how to create a custom image with the Dockerfile.

Articles about Docker: Docker