12  Tutorial - Introduction to Docker

After completing this tutorial the student should be able to:

12.1 Why do we need Docker?

When we write a program on our system, we often execute the program to check if the results come out as expected. This phase is known as development phase. Sometimes there maybe a testing and evaluation phase that follows the development phase. However, for small programs we do this ourselves and it still can be thought as part of the development phase. Once the debugging process is complete and we are confident that the program is free of errors, we are usually ready to share the program with others so that more users can start using our code. This is known as production phase.

Many times you may have observed that your code executes correctly on your system, but does not execute or provide expected results when executed on your user’s machine. This happens due to several reasons, including the following:

  • The operating system is different between your machine (development server/machine/system) and your user’s machine (production server/machine/system)
  • The version of Python (or programming language) might be different betwen the development and production systems
  • The version of packages that your program relies on might be different between the development and production systems

These issues result in you spending a significant amount of time in resolving issues that related to the environment setup and are not directly relevant to the algorithm you are trying to develop. Docker (Merkel 2014) allows for setup of an isolated environment that is agnostic to the host operating system. These isolated environments are called containers. This way, when you want to share your program with your users, you simply point them to your docker container. When they pull your docker container and run it on their systems, it creates an isolated environment exactly matching with the environment in which you developed the code. This allows sharing of applications across different production machines while ensuring that the code will execute in a similar manner as on the development machine.

Common Confusion

Are docker containers simply virtual machines?

While they do almost appear as virtual machine running its own operating system, they are infact much lighter than virtual machines in terms of the computational overhead they induce on the host operating system. While most virtual machines will have a full OS installation take up a lot of space, docker containers mostly avoid graphics and work off a shell terminal (although GUI functionality can also be routed through a docker container).

As we embark on the journey to develop waypoint tracking module for an ASV in this course, we all will be working with different computers that have different operating systems and different python versions. To be uniform and to ensure that your final code can actually work fine with the hardware, we will leverage upon docker containers for code development.

12.2 Installing Docker on your machine

Based on your operating system, follow the instructions to install docker on your system. In Linux and Mac installation is possible via command line too. However, it is recommended to install Docker Desktop rather than through command line.

12.2.1 Ubuntu

You can install Docker Desktop for Linux or install using the apt repository. You can see the installation instructions here for Docker Desktop Linux.

After main installation, follow the post install instructions here to enable running docker without sudo.

12.2.2 Windows

On Windows you will need to enable WSL2 for Windows before installing Docker. Check out the instructions here. Make sure to choose Ubuntu 22.04 distribution when installing WSL2.

Once WSL2 has been enabled, you can install Docker Desktop for Windows from here.

12.2.3 Mac

On Mac you can install Docker Desktop for Mac directly without any pre-installation steps. Follow the instructions to install Docker Desktop for Mac here.

12.3 Running hello-world container

Make sure you are connected to the internet as we will be pulling the docker container from the cloud. Remember to start Docker Desktop first before executing the docker commands in the terminal (When you start Docker Desktop, it might prompt you to login first; you can safely skip and continue)

12.3.1 Windows

On Windows, open a powershell and type the following command

docker run hello-world

12.3.2 Mac

On Mac, open a terminal and type the following command

docker run hello-world

12.3.3 Linux

On Linux, open a terminal and type the following command

docker run hello-world

This command will pull the hello-world docker container and run it. The output should be similar to the following:

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

12.4 Pulling the course container

Now let us pull the container we will be using for this course. Note that this docker container is about 2.5 GB in size. So try to pull this container while you are connected to the network rather than over your mobile hot-spot.

Run the following command on the command line after starting docker desktop.

docker pull abhilashiit/oe3036:1.0

You should see an output that looks similar to the following:

...
c2f173c78ba1: Pull complete 
8913a4873b18: Pull complete 
bc4c66b0c3c7: Pull complete 
ea6e40c68297: Pull complete 
355f50b6f8c1: Pull complete 
4c671cb8bf91: Pull complete 
abd1edf68aa0: Pull complete 
f7d6ecd5d4a1: Pull complete 
647e7f1f292e: Pull complete 
1a9db48a7c5c: Pull complete 
b84be56effd8: Pull complete 
47d5ae67b8a3: Pull complete 
Digest: sha256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Status: Downloaded newer image for abhilashiit/oe3036:1.0
docker.io/abhilashiit/oe3036:1.0

12.4.1 Working with course container

We can try running the course container with the following command:

docker run -it --rm --name oe3036 abhilashiit/oe3036:1.0 bash

This should result in a bash shell access to the docker container named oe3036. You should see the shell terminal change from <your-username>@<your-systemname>:<your-working-path>$ (on my Ubuntu system this might look like abhilash@iitm:~/Academics/Teaching/OE3036/tutorials$) to something similar to mavlab@docker-desktop:~$. Notice that you are logged in as the user mavlab inside the docker container. You can check the current working directory that you are at with the command pwd.

