Docker自动安装、启动、接入、结束ros-melodic-desktop-full的脚本

脚本

#! /bin/bash

# A script to start ros:melodic-desktop-full in docker.
# You need to install the latest docker first.
# Author: liuqixuan.cn
# Email: [email protected]

# set -x
RES_NAME=ros
TAG=melodic-desktop-full

if [[ -z `which docker` ]]
then
    echo "Can't find docker. You need to install the latest docker first."
    exit
fi

if [[ `docker images $RES_NAME:$TAG | grep $TAG | wc -l` == 0 ]]
then
    echo "Docker image $RES_NAME:$TAG not exist!"
    echo
    echo "Do you want to setup docker image now? It will take about 20 minutes and 3GB disk space."
    read -t 20 -p "(wait 20 sec) Enter your choce: [y/N]" choice
    choice=${choice:-n} # Default is No
    if `echo $choice | grep -qi n`; then echo -e "\033[1m\033[32mAbort.\033[0m" && exit; fi # Uniform cases through grep
    echo
    dir="$(mktemp -d)"
    script="$dir/Dockerfile"
    if [[ ! (-f "$script" && -s "$script") ]]
    then
        echo -e "\033[1m\033[32mWrite script to\033[0m $script"
        cat>"$script"<<EOF
# FROM ubuntu:18.04
FROM nvidia/cudagl:10.2-base-ubuntu18.04
LABEL Description="A Dockerfile to setup $RES_NAME:$TAG" Author="liuqixuan" Version="1.0"
# Official sources.list
ARG sourcelist="http://packages.ros.org/ros/ubuntu"
# Mirrors sources.list
# ARG sourcelist="http://mirrors.ustc.edu.cn/ros/ubuntu/"
# ARG sourcelist="http://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/"
# ARG sourcelist="http://mirrors.sjtug.sjtu.edu.cn/ros/ubuntu/"
ARG workdir="/root/catkin_ws"
ARG DEBIAN_FRONTEND=noninteractive
ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=YES
ENV XDG_RUNTIME_DIR="/tmp/runtime-root"
RUN set -x \
    && apt-get update \
    && apt-get install -y --no-install-recommends gnupg2 curl vim git xarclock \
    && sh -c '. /etc/lsb-release && echo "deb \${sourcelist} \${DISTRIB_CODENAME} main" > /etc/apt/sources.list.d/ros-latest.list' \
    && curl -sSL 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xC1CF6E31E6BADE8868B172B4F42ED6FBAB17C654' | apt-key add - \
    && apt-get update \
    && apt-get install -y --no-install-recommends ros-melodic-desktop-full \
    && echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc \
    && bash -c '. ~/.bashrc' \
    && apt-get install -y --no-install-recommends python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential \
    && rosdep init \
    && rosdep update \
    && apt-get autoremove -y \
    && rm -rf /var/lib/apt/lists/* \
 	&& mkdir \${XDG_RUNTIME_DIR} && chmod 700 \${XDG_RUNTIME_DIR} \
 	&& mkdir \${workdir} \
    && set +x
WORKDIR \${workdir}
ENTRYPOINT ["/bin/bash"]
EOF
    fi
    echo -e "\033[1m\033[32m docker build -f $script -t $RES_NAME:$TAG --network host $dir\033[0m"
    docker build -f "$script" -t $RES_NAME:$TAG --network host "$dir"
    if [ $? == 0 ]
    then
        echo -e "\033[1m\033[32m\nBuild finished!\n\033[0m"
    else
        echo -e "\033[1m\033[31m\nBuild failed!\033[0m"
        echo "Most problems are caused by network connection, please check your network on host machine, then try again."
        exit 1
    fi
    rm -rf "$dir"
fi

# Enabel XServer
echo -e "\033[32m xhost +local:\033[0m"
xhost +local:docker

# Starting docker containers
CONTAINER_NAME=$RES_NAME
STATUS=$(docker ps -a --format "{
     
     {.Names}}\t{
     
     {.Status}}" | grep -m 1 $CONTAINER_NAME$'\t' | awk '{print $2}')
ENV="DISPLAY=$DISPLAY"
MOUNT_FROM="/tmp/.X11-unix/"
MOUNT_TO="/tmp/.X11-unix"

if [[ $1 == "-c" ]]
then
    echo -e "\033[32m docker stop $CONTAINER_NAME\033[0m"
    docker stop $CONTAINER_NAME
    echo -e "\033[32m docker rm $CONTAINER_NAME\033[0m"
    docker rm $CONTAINER_NAME
    exit
fi

if [ -z $STATUS ]
then
    CMD="docker run -it --gpus all --name $CONTAINER_NAME"
    if [ -n $ENV ]; then CMD=$CMD" -e $ENV"; fi
    if [[ -n $MOUNT_FROM && -n $MOUNT_TO ]]; then CMD=$CMD" -v $MOUNT_FROM:$MOUNT_TO"; fi
    CMD=$CMD" $RES_NAME:$TAG"
    echo -e "\033[32m $CMD\033[0m"
    $CMD
