`bash: webots: command not found` 在我的 docker 容器中,因为有多个 FROM

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

我有一个 docker 容器,其中安装了 Webots 和 ROS2。但是,运行宁 webots 而在容器内 returns bash: webots: command not found。为什么?

运行 webots 的容器(但没有 ROS2)

这是来自 Webots installation instructions 的容器 运行 DOES 成功 运行 webots(但缺少我需要的 ROS2 ):

$ 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

容器不 运行 webots

这是我的 docker 容器,它 NOT 成功 运行 webots,而是 bash: webots: command not found。但是,它确实成功 运行 webots_ros2 demos(我认为这个问题与我如何从两个容器继承有关,因为如果我交换我的两个 ARGFROM 语句,找到了 webots 但没有找到 ros2。不过我不确定解决方案):

Docker 文件

# 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(用于启动 docker 容器)

#! /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 *\).*//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 *\).*//ip') ];
then
    xhost -local:$CONTAINER_USER
    xhost -local:root
fi

总结

如您所见,两个容器都使用 cyberbotics/webots:R2021a-ubuntu20.04,第二个容器使用第一个容器的所有选项,但有一些额外的选项。为什么第一个容器运行webots成功,而第二个容器找不到命令?

当您有多个 FROM 命令时,您并不是将它们的两个内容“继承”到同一个图像中——您是在执行 multi-stage build。这允许您从指定 --from 选项的那个阶段 COPY。默认情况下,Dockerfile 中的最后一个阶段将是目标(因此,在您的示例中,您实际上只是在使用 ros2 图像。webots 图像实际上并未在那里使用。

这里有两个选择:

  1. 使用 COPY --from=base
  2. 从 webots 图像中仅复制您需要的文件

这可能会很困难和挑剔。您需要复制所有依赖项;如果它们是通过您的包管理器 (apt-get) 获取的,您将使 dpkg 的本地数据库不一致。

  1. 复制其中一个 Dockerfile 并更改它们的 FROM

只要它们都使用相同的基础分布,这可能会很好地工作。您可以进入项目的一个存储库并获取它们的 Dockerfile,从另一个图像重建它 - 例如,只需将 cyberbotics/webots:R2021a-ubuntu20.04 的 Dockerfile 更改为 FROM niurover/ros2_foxy:latest。不过,它可能需要修改那里的其他命令。

我最终采纳了 Leonardo Dagnino 的建议,并且奏效了。我不得不复制几个连续的 ROS2 容器的内容以使树层次结构从 webots 基本图像中工作,但它让我到达了我要去的地方。为了繁荣,这里是完整的新 Dockerfile:

# 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"]