dockerdockerfilewebots

`bash: webots: command not found` in my docker container because of multiple FROMs


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?


Solution

  • 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.