elif [ $STATUS == "Up" ]
then
    echo -e "\033[33m Attaching to $CONTAINER_NAME \033[0m"
    docker exec -it $CONTAINER_NAME bash
elif [ $STATUS == "Exited" ]
then
    echo -e "\033[32m docker restart $CONTAINER_NAME \033[0m"
    docker restart $CONTAINER_NAME
    echo -e "\033[32m docker exec -it $CONTAINER_NAME bash \033[0m"
    docker exec -it $CONTAINER_NAME bash
else
    echo -e "\033[31m Unknown $CONTAINER_NAME Status: $STATUS \033[0m"
fi

使用说明

在本地PATH下(如~/.local/bin)新建文件ros-docker-run并粘贴以上代码,并使用chmod 755 ros-docker-run添加可执行权限。
在使用ros-docker-run执行脚本后,会自动构建ros镜像并启动容器,如果之前已经构建或启动容器,会重新接入到容器中。
使用ros-docker-run -c会清空并删除容器,所有的修改都会失效,一定要先用docker commit保存修改

脚本解析

首先检查宿主机上有没有安装docker,如果没有则退出。
然后检查是不是已经用本脚本构建过ros镜像,如果还没创建,则会在/tmp中生成一个临时目录dir="$(mktemp -d)",然后将Dockerfile的内容输出到该目录下script="$dir/Dockerfile",并用docker build -f "$script" -t $RES_NAME:$TAG --network host "$dir"构建镜像,镜像名为$RES_NAME:$TAG,本代码中为ros:melodic-desktop-full

对于常见的执行rosdep init因为连不上raw.githubusercontent.com而报错,一开始我是在Dockerfile中添加了修改hosts的语句,后来改成build命令中添加--network host选项。这样,只要宿主机能访问的地址,镜像应该就能访问。
所以可以通过修改宿主机的/etc/hosts文件,添加199.232.96.133 raw.githubusercontent.com来解决此问题。
其它网络问题的解决方法类似。

然后是启动阶段。
首先让宿主机的XServer接收来自本机容器的输出:xhost +local:
然后检查容器当前状态,如果状态为空,说明没有启动。使用$CMD启动容器。

CMD="docker run -it --gpus all --name $CONTAINER_NAME" -e $ENV -v $MOUNT_FROM:$MOUNT_TO $RES_NAME:$TAG

  • --gpus all:启用Nvidia GPU支持
  • --name $CONTAINER_NAME":设置$CONTAINER_NAME方便后续检查容器状态
  • ENV="DISPLAY=$DISPLAY":设置显示器ID
  • $MOUNT_FROM:$MOUNT_TO:设置x11接口

踩坑注意:

  • 如果添加了--network=host选项,在容器内运行rosrun rviz rviz等需要GUI的node时,会报如下错误:
    dbus[XXX]: The last reference on a connection was dropped without closing the connection. This is a bug in an application. See dbus_connection_unref() documentation for details.
  • 如果一定要添加--network=host选项,则需要同时添加--priviledged选项才能正常显示GUI,但这可能导致宿主机系统文件受到威胁,不建议这么做。

如果容器正在运行(Up),则使用docker exec -it $CONTAINER_NAME bash接入容器,相当于新开一个终端窗口。
如果容器已经停止(Exited),则将其重启docker restart $CONTAINER_NAME并接入。
对于其它不常见状态,请自行处置。

Dockerfile解析

下面简单介绍Dockerfile中的内容(脚本第33行至64行部分)。
一开始我是用FROM ubuntu:18.04来构建的,后来发现gazebo等组件还是需要nvidia驱动支持,于是把基础镜像改成了FROM nvidia/cudagl:10.2-base-ubuntu18.04
然后是ROS源,默认使用官方源,如果网络不行,可以自行取消后面其它镜像源的注释以替代官方源。
然后是默认工作目录/root/catkin_ws
然后是禁止apt的交互行为与警告语句。
然后设置XDG_RUNTIME_DIR的目的是防止使用GUI时一直出QStandardPaths: XDG_RUNTIME_DIR not set的Warning。(参考我另一篇博客
然后是apt安装的工具。其中,gnupg2curl是安装apt-key必需的,剩下的vim、git、xarclock是常用工具,xarclock可以测试GUI显示功能。
接下来就是安装ros-melodic-desktop-full本体与各种第三方库的部分。
然后是构建ROS。
然后是删除各种缓存。
然后是创建XDG_RUNTIME_DIR并设置权限。
最后切换到工作目录,并设置用户载入镜像的默认进入点为/bin/bash

猜你喜欢

转载自blog.csdn.net/liuqixuan1994/article/details/114089786