At work I am in a similar situation. We extensively work with docker images and found that ROS certainly doesn’t make it easy to have small docker images. On the larger end they could easily reach 12GiB (although a lot of that was our own negligence). Even a base install of ROS resulted in close to a 800MiB compressed image.

You’re absolutely correct that there is an inherent limitation at the moment with ROS packages always including the dev artifacts and that has given us real trouble in a few areas. One of the things we did actually find however is that nine times out of ten it isn’t actually the ROS packages themselves that are the problem - virtually all of the built debians contain just shared libraries and headers which is only going to be barely larger than a non-dev package. It’s the upstream dependencies that they hold which are consistently problematic.

As an example the ros-melodic-cpp-common deb package, which is in any core installation of ROS, has a dependency on libboost-dev. This boost metapackage is massive and has massive dependencies of its own, installing it on a fresh Ubuntu docker image installs 586MB worth of stuff even with --no-install-recommends. Some of the dependencies that get pulled in are just plain useless for actually running ROS applications:

  • gcc
  • perl
  • python3

To workaround this problem we came up with a novel solution. Before installing our ROS dependencies into our docker images we install custom, fake, metapackages. These metapackages are special in that they don’t install anything themselves, they simply “provide” (in the debian packaging sense) all of the upstream dev dependencies of which ever ROS packages we’re intending to install (plus a subset of ROS packages that we definately don’t need) without actually adding anything to the host system. That way when we later install the ROS dependencies it doesn’t actually install the dev dependencies. It turns out with this alone the size of a bare ROS docker image went from ~800MiB down to ~100MiB compressed. We’ve similarly extended this strategy to tame OpenCV and CUDA which means even our largest base images max out at about 400MiB.

The positive news is that solving this specific problem in ROS packaging more generally is probably much easier than implementing -dev packages in their entirety. In theory all that is needed is to produce -dev packages that just have extra -dev dependencies of their own, no extra files.

To a degree this should be straight forward as package.xml already has a mechanism for declaring these dev only transitive dependencies: build_export_depend. The downside of this approach is that it involves fixing dependencies on basically every package in the ROS ecosystem since that’s mainly used to export headers at the moment.

A hackier approach would be on the rosdep side. At the moment the boost rosdep key is declared as being the dev packages for every given OS: https://github.com/ros/rosdistro/blob/master/rosdep/base.yaml#L272. It should be possible to create a “parallel” rosdep file that declares the non-dev versions of packages which can then be automatically used by bloom to generate runtime packages with the non-dev dependencies.

The final approach is as @tfoote has suggested, explicitly create ‘-dev’ ROS packages. The downside of this approach is legacy, there is a substantial amount of legwork to implement this approach and tons of breaking changes to go with it. It also seems overkill since ROS already has most of the mechanisms in place to resolve this on the release side.

2 Likes