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 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.

# Specify the MoveIt Studio release to build on top of.
ARG USERNAME=studio-user
ARG USER_UID=1000
ARG USER_GID=1000

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

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

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

# Also mkdir with user permission directories which will be mounted later to avoid docker creating them as root
WORKDIR $USER_WS
# hadolint ignore=DL3008
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} && \
    cp -r /etc/skel/. /home/${USERNAME} && \
    mkdir -p \
      /home/${USERNAME}/.ccache \
      /home/${USERNAME}/.config \
      /home/${USERNAME}/.ignition \
      /home/${USERNAME}/.colcon \
      /home/${USERNAME}/.ros && \
    chown -R $USER_UID:$USER_GID /home/${USERNAME} /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.
# hadolint ignore=SC1091
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

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
ARG USER_WS=/home/${USERNAME}/user_overlay_ws
ENV USER_WS=${USER_WS}

# Compile the workspace
WORKDIR $USER_WS
# hadolint ignore=SC1091
RUN --mount=type=cache,target=/home/${USERNAME}/.ccache \
    . /opt/overlay_ws/install/setup.sh && \
    colcon build

CMD ["/usr/bin/bash"]

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}
    # Set the user workspace folder location.
    environment:
      - USER_WS=/home/${USERNAME:-studio-user}/user_overlay_ws

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 and the Bridge between the Agent and the Web UI
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

# Starts the web UI frontend.
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.11.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.
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

ARG USERNAME
ARG USER_WS=/home/${USERNAME}/user_overlay_ws
ENV USER_WS=${USER_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

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/:/home/${USERNAME:-studio-user}/user_overlay_ws/src:rw
    - ./build/:/home/${USERNAME:-studio-user}/user_overlay_ws/build:rw
    - ./install/:/home/${USERNAME:-studio-user}/user_overlay_ws/install:rw
    - ./log/:/home/${USERNAME:-studio-user}/user_overlay_ws/log:rw
    - ./.ccache/:/home/${USERNAME:-studio-user}/.ccache:rw
    - ${HOME}/.ros/log_moveit_studio:/home/${USERNAME:-studio-user}/.ros/log
    # 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.