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.