mavlab@docker-desktop:~$ pwd

This should show the output:

/home/mavlab

Notice that this path is not on your computer but inside the docker container. You cannot navigate to this path using the regular way that we access the folders through your operating system.

In order to be able to edit code and see its execution inside the docker container, we will mount the directory from which you have initiated the docker container to the location /workspaces/mavlab inside your docker container’s file system. That way we all will be seeing the same path for all our tutorial files. To do this, we will create a shell script (batch script on Windows). First type exit inside the docker container shell as shown below and you should be back to your own system’s shell.

mavlab@docker-desktop:~$ exit

Now create a new file on your system and name it ros2_run_devdocker.sh (on Windows name it as ros2_run_devdocker.bat). Open the file with a text editor of your choice (notepad on Windows and gedit on Ubuntu should work fine), copy the following lines into the file, save it and exit the text editor.

For Ubuntu or MacOS (ros2_run_devdocker.sh):

if [ $# -eq 0 ]
  then
    echo "No arguments supplied! bash assumed"
    cmd=bash
  else
    cmd=$1
fi

docker run -it --rm --privileged --name oe3036 \
    --net=host \
    --env="DISPLAY" \
    --env="ROS_DOMAIN_ID=42" \
    --workdir="/workspaces/mavlab" \
    --volume="$(pwd)":"/workspaces/mavlab" \
    --volume="/dev/shm":"/dev/shm" \
    --volume="/dev/sbg":"/dev/sbg" \
    --volume="/dev/ardusimple":"/dev/ardusimple" \
    --volume="/dev/propeller":"/dev/propeller" \
    --volume="/dev/rudder":"/dev/rudder" \
    --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
    abhilashiit/oe3036:1.0 $cmd

For Windows (ros2_run_devdocker.bat):

@echo off
setlocal enabledelayedexpansion

:: Check if an argument is supplied
if "%~1"=="" (
    echo No arguments supplied! bash assumed
    set cmd=bash
) else (
    set cmd=%1
)

:: Run the Docker container
docker run -it --rm --privileged --name oe3036 ^
    --net=host ^
    --env="DISPLAY" ^
    --env="ROS_DOMAIN_ID=42" ^
    --workdir="/workspaces/mavlab" ^
    --volume="%cd%":"/workspaces/mavlab" ^
    --volume="/dev/shm":"/dev/shm" ^
    --volume="/dev/sbg":"/dev/sbg" ^
    --volume="/dev/ardusimple":"/dev/ardusimple" ^
    --volume="/dev/propeller":"/dev/propeller" ^
    --volume="/dev/rudder":"/dev/rudder" ^
    --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" ^
    abhilashiit/oe3036:1.0 !cmd!

On Ubuntu (and on MacOS) you will need to make the shell script executable by navigating to the folder containing the file ros2_run_devdocker.sh in the terminal and executing the following command:

chmod +x ros2_run_devdocker.sh

Now we can run the created shell script (batch script on Windows) as follows:

Ubuntu (or MacOS):

./ros2_run_devdocker.sh

Windows:

./ros2_run_devdocker.bat

This should result in we logging into the container named oe3036 into the working directory /workspaces/mavlab as shown below.

No arguments supplied bash assumed
mavlab@docker-desktop:/workspaces/mavlab$

If you query with the command ls on this docker container shell, you will see that the files on your operating system in the folder from which you launched this shell script (or batch script) are visible inside the docker container too. You can try exiting out of the container and moving the shell script (or batch script) to a new folder and verify that now the new folder’s contents are accessible inside the docker container.

12.5 Connecting to a running Container

Many times we might want have started a container in one shell, but might require more terminals accessing the same docker container. This is particularly required when we start using ROS2 and develop different packages that we might want to test parallelly. For such scenarios we want to gain shell access to an already running container. This can be achieved with docker exec command. While our course container is running, try connecting to this container from a new terminal by executing the following command.

docker exec -it oe3036 bash

Notice that when we perform docker exec we are connecting to a running container. Let’s now try stopping the container by typing exit on the terminal where the docker run command was executed. You should see that the terminal where you executed docker exec would also have lost access to the container. However, when we type exit on a terminal where we executed docker exec command, the contianer started from the terminal where we executed docker run is still active.

There are also ways to run containers in the background such that even when we have closed the terminal executing docker run command, the container will be running in the background. However, this functionality will not be needed for us in this course and we shall not delve into it.

Thus, now we have our course container successfully setup which can also access the files on our computer located in the directory from which we launch the shell script (or batch script). We are also able to gain access to a running container through docker exec. In the next tutorial we will learn how to maintain a version control for our code through git and GitHub.

12.5.1 IDE for Programming

We will also soon need an IDE to work with the code. VS Code has become a popular choice and is the recommended for this course tutorials. You can download VS Code from this link. However, if you are more comfortable with some other IDE, you can follow along with that IDE too.