3. Docker and Containerisation¶
This tutorial gives a brief introduction to a key element of Starling - containerisation. By the end you will hopefully have an idea of what containerisation is, what docker is, how to use it, and how we use it within Starling.
This is adapted from the Duckietown Docker Docs, Docker Docs tutorial, and a number of other resources.
3.1 Introduction¶
3.1.1 What is Containerisation and Docker¶
It would be nice to give a computer - any computer with an internet connection - a short string of ASCII characters (say via a keyboard), press enter, and return to see some program running. Forget about where the program was built or what software you happened to be running at the time (this can be checked, and we can fetch the necessary dependencies). Sounds simple, right? In fact, this is an engineering task that has taken thousands of the world’s brightest developers many decades to implement.
Thanks to the magic of container technology we now can run any Linux program on almost any networked device on the planet, as is. All of the environment preparation, installation and configuration steps can be automated from start to finish. Depending on how much network bandwidth you have, it might take a while, but that’s all right. All you need to do is type the string correctly.
Docker is one very widely used example of containerisation technology, and the one we make use of in Starling. They provide a large number of tools and programs to help us contain, develop, test and deploy our containers to the real world.
If you followed the getting started, you should hopefully have done the full docker install. If not, you can run the following command from a linux command line to install basic docker.
curl -sSL https://get.docker.com/ | sh
3.1.2 Docker Concepts in more detail¶
Adapted from Docker Resources
A container is a standard unit of software that packages up code and all its dependencies so the application runs quickly and reliably from one computing environment to another. A Docker container image is a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries and settings.
Container images become containers at runtime and in the case of Docker containers – images become containers when they run on Docker Engine. Available for both Linux and Windows-based applications, containerized software will always run the same, regardless of the infrastructure. Containers isolate software from its environment and ensure that it works uniformly despite differences for instance between development and staging.
Containers are Standard (can run anywhere), Lightweight (Share low level machine system and not the whole Operating System) and Secure (Each application is as isolated as possible). For us this also translates to providing Reproduceable and Reusable systems.
On the left, Containers are an abstraction at the app layer that packages code and dependencies together. Multiple containers can run on the same machine and share the OS kernel with other containers, each running as isolated processes in user space. Containers take up less space than VMs (container images are typically tens of MBs in size), can handle more applications and require fewer VMs and Operating systems.
On the right, Virtual machines (VMs) are an abstraction of physical hardware turning one server into many servers. The hypervisor allows multiple VMs to run on a single machine. Each VM includes a full copy of an operating system, the application, necessary binaries and libraries – taking up tens of GBs. VMs can also be slow to boot.
3.1.3 Starling Container Ecosystem¶
The purpose of Starling is to allow you to quickly and easily install and run a UAV simulation within a simulated environment, so that you can test your developed controllers against a semi-realistic scenario, to then test in the real world
Therefore Starling is a set of pre-built programs/executables, some of which are pre-configured for the following:
- Running a Physics Simulation with Visualisation
- Running the Drone autopilot control software locally (a.k.a Software In The Loop or SITL)
- Running the interface between Mavlink and other protocols such as the Robot Operating System (ROS)
- And many others...
These pre-built containers are all available in the StarlingUAS repository on github and on Docker Hub.
Together these containers form a modular ecosystem of drone systems which can be composed together to develop software for real drones. Any controllers developed via the simulator can be directly ported to run on a real drone.
3.2 Using Docker with Starling¶
3.2.1 Getting and Running Containers¶
Every docker container is registered to a developer or organisation. In Starling, our docker organisation is known as uobflightlabstarling
. Within our organisation, we have a large number of Docker containers available. These Docker containers live inside container registries (such as DockerHub), which are servers that host Docker images. A Docker image is one particular version or snapshot of a container and is basically a filesystem snapshot - a single file that contains everything you need to run our container.
You can manually fetch one of our core containers called starling-mavros
from docker hub using:
docker pull uobflightlabstarling/starling-mavros
You can also try and pull one of our simulation containers:
docker pull uobflightlabstarling/starling-sim-iris-px4-flightarena:latest
This might take a few minutes to download depending on internet connection (some containers like the simulation can be quite big!). Once downloaded, to see a list of Docker images on your machine, run:
docker images
Every image has an image ID, a name and a tag
REPOSITORY TAG IMAGE ID CREATED SIZE
uobflightlabstarling/starling-sim-iris-px4-flightarena latest 62d7f96637cf 3 weeks ago 5.97GB
uobflightlabstarling/starling-mavros latest b70812c16731 5 months ago 2.06GB
To run a Docker container, type the repository name, like so:
docker run uobflightlabstarling/starling-mavros
# Or with the tag if you want to run a specific tag (version) of that container
docker run uobflightlabstarling/starling-mavros:latest
In another terminal, you can see what is currently running using docker ps
:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4fd1e0948f23 uobflightlabstarling/starling-mavros "/ros_entrypoint.sh …" 46 seconds ago Up 45 seconds vigilant_nobel
Note how your the running container has a container ID, a base image you ran, and at the end, a funny name vigilant_noble. This funny name is an alias for the container ID.
To stop the container, simply press ctrl+c
in the terminal which you ran docker run
.
As a second example, you can similarly try and run the simulator, this time also specifying a port mapping to let you see the simulator in your web-browser.
docker run -p 8080:8080 uobflightlabstarling/starling-sim-iris-px4-flightarena
Then you can navigate to localhost:8080
in your web browser to see the simulator. You should hopefully see something like the following:
You can use the cursor to move around the environment, we will be coming back to the simulator in a later section.
To stop the simulator, you can try and use ctrl+c
, but sometimes this doenst work. Another way is to first get the container ID or name like before docker ps
:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6a4bd538118c uobflightlabstarling/starling-sim-iris-px4-flightarena "/entrypoint.sh ros2…" 2 minutes ago Up 2 minutes 7681/tcp, 11345/tcp, 0.0.0.0:8080->8080/tcp trusting_diffie
See how the name is trusting_diffie
with ID 6a4bd538118c
.
You can then explicitly stop the container by running (it can sometimes take a minute).
docker stop trusting_diffie
# or
docker stop 6a4bd538118c
Dont forget to also remove the container afterwards
docker rm trusting_diffie
# or
docker rm 6a4bd538118c
3.2.2 Creating Containers¶
To create a Docker image we write a recipe, called a Dockerfile. A Dockerfile is a text file that specifies the commands required to create a Docker image, typically by modifying an existing container image using a scripting interface. They also have special keywords (which are always CAPITALIZED), like FROM
, RUN
, ENTRYPOINT
and so on. For example, create a file called Dockerfile
with the following content:
FROM ros:foxy # Defines the base image
RUN touch new_file1 # new_file1 will be part of our snapshot
CMD ls -l # Default command to be run when the container is started
Now, to build the image we can simply run:
docker build -t your/duck:v3 . # Where '.' is the directory containing your Dockerfile
You should see something like:
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM uobflightlabstarling/starling-mavros
--- ea2f90g8de9e
Step 2/3 : RUN touch new_file1
--- e3b75gt9zyc4
Step 3/3 : CMD ls -l
--- Running in 14f834yud59
Removing intermediate container 14f834yud59
--- 05a3bd381fc2
Successfully built 05a3bd381fc2
Successfully tagged your/duck:v3
Now run the command docker images in your terminal, and you should see an image called your/duck with tag v3:
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
your/duck v3 ea2f90g8de9e 1 minute ago 2.06GB
uobflightlabstarling/starling-sim-iris-px4-flightarena latest 62d7f96637cf 3 weeks ago 5.97GB
uobflightlabstarling/starling-mavros latest b70812c16731 5 months ago 2.06GB
This procedure is identical to the snapshot method we performed earlier, but the result is much cleaner. Now, instead of needing to carry around a 2.06GB BLOB, we can just store the 4KB text file and rest assured that all our important setup commands are contained within. Similar to before, we can simply run:
docker run -it your/duck:v3
total 0
-rw-r--r-- 1 root root 0 May 21 21:35 new_file1
Notice that as soon as we run the container, Docker will execute the ls -l command as specified by the Dockerfile, revealing new_file1 was stored in the image. However we can still override ls -l by passing a command line argument:
docker run -it your/duck:v3 [custom command]
In the next tutorial, we will go into more detail in how these Dockerfiles are constructed for us.
3.2.3 Layer Caching¶
An important concept in Docker is the layers. In the previous section you may think that every time we build, we end up having to copy over the entire parent container. e.g. your/duck:v3 takes up another 2Gb of storage space! In actual fact, it (thankfully) does not, because under the hood the executable does not exist as one giant individible binary. It is in fact split into multiple independnet layers which can be shared in between images!
Essentially each
RUN
line in your Dockerfile is compiled into a new layer placed upon the previous layers.
This is helpful as if you try to build your container again, unless you change something, those previous layers are cached by Docker to be used instead of rebuilding the entire thing from scratch!
3.2.4 Inspecting a container¶
One of the downsides of containers is that manipulating files and inspecting their state is not as simple. Previously, you could just browser through your own file system and check things. Now that a container has its own file system, its not as clear how you could check things have been set up correctly, or test run commands manually or similar.
There are a number of different ways, but the simplest way is to exec
into a running container. As an example, you can run starling-mavros
again in one terminal.
docker run uobflightlabstarling/starling-mavros
In another terminal, identify the container ID or name by using docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
81b7dfb6c443 uobflightlabstarling/starling-mavros "/ros_entrypoint.sh …" 3 seconds ago Up 2 seconds angry_yalow
Note: The IDs have changed from the previous time we ran this container. Sometimes you want containers to persist, or do not want to delete them for testing and such.
We can then use the ID of 81b7dfb6c443
or its name angry_yalow
to exec
into a container to run a command:
docker exec -it angry_yalow [command]
For example, in most cases it will be most useful to open up a bash
terminal to inspect contents:
docker exec -it angry_yalow bash
root@81b7dfb6c443:/ros_ws#
This opens up a terminal inside the container for you to navigate around and inspect things as you wish, in a similar manner to if you SSH'd into another machine.
Note: Use the
cat
command to view the contents of files.Note: These containers have almost no tools to keep them slim. If you want to edit things, you will need to download a command line file editor. Run
apt-get update
then install an editor likenano
withapt-get install nano
.
3.3 Starling Mavros¶
Finally, we bring in ros2 and uav control from the previous tutorial. In Starling, there exists a core container called starling-mavros
which facilitates the communication between the user application and the UAV autopilot in simulation or reality.
This container, which you have hopefully run above, uses a Mavros node to translate between ROS2 for the user, and MAVLINK for the autopilot.
We give examples of its use later on.
3.4 Next Steps¶
Hopefully you now have a decent understanding of what containerisation is and its purpose within Starling. You have also had a go at using the Docker command line tool to pull, run, build and inspect Starling containers going forward.
With all that, we are now at a point where you can start creating your own containers to use!