# ai_robot_dockerizer **Repository Path**: katly/ai_robot_dockerizer ## Basic Information - **Project Name**: ai_robot_dockerizer - **Description**: 私有仓库,单独维护。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-03-20 - **Last Updated**: 2025-03-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AI Robot Dockerizer AI Robot Dockerizer is a utility that automates the build of Docker images for AI Robot project, offering containerization support for the project development, testing and deployment. ## Why not use ROS official Docker images Docker images delivered by ROS official have the following limitations: - The original source of Ubuntu (APT) software repository is an overseas site that slows down software package installation. - The original source of ROS package repository is an overseas site that slows down software package installation. - The image has no non-root user account, thus a container can only be run as root leading to permission issues for shared resources between the host and the running container. - No NVIDIA GPU (optionally CUDA/cuDNN) support, thus being incapable of running some AI applications. - Ros Melodic has a relatively old version of CMake which may fail the build on some new projects. ## Staged design ai_robot_dockerizer splits the process of building a Docker image into three stages, they are: 1. Build a ROS image based on Ubuntu with optional NVIDIA OpenGL + CUDA/cuDNN support 2. Build an image with non-root user upon the ROS image 3. Build a *devel* (development) or *release* type of image upon the non-root user image ### ROS image Compared with ROS official Docker images, the images built by this utility have the following improvements: - Replacing the original Ubuntu software repository with the SJTU mirror site. - Replacing the original ROS software repository with the SJTU mirror site. - Integrating the `su-exec` command that allows to run a container with non-root privilege. - Integrating *catkin_tools* as the default ROS workspace build tool. - Optionally integrating NVIDIA OpenGL support. - Selectively integrating CUDA/cuDNN runtime or development toolkit. According to different "roles" (see [REP-412](https://www.ros.org/reps/rep-0142.html)), ROS community divides ROS into five levels: *Core*, *Base*, *Robot*, *Desktop* and *Desktop Full* , and provides corresponding installation packages and Docker images. However, ai_robot_dockerizer currently supports the build of only **three** roles: *Base*, *Desktop* and *Desktop Full*. ### Non-root user image Non-root user image is built upon the ROS image, and has the following extensions: - Creating a group named `render`. - Creating a new user named `uav` with UID and GID both `1000`, adding the `uav` account to `sudo`, `audio`,`video` and `render` groups respectively. - Creating the `/home/uav/.config` directory and setting its ownership to `uav:uav`. - Installing the custom entrypoint scripts to the `/usr/local/bin/scripts` directory. - Creating the `/home/uav/catkin_ws` directory as the ROS catkin workspace and the default working directory in the image. ### Application image Application image is built upon the non-root user image. ai_robot_dockerizer defines two types for application images: **devel** and **release**. A *devel* type of image contains all dependencies used for developing/testing/debugging an application, while a *release* type of image contains only the runtime dependencies for running an application. The build of a *devel* type of image does not copy the source space of an application into the image, instead it mounts the source space into the container run from the image. During the process of building an application image, regardless of the image type, the path to the ROS (catkin) workspace of your application on the host must be specified. This utility (actually the `scripts/docker_build` script) invokes the `rosdep` command to parse the `package.xml` file from each ROS package within the specified workspace to get a complete list of dependencies, and then attempts to install them all. Hence it is critical to have all `package.xml` complete and correct. For those dependencies that cannot be installed via `rosdep`, this utility provides a simple shell-script-based extension mechanism. Building a *release* type of image first creates an intermediate image from a *devel* image as the builder for compiling the source code from the specified ROS workspace, and then copies the built result from the intermediate *builder* image into the *release* image. A successful build requires the `CMakeLists.txt` of all involved ROS packages within the workspace are complete and correct, especially making sure that the `install` targets can indeed get all needed files (binaries, data files, configuration files, etc) installed to the destination. The steps of building an AI Robot specific application image can be illustrated roughly as below: ```mermaid graph TD BEG([Begin]) --> ROS ROS[Build a ROS image with optional CUDA/cuDNN runtime or complete toolkit integrated] ROS --> BASEONLY{Is ROS image only?} BASEONLY -->|Yes| BASEIMG[\A base ROS image\] --> END([End]) BASEONLY -->|No| USER[Build a user image upon the ROS image with a non-root user named 'uav' added] USER --> TYPE{Image type} TYPE -->|devel| DEV[Build an 'devel' type of image] --> DEVIMG[\A devel type of image\] DEVIMG --> END TYPE -->|release| REL[Build a release type of image] --> RELIMG[\A release type of image\] TYPE -->|release| INTERMD[Build an intermediate 'devel' type of image] INTERMD --> BUILDER[Build an intermediate image upon the intermediate 'devel' type of image as a builder to build the given project workspace] BUILDER -->|Copy built results| RELIMG RELIMG --> END BUILDER --> CLEAN[Clean up the intermediate images] ``` ## Dependencies ai_robot_dockerizer requires the following dependencies to work: - Bash - Make - Docker Engine, i.e. *docker-ce* and *docker-cli* - (Optional) NVIDIA proprietary driver (installed on the host not in the Docker image) - (Optional) NVIDIA Container Toolkit (currently only supporting Ubuntu 18.04/20.04/22.04 LTS, working with NVIDIA proprietary driver) *Bash* is pre-installed on most major Linux distros. *Make* and NVIDIA proprietary driver can be installed via the system package manager (e.g. APT on Ubuntu). Recommend using `scripts/docker_install` to install Docker Engine and NVIDIA Container Toolkit as they need a bit of post-installation configurations. ## Helper scripts ai_robot_dockerizer provides three helper shell scripts to ease the process of building Docker images and running Docker containers for AI Robot applications. They are: - `srcipts/docker_install`: used to install Docker Engine and NVIDIA Container Toolkit - `srcipts/docker_build`: wrapping the `docker build` command to help build AI Robot Docker images - `srcipts/docker_run`: wrapping the `docker run` command to help run AI Robot containers Note that `scripts/docker_install` will not install NVIDIA Container Toolkit if it fails to detect any of Docker Engine, NVIDIA GPU and NVIDIA proprietary driver installed in your system. These three scripts are expected to cover common ROS-based development scenarios without requiring users to manually write Dockerfiles. However, for those more complex cases where these scripts cannot help, you can still build a base ROS image by using `scripts/docker_build` and then write your own Dockerfile to build your specific Docker image upon the built ROS image. ## Usage All three helper scripts listed in the previous chapter support command line options. You can run any of them with the `--help` flag to view their usage. ### Installing Docker Engine NVIDIA Container Toolkit provides NVIDIA's Linux container runtime implementation that supports accessing the hosted NVIDIA driver from inside Docker containers. You don't have to install NVIDIA Container Toolkit if you do not use an NVIDIA GPU or its proprietary driver. Use the `scripts/docker_install` script: ```sh # print command help scripts/docker_install --help # Install Docker Engine only scripts/docker_install # Install both Docker Engine and NVIDIA Container Toolkit scripts/docker_install --nv # Install NVIDIA Container Toolkit only(Docker Engine already installed) scripts/docker_install --nv-only ``` ### Setting proxy (optional) The build of a ROS image needs to run `rosdep init` during the build process, which requires the access to Github to fetch the metadata (several YAML files) that maps the ROS package names to distro package names. You may have to use a proxy the get around the Github access timeout. Please note that the Docker engine does not respect the system proxy settings but uses its own configuration files at `~/.docker/config.json`. Adding the following content to this file could usually help fix the Github connection issue (replace the proxy `url:port` with your own settings): ```json { "proxies": { "default": { "httpProxy": "socks5://127.0.0.1:7890", "httpsProxy": "socks5://127.0.0.1:7890", "noProxy": ".docker.io,.aliyun.com,.163.com,ubuntu.com,.edu.cn,.nvidia.cn,.nvidia.com,127.0.0.0/8,192.168.0.0/16" } } } ``` The `noProxy` entry defines the domains/IPs to bypass. Add additional domains/IPs to this entry that you need to access without a proxy during the image build process. Besides the Docker engine itself, some proxy client allows to customize proxy rules as well. All in all, set your proxy rules properly in case your proxy service runs out of credit. ### Building a devel type of image Use the `scripts/docker_build` script. You can supply the `--dry-run` option to the script to just print the generated `docker build` command line instead of performing the actual build action, which would be useful in the cases where you want to do a pre-check first. Supposing the path to the workspace of your ROS project is `~/workspaces/ai_robot_ws`, the command below is going to build a *devel* type of image that has ROS Noetic Desktop Full with CUDA 11.7.1 and cuDNN 8 integrated. ```sh scripts/docker_build --cuda 11.7.1 --cudnn 8 --type devel --tag batc/ai_robot:noetic-desktop-full_cuda11.7.1-cudnn8-rt_dev noetic-desktop-full ~/workspace/ai_robot_ws ``` In the command above: - The positional argument `noetic-desktop-full` specifies the ROS distro and role, and in the example the "Desktop Full" role contains all visualization and simulation tools besides the core features. - The positional argument `~/workspaces/ai_robot_ws` specifies the workspace of our project on the host. - The `--tag` option specifies the tag (name) that is going to be assigned to the image to build. It is recommended to follow the Docker Hub image naming convention that is in form of `/:` where the `` should be descriptive to tell the feature of the image. - The `--type` option specifies the image type, defaulting to `devel` if omitted. In other words, you must explicitly supply it with the value of `release` when building a *release* type of image. If the compilation of your project requires CUDA headers and CUDA compiler (nvcc), you can supply the `--cuda-dev` option to have the build script install not only the runtime but the full CUDA toolkit into the image: ```sh scripts/docker_build --cuda 11.7.1 --cudnn 8 --cuda-dev --type devel --tag batc/ai_robot:noetic-desktop-full_cuda11.7.1-cudnn8-dev_dev noetic-desktop-full ~/workspace/ai_robot_ws ``` For a project that does not depend on CUDA at all but needs NVIDIA OpenGL support, you can just use the `--nv` option: ```sh scripts/docker_build --nv --type devel --tag batc/ai_robot:noetic-desktop-full_nvidia_dev noetic-desktop-full ~/workspace/ai_robot_ws ``` If there is no NVIDIA stuff involved at all, do not supply any NVIDIA related options: ```sh scripts/docker_build --type devel --tag batc/ai_robot:noetic-desktop-full_dev noetic-desktop-full ~/workspace/ai_robot_ws ``` **NOTE**: ai_robot_dockerizer does not support and is not likely to support all versions of CUDA and cuDNN. Before starting to build an image, you better check all ROS distro and CUDA/cuDNN combinations supported by doing: ```sh scripts/docker_build --ls-base ``` ### Building a release type of image Building a *release* type of image is similar to building a *devel* type of image, the only difference is that you should supply the `--type` option with the value of `release`. You certainly should give a reasonable image tag that can reflect the feature of the image to build, a `_rel` suffix makes sense in terms of distinguishing the *release* type of images from the *devel* type of images. However, if all *devel* type of images are tagged with `_dev` suffix, then the `_rel` suffix for *release* type of images is not mandatory since the presence of the `_dev` suffix has already been sufficient for distinguishing between these two types. Here are some examples: ```sh # ROS noetic-desktop with CUDA 11.7.1 + cuDNN 8 runtime scripts/docker_build --cuda 11.7.1 --cudnn 8 --type release --tag batc/ai_robot:noetic-desktop_cuda11.7.1-cudnn8-rt noetic-desktop ~/workspace/ai_robot_ws # ROS noetic-desktop with NVIDIA OpenGL support scripts/docker_build --nv --type release --tag batc/ai_robot:noetic-desktop_nvidia noetic-desktop ~/workspace/ai_robot_ws # ROS noetic-desktop without NVIDIA support scripts/docker_build --type release --tag batc/ai_robot:noetic-desktop_dev noetic-desktop ~/workspace/ai_robot_ws ``` **NOTE**: a *release* type of image does not contain source files, hence the `--cuda-dev` option is not supported and will be ignored even if it is present on the command line. Additionally, a *release* type of image usually does not need simulation tools, consider using ROS Desktop rather than ROS Desktop Full if possible to reduce the image size. ### Building a base ROS image In the cases where this utility is not able to meet your requirements, you may want to use it to build a base ROS image with NVIDIA stuff integrated and then extend the base image by yourself. The `scripts/docker_build` script allows you to do so with the `--base-only` option: ```sh # ROS noetic-desktop-full with CUDA 11.7.1 + cuDNN runtime scripts/docker_build --base-only --cuda 11.7.1 --cudnn 8 --tag batc/ros:noetic-desktop-full_cuda11.7.1-cudnn8-rt noetic-desktop-full # ROS noetic-desktop-full with CUDA 11.7.1 + cuDNN devel toolkit scripts/docker_build --base-only --cuda-dev --cuda 11.7.1 --cudnn 8 --tag batc/ros:noetic-desktop-full_cuda11.7.1-cudnn8-dev noetic-desktop-full # ROS noetic-desktop-full with NVIDIA OpenGL support scripts/docker_build --base-only --nv --tag batc/ros:noetic-desktop-full_nvidia-rt noetic-desktop-full # ROS noetic-desktop-full without NVIDIA support scripts/docker_build --base-only --tag batc/ros:noetic-desktop-full_nvidia-rt noetic-desktop-full ``` ### Shell-script-based extension As described before, the image build process invokes the `rosdep` command to install all dependencies defined in the `package.xml` files of each involved ROS packages. However, you may have some dependencies that are not yet covered by `rosdep` or need some extra configurations and operations during the image build. ai_robot_dockerizer allows users to use shell scripts to extend an application image in a limited manner. The extension shell scripts must be placed under the `/src/docker/devel/` for *devel* type of images and `/src/docker/release/` for *release* type of images. You can put any number of scripts under these directories, and the image build script will execute them one by one during the build process. **Note that all extension scripts must be with executable permission**. See the "ROS YOLOv5 containerization" chapter below for your reference. ### Running a container The `scripts/docker_run` script wraps the `docker run` command to avoid the hassle of manually supplying complex command line options. Running a container from a *devel* type of image requires you specify your project workspace on the command line, while running a container from a *release* type of image normally does not. Here are some examples with `~/workspaces/ai_robot_ws` as workspace: ```sh # Print command help scripts/docker_run --help # Run a *devel* type of container (with CUDA 11.7.1 + cuDNN 8 support) with ~/workspaces/ai_robot_ws bind-mounted at /home/uav/catkin_ws in the container scripts/docker_run --nv --ws "~/workspace/ai_robot_ws" batc/ai_robot:noetic-desktop-full_cuda11.7.1-cudnn8-rt_dev # Run a *devel* type of container (with NVIDIA OpenGL support) with ~/workspaces/ai_robot_ws bind-mounted at /home/uav/catkin_ws in the container scripts/docker_run --nv --ws "~/workspace/ai_robot_ws" batc/ai_robot:noetic-desktop-full_nvidia_dev # Run a *devel* type of container (without NVIDIA support) with ~/workspaces/ai_robot_ws bind-mounted at /home/uav/catkin_ws in the container scripts/docker_run --ws "~/workspace/ai_robot_ws" batc/ai_robot:noetic-desktop-full_dev # Run a *release* type of container (with NVIDIA 11.7.1 + cuDNN 8 support) and execute `roslaunch ai_robot_bringup start.launch` from inside scripts/docker_run --nv batc/ai_robot:noetic-desktop-full_cuda11.7.1-cudnn8-rt roslaunch ai_robot_bringup start.launch # Run a *release* type of container (with NVIDIA OpenGL support) and execute `roslaunch ai_robot_bringup start.launch` from inside scripts/docker_run --nv batc/ai_robot:noetic-desktop-full_nvidia roslaunch ai_robot_bringup start.launch # Run a *release* type of container (without NVIDIA support) and execute `roslaunch ai_robot_bringup start.launch` from inside scripts/docker_run batc/ai_robot:noetic-desktop-full roslaunch ai_robot_bringup start.launch # Print the generated `docker run` command line only scripts/docker_run --dry-run batc/ai_robot:noetic-desktop-full roslaunch ai_robot_bringup start.launch ``` Note that the command to be executed inside a container defaults to `bash` if not explicitly specified. ## Containerization examples ### ROS YOLOv5 containerization This simple application receives images from a ROS topic and then feeds the received images to YOLOv5 to perform object detection. The path to the workspace for the application is `~/workspaces/ai_ws` and the structure of the workspace is shown as below: ``` ~/workspaces/ai_ws/ |__ src/ |__ ai_robot_objdet/ |__ CMakeLists.txt |__ package.xml |__ data/ | |__ images/ | |__ weights/ |__ launch/ | |__ start.launch |__ nodes/ |__ feed |__ detect ``` The content of `~/workspaces/ai_ws/src/ai_robot_objdet/package.xml`: ```xml ai_robot_objdet 0.1.0 The ai_robot object detection package. To Do To To TODO https://bat.sjtu.edu.cn catkin cv_bridge ``` Here `cv_bridge` package is required to convert between ROS Image messages and OpenCV images, and it will get installed by the `rosdep` command during the image build process. The content of `~/workspaces/ai_ws/src/ai_robot_objdet/CMakeLists.txt`: ```cmake cmake_minimum_required(VERSION 3.1) project(ai_robot_objdet) find_package(catkin REQUIRED) catkin_package() catkin_install_python(PROGRAMS nodes/feed nodes/detect DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} ) ``` This file will have CMake to install two Python files, i.e. `/nodes/feed` and `/nodes/detect`, to the destination (i.e. the `install` space in the workspace) during the catkin build. This application depends on YOLOv5 and is going to use `pip` to install `yolov5` package. The installation of `yolov5` package automatically installs its dependencies including `torch` (PyTorch) that further installs `nvidia-cuda-runtime-cu11` and `nvidia-cudnn-cu11` as CUDA and cuDNN runtimes. There are no requirements of visualization, simulation and pre-installed CUDA+cuDNN runtime for the Docker image of this simple application, thus an application image built upon a ROS Base image with NVIDIA OpenGL support will be sufficient. To have `yolov5` installed via `pip`, we utilize the shell-script-based extension mechanism by creating two executable shell scripts, i.e., `~/workspaces/ai_ws/src/docker/scripts/devel/deps.sh` and `~/workspaces/ai_ws/src/docker/scripts/release/deps.sh`. In our particular case, these two files happen to have the same content: ```sh #!/bin/bash -e # Creates pip config file to use SJTU mirror for speedup. mkdir /home/uav/.config/pip cat < /home/uav/.config/pip/pip.conf [global] index-url = https://mirror.sjtu.edu.cn/pypi/web/simple [install] trusted-host = mirror.sjtu.edu.cn EOF # Docker image builder runs scripts as 'root', it is necessary to change # ownership of the created files under the 'uav' home directory. Note that # USER_UID and USER_GID are both defined by the image builder, you don't have to # and shouldn't define them by yourself. chown -R ${USER_UID}:${USER_GID} /home/uav/.config/pip apt-get update apt-get install -y --no-install-recommends python3-pip # Disable pip cache to reduce image size. pip3 --no-cache-dir pip3 install yolov5 ``` Commands to build image: ```sh # Build a devel type of image scripts/docker_build --nv --type devel --tag batc/ai_robot_objdet:noetic-base_nvidia_dev noetic-base ~/workspaces/ai_ws # Build a release type of image scripts/docker_build --nv --type release --tag batc/ai_robot_objdet:noetic-base_nvidia noetic-base ~/workspaces/ai_ws ``` Commands to run container: ```sh # Run the devel container scripts/docker_run --nv --ws ~/workspaces/ai_ws batc/ai_robot_objdet:noetic-base_nvidia_dev # Run the release container scripts/docker_run --nv batc/ai_robot_objdet:noetic-base_nvidia_dev roslaunch ai_robot_objdet start.launch ```