1

I have a docker container that has Webots and ROS2 installed. However, running webots while inside the container returns bash: webots: command not found. Why?

Container that does run webots (but no ROS2)

Here's a container run from the Webots installation instructions that DOES successfully run webots (but lacks ROS2 like I need):

$ xhost +local:root > /dev/null 2>&1 #so webots won't say unable to load Qt platform plugin "xcb"
$ docker run -it -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix:rw cyberbotics/webots:R2021a-ubuntu20.04

Container that does NOT run webots

Here's my docker container which does NOT successfully run webots, but instead says bash: webots: command not found. However, it DOES successfully run webots_ros2 demos (I think the issue has to do with how I'm inheriting from two containers, because if I swap the order of my two ARG and FROM statements, webots is found but ros2 is not. I'm not sure the solution though):

Dockerfile

# inherit both the ROS2 and Webots containers
ARG BASE_IMAGE_WEBOTS=cyberbotics/webots:R2021a-ubuntu20.04
ARG IMAGE_ROS2=niurover/ros2_foxy:latest

FROM $BASE_IMAGE_WEBOTS AS base
FROM $IMAGE_ROS2 AS image_ros2

# resolve a missing dependency for webots demo
RUN apt-get update && apt-get install -y \
        libxtst6 \
        && apt-get clean \
        && rm -rf /var/lib/apt/lists/*

# Finally open a bash command to let the user interact
CMD ["/bin/bash"]

launch.sh (used to launch docker container)

#! /bin/bash
 
CONTAINER_USER=$USER
CONTAINER_NAME=webots_ros2_foxy
USER_ID=$UID
IMAGE=niurover/webots_ros2_foxy:latest
if [ $(uname -r | sed -n 's/.*\( *Microsoft *\).*/\1/ip') ];
then
    xhost +local:$CONTAINER_USER
    xhost +local:root
fi

sudo docker run -it --rm \
    --name $CONTAINER_NAME \
    --user=$USER_ID\
    --env="DISPLAY" \
    --env="CONTAINER_NAME=$CONTAINER_NAME" \
    --workdir="/home/$CONTAINER_USER" \
    --volume="/home/$CONTAINER_USER:/home/$CONTAINER_USER" \
    --volume="/etc/group:/etc/group:ro" \
    --volume="/etc/passwd:/etc/passwd:ro" \
    --volume="/etc/shadow:/etc/shadow:ro" \
    --volume="/etc/sudoers.d:/etc/sudoers.d:ro" \
    --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
    $IMAGE bash\

if [ $(uname -r | sed -n 's/.*\( *Microsoft *\).*/\1/ip') ];
then
    xhost -local:$CONTAINER_USER
    xhost -local:root
fi

Summary

As you can see, both containers use cyberbotics/webots:R2021a-ubuntu20.04, and the second container uses all of the options of the first container, but with some extras. Why does the first container run webots successfully, while the second container can't find the command?

Drake P
  • 174
  • 9
  • Did you set the `WEBOTS_HOME` environment variable? Did you try to launch webots using `/usr/local/webots/webots`? – Olivier Michel Mar 24 '21 at 07:38
  • @OlivierMichel `/usr/local/webots/` no such file or directory, and `export WEBOTS_HOME=/usr/local/webots` doesn't help much as a result – Drake P Mar 26 '21 at 00:07
  • I think the issue has to do with how I'm inheriting from two containers. If I swap the order of my two `ARG` and `FROM` statements, `webots` is found but `ros2` is not. I'll add this info to the post. – Drake P Mar 26 '21 at 00:32

2 Answers2

1

I ended up using Leonardo Dagnino's suggestion, and it worked. I had to copy a couple successive ROS2 containers' contents to make the tree hierarchy work off of the webots base image, but it got me where I was going. For prosperity, here is the new Dockerfile in full:

# Use Webots docker container as base
ARG BASE_IMAGE_WEBOTS=cyberbotics/webots:R2021a-ubuntu20.04
FROM $BASE_IMAGE_WEBOTS AS base

# ==================================================================================
# niurover/ros2_foxy uses osrf/ros:foxy-desktop as its base, so I need to add code from
# container heirarchy all the way back to where it can stem off of `base` from above
# ==================================================================================

# ----------------------------------------------------------------------------------
# taken from Dockerfile for ros:foxy-ros-core-focal found at:
# https://github.com/osrf/docker_images/blob/master/ros/foxy/ubuntu/focal/ros-core/Dockerfile
# ----------------------------------------------------------------------------------

## setup timezone   # NOTE commented out since timezone should already be set up
#RUN echo 'Etc/UTC' > /etc/timezone && \
#    ln -s /usr/share/zoneinfo/Etc/UTC /etc/localtime && \
#    apt-get update && \
#    apt-get install -q -y --no-install-recommends tzdata && \
#    rm -rf /var/lib/apt/lists/*

