Create Overlay Docker Images

When working with MoveIt Studio, you will likely create new packages which may contain:

  • New Behaviors and Objectives

  • New robot configuration packages

  • Descriptions and drivers for a new robot

  • Novel algorithms currently not present in MoveIt Studio

For many of these applications, you can mount a user workspace directly into the MoveIt Studio Docker containers. However, there may be some use cases where this may not be sufficient, such as:

  • Your workspace requires additional binary packages, where rosdep install, apt-get install, pip install, etc. is needed to build the workspace.

  • The hardware and/or algorithms you are working with require additional dependencies.

  • You want to install additional tools for debugging during development.

Creating a Release Overlay Container

You can create an overlay Docker image through the following steps:

  1. Create a Dockerfile.

  2. Create a custom entrypoint to run at container startup.

  3. Create docker-compose.yaml and .env files to manage container configuration and startup.

Let’s first create the Dockerfile.

The first layer will be called base and do all the necessary setup. This includes copying source code into the image, creating a new non-root user to that you have ownership of mounted files on the host, and installing any additional dependencies needed by your workspace.

# Extend the 2.6.0 release of MoveIt Studio
FROM picknikciuser/moveit-studio:2.6.0 as base

USER root

# Copy source code from the workspace's ROS 2 packages to a workspace inside the container
ARG USER_OVERLAY_WS=/opt/user_overlay_ws
ENV USER_OVERLAY_WS $USER_OVERLAY_WS
RUN mkdir -p ${USER_OVERLAY_WS}/src ${USER_OVERLAY_WS}/build ${USER_OVERLAY_WS}/install ${USER_OVERLAY_WS}/log
COPY ./src ${USER_OVERLAY_WS}/src

# Create a non-root user
ARG USERNAME
ARG USER_UID
ARG USER_GID

# Also mkdir with user permission directories which will be mounted later to avoid docker creating them as root
WORKDIR $USER_OVERLAY_WS
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    groupadd --gid $USER_GID ${USERNAME} && \
    useradd --uid $USER_UID --gid $USER_GID --shell /bin/bash --create-home ${USERNAME} && \
    apt-get update && \
    apt-get install -q -y --no-install-recommends sudo && \
    echo ${USERNAME} ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/${USERNAME} && \
    chmod 0440 /etc/sudoers.d/${USERNAME} && \
    mkdir -p \
      /home/${USERNAME}/.ccache \
      /home/${USERNAME}/.config \
      /home/${USERNAME}/.ignition \
      /home/${USERNAME}/.ros/log && \
    chown -R $USER_UID:$USER_GID /home/${USERNAME} ${USER_OVERLAY_WS} /opt/overlay_ws/

# Install additional dependencies
# You can also add any necessary apt-get install, pip install, etc. commands at this point.
# NOTE: The /opt/overlay_ws folder contains MoveIt Studio binary packages and the source file.
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    . /opt/overlay_ws/install/setup.sh && \
    apt-get update && \
    rosdep install -q -y \
      --from-paths src \
      --ignore-src

# Remove .bashrc from parent image and create a new one
USER ${USERNAME}
RUN rm /home/${USERNAME}/.bashrc && touch /home/${USERNAME}/.bashrc

Next, we will create a new stage called user-overlay which builds the source code for release and sets up an entrypoint (a script that executes at container startup).

FROM base as user-overlay

ARG USERNAME
USER ${USERNAME}

# Compile the workspace
# hadolint ignore=SC1091
RUN --mount=type=cache,target=/home/studio-user/.ccache \
    . /opt/overlay_ws/install/setup.sh && \
    colcon build

# Add the custom entrypoint
COPY ./entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
RUN echo "source /entrypoint.sh && set +e" >> ~/.bashrc
CMD ["/usr/bin/bash"]

The entrypoint script, named entrypoint.sh, is copied in from your host system and should look as follows.

#!/bin/bash
set +e

# Source the MoveIt Studio binaries and your workspace
source "/opt/overlay_ws/install/setup.bash"
if [ -f "${USER_OVERLAY_WS}/install/local_setup.bash" ]; then
  source "${USER_OVERLAY_WS}/install/local_setup.bash"
fi

# Set the location for custom Behavior package generation
export STUDIO_GENERATE_PACKAGE_OUTPUT_PATH="${USER_OVERLAY_WS}/src"

# Set the permissions for your non-root user at startup.
source /moveit_studio_utils/change_permissions.sh

# Set the DDS configuration for ROS 2 inter-process communication.
source /moveit_studio_utils/copy_dds_configs.sh

exec "$@"

Next, create a docker-compose.yaml file which extends the docker-compose.yaml file that was installed with MoveIt Studio. The default location for the installed file is ${HOME}/moveit_studio/docker-compose.yaml, but feel free to change this for your setup.

version: "3.9"

services:
  # Sets common properties for other services. Should not be instantiated directly.
  base:
    # Extend the installed MoveIt Studio docker compose file.
    # Change this to match your environment, if MoveIt Studio was installed to a different location.
    extends:
      file: ${HOME}/moveit_studio/docker-compose.yaml
      service: base
    image: moveit-studio-overlay
    build:
      context: .
      target: user-overlay
      args:
        - USER_UID=${STUDIO_USER_UID:-1000}
        - USER_GID=${STUDIO_USER_UID:-1000}
        - USERNAME=${USERNAME:-studio-user}

Similarly, you can extend all the services from the base image as follows. This can be directly copied from the docker-compose.yaml that is installed with MoveIt Studio.

