We wanted to reduce the amount of work while deploying code of each module on multiple submarines. We also wanted to support development on Ubuntu, Windows, MacOS and finally enable development on different architectures, such as X86_64
or arm64
. After reviewing multiple container technologies (docker
, singularity
), we decided to go with docker since ROS
was already offering a docker image with all of the required dependencies installed in it (refer to Technology Stack for more details). This page's purpose is to give an overview of how the new ROS node should be developed and how to create a new ROS
node using ros-package-repo-template. By reading this page you should also be able to understand the basics of how our modules are packed into a docker
image.
We will use the proc_mapping node as an example to explain how the repository layout has been generated and how it works with
docker
First of all, here is a view of proc mapping content we will quickly go over important files and folders. Notice some files and folders have been removed from the tree to simplify the example since they were not significant here:
.
├── .devcontainer
│ └── devcontainer.json
├── .dockerignore
├── .github
│ ├── pull_request_template.md
│ └── workflows
│ ├── docker-image-perception-develop.yml
│ ├── docker-image-perception-feature.yml
│ └── docker-image-perception-master.yml
├── .vscode
│ ├── c_cpp_properties.json
│ ├── launch.json
│ ├── settings.json
│ └── tasks.json
├── CMakeLists.txt
├── Dockerfile
├── launch
│ ├── proc_mapping.launch
│ └── proc_mapping_simulation.launch
├── package.xml
├── scripts
│ └── sonia_entrypoint.sh
├── src
│ └── proc_mapping
│ ├── main.cc
│ ├── objectives
│ │ ├── HydroObjective.cc
│ │ └── HydroObjective.h
│ ├── proc_mapping_node.cc
│ └── proc_mapping_node.h
└── test
Here is a quick overview of the content of each of the listed folders:
.devcontainer
: This folder contains the configuration file to launch a development container in a local or remote environment.github
: Contains workflow (CI/CD) and issues related files.vscode
: Contains multiple configuration files to resolve dependencies in a container (C++ and Python) and also contains Visual Studio Code debug task filelaunch
: Contains ROS node launch configuration filescripts
: Contains manual scripts and docker entry point file (sonia_entrypoint.sh
)src\<node_name>
: Contains ROS node code (C++ or Python)test
: Contains ROS node testsdrivers
(not shown here): This folder should contain any additional drivers requested by the nodeHere is a list of important files that are required to dockerize a ROS node and be able to debug/run it remotely:
devcontainer.json
: This files contains configuration to run a development containers (env variables, docker run arguments and much more for additional info see local-and-remote-development-process.dockerignore
: This files works much like .gitignore but prevent docker from passing files and folders into the docker image build contextc_cpp_properties.json
: Contains all required configurations for CPP debugging and resolving dependencies through a docker container using Visual Studio Code Remote-Container extensionslaunch.json
: Contains specific configuration to launch a given ROS node using Visual Studio Code debuggersettings.json
: Contains path into devcontainer to be able to resolve ROS Python dependenciestasks.json
: Contains defined tasks that can be called into launch.json preLaunchTask
sectionpackage.xml
: Contains package information i.e: Package version (Very important to bump version when making changes to a package)sonia_entrypoint.sh
: Contains commands that must be run while a docker image is startedWe highly encourage you to get to know docker by reading this Dockerfile Reference.
First of all, we will introduce the Dockerfile
and the docker build context: As you can see, Dockerfile
contains a regular command like you would see in a Unix terminal with additional commands specific to docker
. Dockerfile
can be seen as a list of instructions to execute to create an image of an operating system in which in our case each ROS node will be isolated from each other. This way, it eases up module development, debugging, and deployment. You won't need to install all Custom SONIA ROS modules on your development machine if you do not need to work with a given module.
Here is the Dockerfile
from proc_mapping node
ARG BASE_IMAGE="docker.pkg.github.com/sonia-auv/sonia_common/sonia_common:x86-perception-latest"
FROM ${BASE_IMAGE}
USER root
ARG BUILD_DATE
ARG VERSION
ENV NODE_NAME=proc_mapping
LABEL net.etsmtl.sonia-auv.node.build-date=${BUILD_DATE}
LABEL net.etsmtl.sonia-auv.node.version=${VERSION}
LABEL net.etsmtl.sonia-auv.node.name=${NODE_NAME}
ENV SONIA_WS=${SONIA_HOME}/ros_sonia_ws
ENV NODE_NAME=${NODE_NAME}
ENV NODE_PATH=${SONIA_WS}/src/${NODE_NAME}
ENV LAUNCH_FILE=${NODE_NAME}.launch
ENV SCRIPT_DIR=${SONIA_WS}/scripts
ENV ENTRYPOINT_FILE=sonia_entrypoint.sh
ENV LAUNCH_ABSPATH=${NODE_PATH}/launch/${LAUNCH_FILE}
ENV ENTRYPOINT_ABSPATH=${NODE_PATH}/scripts/${ENTRYPOINT_FILE}
ENV SONIA_WS_SETUP=${SONIA_WS}/devel/setup.bash
WORKDIR ${SONIA_WS}
COPY . ${NODE_PATH}
RUN bash -c "source ${ROS_WS_SETUP}; source ${BASE_LIB_WS_SETUP}; catkin_make"
RUN chown -R ${SONIA_USER}: ${SONIA_WS}
USER ${SONIA_USER}
RUN mkdir ${SCRIPT_DIR}
RUN cat $ENTRYPOINT_ABSPATH > ${SCRIPT_DIR}/entrypoint.sh
RUN echo "roslaunch --wait $LAUNCH_ABSPATH" > ${SCRIPT_DIR}/launch.sh
RUN chmod +x ${SCRIPT_DIR}/entrypoint.sh && chmod +x ${SCRIPT_DIR}/launch.sh
RUN echo "source ${SONIA_WS_SETUP}" >> ~/.bashrc
ENTRYPOINT ["./scripts/entrypoint.sh"]
CMD ["./scripts/launch.sh"]
After having a look at the given Dockerfile
you can see that each specific Dockerfile
command has a specific prefix handled by the docker build context.
Here is an overview of these commands:
ARG
: This command lets you define arguments that must be passed through the docker build context (you can also set a default value (see line 1)FROM
: This command lets you specify the source base image to build the container fromUSER
: This command lets you switch user context in the OS used inside the image (e.g. a user on Ubuntu)ENV
: Lets you set an environment variable inside the docker container (default value can also be provided from an ARG)LABEL
: Lets you define container metadataWORKDIR
: Lets you switch the current directory inside the containerCOPY
: This command will copy files or folders from the specified path to a container specified by another pathRUN
: This is the command which you will use the most since it allows you to run a bash shell command while building the containerENTRYPOINT
: Lets specify an entry point to be run once the container is runCMD
: Lets you specify a command to run with its argumentsFor more information on wich image should be defined in the
ARG BASE_IMAGE
field, see the auv-environment page
The purpose of this file is to restrict files and folders we do not want to be added to the docker build context while building a container from its image definition file
Since we do not want to add a ROS module build and devel folders which get generated when compiling a ROS module, we add them to the file like so :
build
devel
Currently, SONIA'S existing custom ROS node has been dockerized and can be used with docker. If you have to create a new node you can use
ros-package-repo-template to generate your new node. This template has as all the required files specified in it.
If you are having issues with the dockerise nodes or don't have enough space to download the images, you can use the following command :
docker system prune -a
You can find the documentation for this command on this site : Supprimer les images sur Docker