# install packages
RUN apt-get update && apt-get install -q -y --no-install-recommends \
    dirmngr \
    gnupg2 \
    && rm -rf /var/lib/apt/lists/*

# setup keys
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654

# setup sources.list
RUN echo "deb http://packages.ros.org/ros2/ubuntu focal main" > /etc/apt/sources.list.d/ros2-latest.list

# setup environment
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8

ENV ROS_DISTRO foxy

# install ros2 packages
RUN apt-get update && apt-get install -y --no-install-recommends \
    ros-foxy-ros-core=0.9.2-1* \
    && rm -rf /var/lib/apt/lists/*

## setup entrypoint # NOTE ignore this part of their Dockerfile
#COPY ./ros_entrypoint.sh /
#
#ENTRYPOINT ["/ros_entrypoint.sh"]
#CMD ["bash"]

# ----------------------------------------------------------------------------------
# taken from Dockerfile for ros:foxy-ros-base-focal found at:
# https://github.com/osrf/docker_images/blob/master/ros/foxy/ubuntu/focal/ros-base/Dockerfile
# ----------------------------------------------------------------------------------

# install bootstrap tools
RUN apt-get update && apt-get install --no-install-recommends -y \
    build-essential \
    git \
    python3-colcon-common-extensions \
    python3-colcon-mixin \
    python3-rosdep \
    python3-vcstool \
    && rm -rf /var/lib/apt/lists/*

# bootstrap rosdep
RUN rosdep init && \
  rosdep update --rosdistro $ROS_DISTRO

# setup colcon mixin and metadata
RUN colcon mixin add default \
      https://raw.githubusercontent.com/colcon/colcon-mixin-repository/master/index.yaml && \
    colcon mixin update && \
    colcon metadata add default \
      https://raw.githubusercontent.com/colcon/colcon-metadata-repository/master/index.yaml && \
    colcon metadata update

# install ros2 packages
RUN apt-get update && apt-get install -y --no-install-recommends \
    ros-foxy-ros-base=0.9.2-1* \
    && rm -rf /var/lib/apt/lists/*

# ----------------------------------------------------------------------------------
# taken from Dockerfile for osrf/ros:foxy-desktop-focal (or is it osrf/ros:foxy-desktop?) found at:
# https://github.com/osrf/docker_images/blob/master/ros/foxy/ubuntu/focal/desktop/Dockerfile 
# ----------------------------------------------------------------------------------
  
# This is an auto generated Dockerfile for ros:desktop
# generated from docker_images_ros2/create_ros_image.Dockerfile.em
#FROM ros:foxy-ros-base-focal   # NOTE commented out since satisfied by above

# install ros2 packages
RUN apt-get update && apt-get install -y --no-install-recommends \
    ros-foxy-desktop=0.9.2-1* \
    && rm -rf /var/lib/apt/lists/*

# ----------------------------------------------------------------------------------
# taken from Dockerfile for niurover/ros2_foxy found at:
# https://github.com/NIURoverTeam/Dockerfiles/blob/master/ros2_foxy/Dockerfile
# ----------------------------------------------------------------------------------
#ARG BASE_IMAGE=osrf/ros:foxy-desktop   # NOTE commented out since satisfied by above

# Install work packages
#FROM $BASE_IMAGE as base   # NOTE commented out since satisfied by above
RUN apt-get update && apt-get upgrade -y && apt-get install -y \
        tmux \
        curl \
        wget \
        vim \
        sudo \
        unzip \
        python3-pip \
        && apt-get clean \
        && rm -rf /var/lib/apt/lists/*

# Install ROS Packages
RUN apt-get update && apt-get install -y \
        ros-foxy-turtlesim \
        ~nros-foxy-rqt* \
        ros-foxy-teleop-tools \
        ros-foxy-joy-linux \
        && apt-get clean \
        && rm -rf /var/lib/apt/lists/*

RUN pip3 install pyserial

#CMD ["bash"]   # NOTE ignore this part of the Dockerfile

# ----------------------------------------------------------------------------------
# new stuff added on top of niurover/ros2_foxy to assist with Webots + ROS2
# ----------------------------------------------------------------------------------

# resolve a missing dependency for webots demo
RUN apt-get update && apt-get install -y \
        libxtst6 \
        && apt-get clean \
        && rm -rf /var/lib/apt/lists/*

# Finally open a bash command to let the user interact
CMD ["/bin/bash"]
Drake P
  • 174
  • 9
0

When you have multiple FROM commands, you're not "inheriting" both of their contents into the same image - you're doing a multi-stage build. This allows you to COPY from that stage specifying the --from option. By default, the last stage in your Dockerfile will be the target (so, in your example, you're only actually using the ros2 image. The webots image is not actually being used there.

You have two options here:

  1. Copy just the files you need from the webots image using COPY --from=base

This will probably be hard and finicky. You'll need to copy all dependencies; and if they're acquired through your package manager (apt-get), you'll leave dpkg's local database inconsistent.

  1. Copy one of the Dockerfiles and change their FROM

This will probably work fine as long as they both use the same base distribution. You can go into one of the project's repositories and grab their Dockerfile, rebuilding it from the other image - just change, for example, cyberbotics/webots:R2021a-ubuntu20.04's Dockerfile to have FROM niurover/ros2_foxy:latest. It may require tinkering with the other commands there, though.

Leonardo Dagnino
  • 2,914
  • 7
  • 28
  • Thank you for the comment. I agree option 1 seems finicky. As for option 2, is there a way to not duplicate code? I do have the Dockerfile for niurover/ros2_foxy, but I'm reluctant to just copy paste the contents and make maintainability issues for myself. Is there some way to avoid this? Maybe docker compose or some bash trickery (modifying ARG)? – Drake P Mar 26 '21 at 03:45
  • Honestly I can't think of a good way. You could do something like `sed s/FROM /FROM cyberbotics/webots:R2021a-ubuntu20.04/`, but that doesn't include any other changes you may have to make besides just changing the base image. Just copying the dockerfile looks like the least painful option to me. – Leonardo Dagnino Mar 26 '21 at 17:42
  • I ended up going with your option 2 and it worked. I had to go a bit up the ROS2 dependency tree to get the base distributions to line up, but it got the job done. If you'd like to see the full Dockerfile, I put it in a separate answer and accepted yours. Thanks for the help! – Drake P Mar 27 '21 at 22:23