[Docker] Docker for AI developers: Basics (Part-II)

In the previous blog post, we learned some basics concepts of the Docker. In this post, we will look into more details which will make us more comfortable with using the Docker.

Each time when we run a docker run command on some image, we kind of create a new computer with empty environment on which we can create/setup our application, execute it and then throw it away.

Now let’s play with some new image called as alpine to prove the above statement. Alpine is a small linux image with some pre-installed binary programs. When we run the following command at the terminal

sudo docker run alpine printenv

we are instructing Docker to create and run a new container using alpine image and execute the printenv binary available in the alpine image which produces following output:

Let us run the above command twice again and see its output, we get:

With above two commands executed twice, we are creating a new container from the same alpine image and each container is independent with unique HOSTNAME. For simplicity, this operation can be shown as:

Docker is a tool that allows you to get the equivalent of a disposable, single-time use computer.[1]

Output for docker ps -a is as follows which shows three stopped containers

We can delete all of these containers at once using

sudo docker container prune -f

instead of deleting each container one by one using docker rm ID command. Here, the -f switch provides an implicit confirmation to proceed and delete all stopped containers, instead of asking to confirm that operation.

Useful switch to creating the long lived containers:

One can create the long lived containers and run them in the background using -d switch along with docker run command. Here, -d corresponds to detaching the container. If we don’t detach the container then it will keep running printing its output to the console window. For example, see the difference for detached and non detached containers in the following figure

The output for docker ps -a command now is as follows: One can see that, the detached container is still alive where as the status for non detached container which was closed by Ctrl+C command is stopped.

The output for detached container can be seen anytime using docker logs ID command. This detached container can be stopped, deleted using following commands:

docker stop 401307d6fbe6
docker rm 401307d6fbe6

Listening to incoming network connections

Containers by default do not listen to the incoming connections on the local machine where they are currently running, a user has to explicitly open a port on the host machine and map it to the port on the container.

For example, the following docker run command will run a detached container created from the NGINX image which will map the input port 8085 of the host machine to the port 80 inside the containter.

docker run -d -p 8085:80 nginx

Following output will be shown:

Since the nginx image was not found locally, it was downloaded from the default registry. The output won’t be displayed on the terminal since its running in the background. One can observe that we need to use the -p switch for mapping between the ports.

In short the above command can be explained as (verbatim from [1]):

We want host machine to listen for incoming connections on port 8085 and route them to port 80 inside a container that runs NGINX server

Following output will be shown for http://localhost:8085:

One can use the docker logs ID command to see the output at any time.

Finally we can use docker stop ID and docker rm ID commands to stop and delete the container.

So basically whenever we run the above docker run command we are kind of creating a brand new server, installing whatever we want on it and finally trashing it as per our convenience. Using Docker, one can test any software without polluting the local machine which may break some conflicting dependencies.

One can run a Jenkins server without installing Java or any other dependencies on a local machine by using:

docker run -p 8088:8080 jenkins  

One can see that isolation and ease of use at a very low resource cost is an enormous advantage of using containers [1].

Using Volumes

Volumes are used in Docker to prevent the data loss or for having a persisted data storage mechanism while dealing with the containters.

Why volumes?

Whenever a container dies(stopped, failed etc.) the data inside the container is also lost as the containers are generally assumed to be stateless. To avoid data loss or having a persisted data storage one can use volumes to map a directory inside the container to a persistent storage location on the local machine. For example,

docker run -v /your/dir:/var/lib/mysql -d mysql:5.7

Here, the mapping is specified in docker run command using -v switch followed by the local directory /your/dir in this case to the directory inside the container /var/lib/myql in this case. The -d mysql:5.7 specifies to run the detached version of container created from the mysql5.7 image.

It will ensure that any data written to the /var/lib/mysql directory inside the container is actually written to the /your/dir directory on the host system. This ensures that the data is not lost when the container is restarted.

If we just run docker run -d mysql:5.7 then when this container is stopped or restarted or failed, the data would be lost.

Listing and deleting the available Docker Images

We can use following command to list all the available docker images

docker image ls

It produces following output:

If we want to delete the hello-world image then we can delete it by using the image name or using its ID as follows:

docker rmi hello-world

or

docker rmi bf756fb1ae65

Output: We can see that the hello-world image is deleted.

We can also delete the alpine and nginx as: however, one can see that these images are not deleted and there is an error message showing that some containers of these images are still referenced which can be verified as We can see that, container of alpine image is stopped however is not deleted, whereas container of nginx image is still alive.

Let us delete the alpine image first by deleting its container using docker container rm 972b802b3804 and then deleting the image as

In case of nginx image we can first stop the container and then delete it as

We can delete the nginx image using

The output for command docker image ls is now:

References:

[1] Learn Docker - .NET Core, Java, Node.JS, PHP or Python by Arnaud Weil, 2018