Announcing Official Docker Images for ROS2

TL;DR: Support for ROS2 is now reflected in the Official DockerHub library! :whale:

As mentioned previously in a related post [1], work as been done in adding Docker images for ROS2 to Official Library.

With this just now up-streamed, the resulting addition of ROS2 tags are now available. To see the listing of supported suites, distros and architectures for the official DockerHub library, you can view the manifest for ROS here [2]:

In summery, multiarch (amd64, arm64v8) images for ROS2 releases (ardent, bouncy, crystal) with meta-tags (ros-core, ros-base) have been added, built from targeted Ubuntu parent images.

The latest tag was left to remain pointing to the latest ROS1 LTS available (currently melodic), presumably until a LTS distro for ROS2 is released. Tag names consisting only of the distro name istelf will continue to point to the respective ros-base meta tag, E.g:

$ docker pull ros:crystal
crystal: Pulling from library/ros:crystal-ros-base

Additionally, more meta-tags (desktop, ros1-bridge) are also available under OSRF’s own organizational ros Docker Hub repo [3].

If you find issues with the images, please be sure to ticket them here [4]:

Also don’t forget to share our official repo [5] so others might discover it!

cheers,
@ruffsl

[1] Official Docker Images for ROS2 Crystal and arm32v7
[2] official-images/library/ros at master · docker-library/official-images · GitHub
[3] Docker
[4] GitHub - osrf/docker_images: A repository to hold definitions of docker images maintained by OSRF
[5] Docker

4 Likes

As the readme for library’s repo docs hasn’t yet updated, I’ll include some relevant excerpts from the respective PR that reflect some notable changes.


Creating a Dockerfile to install ROS packages

To create your own ROS docker images and install custom packages, here’s a simple example of installing the C++, Python client library demos and security CLI using the official released Debian packages via apt-get.

FROM ros:crystal