# Additional services
  # Starts the MoveIt Studio Agent
  agent_bridge:
    extends: base
    privileged: true
    # Allow the user to use the host's network video4linux and usb_device devices.
    # "c 81:* rmw": This rule specifies that the container has read, write, and mknod access (rmw) to all character devices (c) with a major number of 81 (video4linux).
    # "c 189:* rmw": This rule gives the container read, write, and mknod access (rmw) to all character devices (c) with a major number of 189 (usb_device).
    device_cgroup_rules:
      - "c 81:* rmw"
      - "c 189:* rmw"
    depends_on:
      rest_api:
        condition: service_healthy
    volumes:
      # Allow the user to run graphical programs from within the docker container.
      - /tmp/.X11-unix:/tmp/.X11-unix:ro
      # Allow access to host hardware e.g. RealSense cameras
      - /dev:/dev
    deploy:
      restart_policy:
        condition: any
        delay: 2s
    command: agent_bridge.app

  # Starts the REST API for the Web UI
  rest_api:
    extends: base
    healthcheck:
      test: "curl -f http://localhost:3200/objectives"
      interval: 5s
      timeout: 1m
    command: rest_api.app

  # Starts the robot drivers
  drivers:
    extends: base
    privileged: true
    # Ensures the drivers container has RT priority
    ulimits:
      rtprio: 99
    devices:
      - "/dev/ttyUSB0:/dev/ttyUSB0"  # Allow access to the gripper.
    command: robot.app

  web_ui:
    image: picknikciuser/moveit-studio-frontend:${STUDIO_DOCKER_TAG:-main}
    ports:
      - "80:80"
    network_mode: host

  # Starts RViz for visualization.
  rviz:
    extends: base
    profiles:
      - rviz
    command: bash -c "ros2 launch moveit_studio_agent developer_rviz.launch.py"

  # Starts MoveIt Setup Assistant for creating MoveIt configurations.
  setup_assistant:
    extends: base
    profiles:
      - setup_assistant
    command: bash -c "ros2 launch moveit_setup_assistant setup_assistant.launch.py"

Finally, you can create a .env file containing the necessary variables to use when launching MoveIt Studio. For this example, you can include the following contents.

# Robot configuration package to launch
STUDIO_CONFIG_PACKAGE=picknik_ur_mock_hw_config

# MoveIt Studio version tag
# Be careful! Changing this without understanding the consequences may break your installation.
STUDIO_DOCKER_TAG=2.6.0

# Licensing -- enter your MoveIt Studio license below
STUDIO_LICENSE_KEY=

# User name and UID to use when building the container
# Ensure these settings match your host environment for mounted file permissions.
STUDIO_USERNAME=
STUDIO_USER_UID=

Now, you should be able to build and launch your MoveIt Studio overlay release image:

docker compose up

You can find a completed version of this example in the moveit_studio_ws repository.

Creating a Dev Container

With the release container workflow from the previous section, every time you change your source code you will need to rebuild the Docker image entirely for the changes to take effect when launching MoveIt Studio. One way to get around this is by making a developer container (or dev container) that mounts the source code and lets you build and run it repeatedly without restarting the container.

As an example, let us extend the moveit-studio-overlay target in our Dockerfile with a development target that adds a few extra packages for debugging.

FROM base as user-overlay-dev

USER root

# The location of the user's workspace inside the container
ARG USER_OVERLAY_WS=/opt/user_overlay_ws
ENV USER_OVERLAY_WS $USER_OVERLAY_WS

# Install any additional packages for development work
# hadolint ignore=DL3008
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    apt-get update && \
    apt-get install -y --no-install-recommends \
        less \
        gdb \
        nano

# Add the dev entrypoint
ARG USERNAME
USER ${USERNAME}
COPY ./entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
RUN echo "source /entrypoint.sh && set +e" >> ~/.bashrc
CMD ["/usr/bin/bash"]

You can then extend the docker-compose.yaml file with a new service named dev as follows:

# Developer specific configuration
  dev:
    extends: base
    build:
    target: user-overlay-dev
    image: moveit-studio-overlay-dev
    stdin_open: true
    tty: true
    privileged: true
    volumes:
    # Mount the source code, colcon generated artifacts, and ccache
    - ./src/:/opt/user_overlay_ws/src:rw
    - ./build/:/opt/user_overlay_ws/build:rw
    -  ./install/:/opt/user_overlay_ws/install:rw
    - ./log/:/opt/user_overlay_ws/log:rw
    - ./.ccache/:/home/${USERNAME:-studio-user}/.ccache:rw
    # Allow access to host hardware e.g. RealSense cameras
    - /dev:/dev
    command: sleep infinity
    # Making a separate profile prevents this service from being built when using `docker compose build`
    # and launched when using `docker compose up`.
    profiles: ["dev"]

Note that we mount several folders in addition to the src folder of the workspace. This ensures that artifacts generated by colcon build persist between container instances.

Now, you should be able to build and launch your MoveIt Studio overlay dev container:

docker compose build dev
docker compose up dev

Once you have started the dev container, you can enter an interactive Bash session and launch one of the MoveIt Studio launch files directly. For example:

docker compose exec -it dev bash

Once inside the container, you can compile, run tests, or even launch different parts of the MoveIt Studio application.

colcon build
source install/setup.bash
ros2 launch moveit_studio_agent studio_agent.launch.py

Note

Using Docker containers for development is a common practice. Many IDEs, such as Visual Studio Code and PyCharm, have built-in support for developing with Docker containers. Once you have familiarized yourself with the steps above, we recommend setting up your favorite IDE(s) to help you develop new functionality for MoveIt Studio.