Containerizing ROS Melodic with LXD on Ubuntu 18.04

The Robot Operating System (ROS) may be the most capable open starting point for building serious robotics applications. However for beginners it can be a challenge to set up, and it is easy to make a mess of the system without any fast way to undo mistakes and return to a working state. Automated systems for testing and deployment can run into similar issues when dealing with a living system. For these reasons it can be advantageous to set up ROS to run in a container which is isolated from the host system and can be easily spawned for experiments and destroyed when no longer needed.

Docker is one tool that can be used to containerize ROS and there are many existing examples of it being used for that. But Docker is not ideal for isolating ROS during development. Docker aims to containerize applications rather than systems. It is not designed to be used like a virtual machine which a user logs into and uses like a normal interactive system.

Linux Containers, however, "offer an environment as close as possible as the one you'd get from a VM but without the overhead that comes with running a separate kernel and simulating all the hardware." That makes them perfect for our purposes.

To make it happen all we have to do is use a tool called LXD to create a container with Ubuntu 18.04 installed and that is configured to let us run GUI applications (such as RViz). Then we can log in and install ROS Melodic as usual. Once everything is set up and working we then export ("publish", in LXD terminology) a container image which we can use to create fresh ROS Melodic systems whenever we need to.

Here is what it takes to set up the bare container:

# First we install LXD if necessary
# If you use the apt package there are additional steps not covered here
snap install lxd

# Add the current user to the LXD group to allow lxd to be run unprivileged
sudo usermod --append --groups lxd $USER

# Reset the user's GID so it is not necessary to log out and then back in
newgrp lxd

# This initial configuration only needs to be done once after installing LXD
lxd init

# Create a profile for containers which are configured to run GUI applications
lxc profile create gui
curl https://gist.githubusercontent.com/jmptable/8a3669cb78c5c1c99d7d8524ce019300/raw/e7abcbde7f6561f320a30ae247fa6c7f76f606e4/lxd-gui-profile.yaml | lxc profile edit gui

# Launch (create) a new Ubuntu 18.04 container for our ROS installation
lxc launch --profile default --profile gui ubuntu:18.04 ros-dev

# Log into the new container in order to install ROS
lxc exec ros-dev -- sudo --user ubuntu --login

The "gui" profile created here configures the environment so that the container can connect to the X server on the host, which allows us to use graphical programs like RViz from within the container. That involves giving access to the Unix domain socket which X uses to communicate with clients on the same machine, setting the DISPLAY environment variable (specifies which X server to connect to). It also makes the host's GPU accessible from the guest which allows GPU-accelerated programs to make full use of that hardware.

One detail that took some investigation on my part to get working was this line:

raw.idmap: both 1000 1000

It maps the user and group ID of 1000 on the host to 1000 in the guest. As long as your user ID is 1000 this will allow the guest to access the X Unix domain socket file. But if your user ID happens to be something else then this will not work and you will need to change the first number to your UID. For example, mine happened to be 1001 when I was testing. So I would use:

raw.idmap: both 1001 1000

Once the new container is created and you are logged in, install ROS Melodic the usual way:

Finally, log out of the container, stop it, and publish the container image ("publish" makes it sound like the image is going somewhere public, but that's not the case):

lxc stop ros-dev
lxc publish ros-dev --alias ros-melodic

Then whenever you want to, launch a new container from your saved image:

lxc launch ros-melodic ros-dev2

References