Это в общем-то первая статья на хабре, пробная и экспериментальная. Цель статьи изложить процесс создания темплейта под разработку для ROS (Robot Operating System) внутри контейнера и сделать это в шутливой манере.
Началось с того, что мне потребовалось установить контейнер с cuda. Все готовые контейнеры с ROS и cuda на докерхабе имели либо проблемы со стартом, либо имели битый пакетный менеджер. Я бы хотел сделать его несколько универсальным, чтобы адаптировать к любым своим проектам.
Dockerfile, собирали всем селом
Конечно, каждый хотел бы схалтурить и взять уже готовый образ, видит бог, я этого не хотел, но придётся ставить все ручками, наследуясь от безпроблемных образов. Поэтому идем на страничку и смотрим как ставить ROS на простую систему.
Любой контейнер начинается с докерфайла, поэтому начнем с установки ROS в Docker. Во-первых для ROS нужен полноценный контейнер, что-то типо убунты одной из не сильно допотопных версий, второе нужно выдернуть строчки из гайда по установке. Единственное что, для какого-то из пакетов от ROS требуется таймзона. Аргументы в свою очередь нужны будут дальше для фокусов, да и в целом полезно в них разобраться, чтобы сделать немного динамический и универсальный докерфайл.
FROM ubuntu:20.04 ARG hostname ARG host_ip ARG ros_master_uri RUN apt update ENV TZ=Europe/Kiev RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN sh -c 'echo "deb http://packages.ros.org/ros/ubuntu focal main" > /etc/apt/sources.list.d/ros-latest.list' RUN apt install -y curl gnupg gnupg2 gnupg1 RUN curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | apt-key add - RUN apt update && apt install -y ros-noetic-ros-base ENV ROS_DISTRO noetic RUN apt install -y python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essentia
Для универсальности и удобства установим рабочую директорию и переменную среды с путем к проету.
ENV PROJECT_DIR=/root/catkin_ws ENV ROS_MASTER_URI=${ros_master_uri} WORKDIR /root/catkin_ws
На этом шаге нам и пригодятся переменные среды выше, появляется задачка немного сложнее, чтобы подтянулись все утилиты ROS’а нужно сурснуть файлик /opt/ros/$ROS_DISTRO/setup.bash. Предлагаю в проекте создать файлик .bashrc с таким содержимым:
source /opt/ros/$ROS_DISTRO/setup.bash source $PROJECT_DIR/devel/setup.bash export ROS_IP=$(hostname -i) export PS1="\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[0;33m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ "
Выставление последних двух переменных не обязательно, если при создании контейнера будет использоваться подсеть хоста (--net host). PS1 нужна лишь для украшения нашего терминала, подчистую украдена с убунты, за исключением цвета.
Его требуется скопировать в домашнюю директорию внутри образа, отсюда появляется такая строчка
COPY .bashrc /root/
Цыганские фокусы
А теперь об организации проекта с докером, которую я вывел для себя как оптимальную путем экспериментов. Темплейт проекта организовать стоит как-то так
catkin_ws/ ├── .catkin_workspace └── src ├── CMakeLists.txt └── ros-docker-template ├── CMakeLists.txt ├── docker │ ├── .bashrc │ └── Dockerfile ├── launch │ └── default.launch ├── package.xml ├── scripts │ ├── attach.sh │ ├── build_docker.sh │ ├── run_prog.sh │ └── start.sh └── src └── node.py
Я хотел чтобы и хост система и докер видели это как ROS пакет. Запуск контейнера предполагается с помощью скрипта start.sh, а build_docker.sh будет собирать образ проекта соответственно, и все это через rosrun на хост системе. Нужно всего-то смонтировать catkin_ws/src/ros-docker-template в контейнер без привязки к абсолютным путям, поэтому будем использовать пути относительно скриптов, он и будет создавать контейнер и запускать его, при условии если контейнер не существует и не запущен. Штош, задача звучит уже так, что не хочется слышать, но в итоге вдоволь намучавшись она была решена.
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )" HOST_IP=($(hostname -I)) img_name=$(dirname $(readlink -m $DIR)) img_name=${img_name##*/}-img docker build \ --build-arg hostname=$(hostname) \ --build-arg ros_master_uri="http://${HOST_IP[0]}:11311" \ --build-arg host_ip=${HOST_IP[0]} \ --tag $img_name \ $DIR/../docker
Основная фишка в том, что он называет контейнер по имени директории с проектом + ‘-img’, эта фишка абузится и во всех остальных скриптах.
Собственно через аргумент --build-arg и передаются переменные в Dockerfile, если их не передать, докер выдаст ворнинг, но все равно соберет образ. Дальше пойдет совсем жеcть, просьба убрать людей, беременных детей и женщин с тонкой душевной организацией от экрана.start.sh
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )" container_name=$(dirname $(readlink -m $DIR)) container_name=${container_name##*/} img_name=$container_name-img if [ ! "$(docker ps -a | grep $container_name)" ]; then docker run -di \ --name $container_name \ --add-host $(hostname):$(hostname -i) \ --mount type=bind,src=$DIR/../..,dst=/root/catkin_ws/src \ --hostname ros-0 \ -P \ $img_name bash fi if ! [ "$( docker container inspect -f '{{.State.Status}}' $container_name )" == "running" ]; then docker start $container_name fi docker exec $container_name bash -c "source /root/.bashrc; catkin_make"
Этот скрипт сразу поднимает/создает контейнер и монтирует директорию catkin_ws/src сразу в образ, что позволит запускать внутри контейнера и все остальные пакеты в этом воркспейсе, ну не чудно ли? Более того он на опережение его собирает.
Для быстрого старта любого пакета из этого воркспейса была собрана из велосипедов и костылей целая консольная утилита run_prog.sh.
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )" container_name=$(dirname $(readlink -m $DIR)) container_name=${container_name##*/} if [ $1 = "--help" ] || [ $1 = "-h" ]; then echo "usage no args: rosrun $container_name node.py 1 arg : rosrun $container_name <ARG> 2 arg : rosrun <arg1> <arg2> " fi if [[ $1 == "" ]] && [[ $2 == "" ]] then PKG=$container_name EXEC=node.py echo 1 elif [[ $2 == "" ]] && [[ $1 != "" ]] then PKG=$container_name EXEC=$1 echo 2 else PKG=$1 EXEC=$2 echo 3 fi echo "launching pkg:$PKG exec:$EXEC" docker exec $container_name bash -c "source /root/.bashrc; catkin_make; rosrun $PKG $EXEC"
Все про нее написано в общем-то в help, оно при запуске перекомпилирует весь воркспейс, ну прямо чудеса bash скриптов.
Итого
Получилась вот такая репа
# Cборка образа rosrun ros-docker-template build_docker.sh # Запуск/создание контейнера rosrun ros-docker-template start.sh # Запуск любого пакета внутри контейнера rosrun ros-docker-template run_prog.sh <pkg> <exec>
ссылка на оригинал статьи https://habr.com/ru/post/704674/
Добавить комментарий