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:
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.
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/.
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.
To see the list of downloaded images, we can run the command: docker images
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
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
Now let's create the container again like this: docker run --name ubuntu-container -it -d ubuntu bash
Let's analyze the command:
- With --name we specify the name of the container.
- 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.
- With -d we indicate to Docker that we want to run the container in detach mode, that is in the background.
- 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:
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:
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:
We could also enter a container by running another bash command, thus avoiding the attach command:
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