# install ros packages for installed release
RUN apt-get update && apt-get install -y \
      ros-${ROS_DISTRO}-demo-nodes-cpp \
      ros-${ROS_DISTRO}-demo-nodes-py \
      ros-${ROS_DISTRO}-sros2 && \
    rm -rf /var/lib/apt/lists/*

# run ros package launch file
CMD ["ros2", "launch", "demo_nodes_cpp", "talker_listener.launch.py"]

Note: all ROS images include a default entrypoint that sources the ROS environment setup before exiting the configured command, in this case the demo packages launch file. You can then build and run the Docker image like so:

$ docker build -t my/ros:app .
$ docker run -it --rm my/ros:app
[INFO] [launch]: process[talker-1]: started with pid [813]
[INFO] [launch]: process[listener-2]: started with pid [814]
[INFO] [talker]: Publishing: 'Hello World: 1'
[INFO] [listener]: I heard: [Hello World: 1]
[INFO] [talker]: Publishing: 'Hello World: 2'
[INFO] [listener]: I heard: [Hello World: 2]
...

Creating a Dockerfile to build ROS packages

To create your own ROS docker images and build custom packages, here’s a simple example of installing a package’s build dependencies, compiling it from source, and installing the resulting build artifacts into a final multi-stage image layer.

FROM ros:crystal-ros-base

# install ros build tools
RUN apt-get update && apt-get install -y \
      python3-colcon-common-extensions && \
    rm -rf /var/lib/apt/lists/*

# clone ros package repo
ENV ROS_WS /opt/ros_ws
RUN mkdir -p $ROS_WS/src
WORKDIR $ROS_WS
RUN git -C src clone \
      -b master \
      https://github.com/ros2/demos.git

# install ros package dependencies
RUN apt-get update && \
    rosdep update && \
    rosdep install -y \
      --from-paths \
        src/demos/demo_nodes_cpp \
        src/demos/demo_nodes_py \
      --ignore-src && \
    rm -rf /var/lib/apt/lists/*

# build ros package source
RUN . /opt/ros/$ROS_DISTRO/setup.sh && \
    colcon build \
      --packages-select \
        demo_nodes_cpp \
        demo_nodes_py \
      --cmake-args \
        -DCMAKE_BUILD_TYPE=Release

# copy ros package install via multi-stage
FROM ros:crystal-ros-core
ENV ROS_WS /opt/ros_ws
COPY --from=0  $ROS_WS/install $ROS_WS/install

# source ros package from entrypoint
RUN sed --in-place --expression \
      '$isource "$ROS_WS/install/setup.bash"' \
      /ros_entrypoint.sh

# run ros package launch file
CMD ["ros2", "launch", "demo_nodes_cpp", "talker_listener.launch.py"]

Note: --from-paths and --packages-select are set here as so to only install the dependencies and build for the demo_nodes_cpp package, one among many in the demo git repo that was cloned. To install the dependencies and build all the packages in the source workspace, merely change the scope by setting --from-paths src/ and dropping the --packages-select arguments.

REPOSITORY    TAG                 IMAGE ID        CREATED         SIZE
my/ros        app-multi-stage     66c8112b2fb6    4 seconds ago   775MB
my/ros        app-single-stage    6b500239d0d6    2 minutes ago   797MB

For this particular package, using a multi-stage build didn’t shrink the final image by much, but for more complex applications, segmenting build setup from the runtime can help keep image sizes down. Additionally, doing so can also prepare you for releasing your package to the community, helping to reconcile dependency discrepancies you may have otherwise forgotten to declare in your package.xml manifest.

Deployment example

Docker Compose

In this example we’ll demonstrate using docker-compose to spawn a pair of message publisher and subscriber nodes in separate containers connected through shared software defined network.

Create the directory ~/ros_demos and add the first Dockerfile example from above. In the same directory, also create file docker-compose.yml with the following that runs a C++ publisher with a Python subscriber:

version: '3'

services:
  talker:
    build: .
    command: ros2 run demo_nodes_cpp talker

  listener:
    build: .
    command: ros2 run demo_nodes_py listener

Use docker-compose inside the same directory to launch our ROS nodes. Given the containers created derive from the same docker compose project, they will coexist on shared project network:

$ docker-compose up -d

Notice that a new network named ros_demos has been created, as can be shown further with:

$ docker network inspect ros_demos

We can monitor the logged output of each container, such as the listener node like so:

$ docker-compose logs listener

Finally, we can stop and remove all the relevant containers using docker-compose from the same directory:

$ docker-compose stop
$ docker-compose rm

Note: the auto-generated network, ros_demos, will persist until you explicitly remove it using docker-compose down.

Networks

For those who use more permissive network setting to share all host network interfaces with the container, such as host network driver; Be aware that this removes the networking namespace separation between containers, and can affect the ability of DDS participants communicate between containers, as exampled here.

Additionally, if shared memory transport between ROS nodes within separate containers is desired, shared memory access must be expressly conferred, as exampled here.

1 Like

Hi @ruffsl,

first of all thank you for this!

Minor comment on Creating a Dockerfile to build ROS packages:

This line fails:

RUN git -C src clone
-b $ROS_DISTRO
GitHub - ros2/demos

There is no crystal branch on ros2/demos (at least not yet).

Cloning into 'demos'...
fatal: Remote branch crystal not found in upstream origin
The command '/bin/sh -c git -C src clone       -b $ROS_DISTRO       https://github.com/ros2/demos.git' returned a non-zero code: 128

If you change to master branch works without problems. Like:

FROM ros:crystal-ros-base

# install ros build tools
RUN apt-get update && apt-get install -y \
      python3-colcon-common-extensions && \
    rm -rf /var/lib/apt/lists/*

# clone ros package repo
ENV ROS_WS /opt/ros_ws
RUN mkdir -p $ROS_WS/src
WORKDIR $ROS_WS
RUN git -C src clone \
      -b master \
      https://github.com/ros2/demos.git

# install ros package dependencies
RUN apt-get update && \
    rosdep update && \
    rosdep install -y \
      --from-paths \
        src/demos/demo_nodes_cpp \
      --ignore-src && \
    rm -rf /var/lib/apt/lists/*

# build ros package source
RUN . /opt/ros/$ROS_DISTRO/setup.sh && \
    colcon build \
      --packages-select \
        demo_nodes_cpp \
      --cmake-args \
        -DCMAKE_BUILD_TYPE=Release

# copy ros package install via multi-stage
FROM ros:crystal-ros-core
ENV ROS_WS /opt/ros_ws
COPY --from=0  $ROS_WS/install $ROS_WS/install

# source ros package from entrypoint
RUN sed --in-place --expression \
      '$isource "$ROS_WS/install/setup.bash"' \
      /ros_entrypoint.sh

CMD ["bash"]

Regards,

@LanderU

1 Like

Thanks @LanderU for checking the example, I’ve updated it with that fix.

Also, if anyone knows of a better workaround for the issues I referenced in the Networks section above, I’d appreciate insight at the answers.ros post: ROS2 connectivity across Docker containers via Host Driver.