diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..b17f666834 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,20 @@ +# ignore .git related folders +.git/ +.github/ +.gitignore +# ignore docs +docs/ +# ignore logs +**/logs/ +**/runs/ +**/output/* +**/outputs/* +**/videos/* +*.tmp +# ignore docker +docker/ +# ignore __pycache__ +**/__pycache__/ +**/*.egg-info/ +# ignore isaac sim symlink +_isaac_sim? diff --git a/docker/.env b/docker/.env new file mode 100644 index 0000000000..6a4611488e --- /dev/null +++ b/docker/.env @@ -0,0 +1,8 @@ +# Accept the NVIDIA Omniverse EULA by default +ACCEPT_EULA=Y +# NVIDIA Isaac Sim version to use (e.g. 2022.2.1) +ISAACSIM_VERSION=2022.2.1 +# Derived from the default path in the NVIDIA provided Isaac Sim container +DOCKER_ISAACSIM_PATH=/isaac-sim +# Docker user directory - by default this is the root user's home directory +DOCKER_USER_HOME=/root diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000000..be45c411ec --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,61 @@ +# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES, ETH Zurich, and University of Toronto +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# +# Nvidia Dockerfiles: +# https://github.com/NVIDIA-Omniverse/IsaacSim-dockerfiles + +# Base image +ARG ISAACSIM_VERSION +FROM nvcr.io/nvidia/isaac-sim:${ISAACSIM_VERSION} + +# Adds labels to the Dockerfile +LABEL version="1.0" +LABEL description="Dockerfile for building and running the Orbit framework inside Isaac Sim container image." + +# Arguments +# Path to Isaac Sim root folder +ARG ISAACSIM_PATH + +# Set environment variables +ENV LANG=C.UTF-8 +ENV DEBIAN_FRONTEND=noninteractive +ENV ORBIT_PATH=/workspace/orbit + +# Install dependencies and remove cache +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + git \ + ncurses-term && \ + apt -y autoremove && apt clean autoclean && \ + rm -rf /var/lib/apt/lists/* + +# FIXME: Only necessary for streaming until this fix is properly +# rolled out by NVIDIA after Isaac Sim2023.1 release +# Ref: https://forums.developer.nvidia.com/t/running-a-standalone-example-with-gui-in-docker-container/248147/3 +RUN sed -i 's/\("omni.isaac.quadruped"\s=\s{}\)/"omni.isaac.quadruped" = {order = 10}/g' \ + ${ISAACSIM_PATH}/apps/omni.isaac.sim.python.kit + +# Copy the orbit directory +COPY ../ ${ORBIT_PATH} +# Delete the logs directory +RUN rm -rf ${ORBIT_PATH}/logs + +# Set up a symbolic link between the installed Isaac Sim root folder and _isaac_sim in the orbit directory +RUN ln -sf ${ISAACSIM_PATH} ${ORBIT_PATH}/_isaac_sim + +# installing Orbit dependencies +RUN ${ORBIT_PATH}/orbit.sh --install --extra +# aliasing orbit.sh and python for convenience +RUN echo "alias orbit=${ORBIT_PATH}/orbit.sh" >> ${HOME}/.bashrc && \ + echo "alias python=${ISAACSIM_PATH}/python.sh" >> ${HOME}/.bashrc && \ + echo "alias python3=${ISAACSIM_PATH}/python.sh" >> ${HOME}/.bashrc && \ + echo "alias pip='${ISAACSIM_PATH}/python.sh -m pip'" >> ${HOME}/.bashrc && \ + echo "alias pip3='${ISAACSIM_PATH}/python.sh -m pip'" >> ${HOME}/.bashrc && \ + echo "alias tensorboard='${ISAACSIM_PATH}/python.sh ${ISAACSIM_PATH}/tensorboard'" >> ${HOME}/.bashrc + +# make working directory as the orbit directory +# this is the default directory when the container is run +WORKDIR ${ORBIT_PATH} diff --git a/docker/container.sh b/docker/container.sh new file mode 100755 index 0000000000..3f4aec80dc --- /dev/null +++ b/docker/container.sh @@ -0,0 +1,100 @@ +#!/bin/bash + +#== +# Configurations +#== + +# Exits if error occurs +set -e + +# Set tab-spaces +tabs 4 + +# get script directory +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +#== +# Functions +#== + +# print the usage description +print_help () { + echo -e "\nusage: $(basename "$0") [-h] [run] [start] [stop] -- Utility for handling docker in Orbit." + echo -e "\noptional arguments:" + echo -e "\t-h, --help Display the help content." + echo -e "\tstart Build the docker image and create the container in detached mode." + echo -e "\tenter Begin a new bash process within an existing orbit container." + echo -e "\tcopy Copy build and logs artifacts from the container to the host machine." + echo -e "\tstop Stop the docker container and remove it." + echo -e "\n" >&2 +} + +#== +# Main +#== + +# check argument provided +if [ -z "$*" ]; then + echo "[Error] No arguments provided." >&2; + print_help + exit 1 +fi + +# check if docker is installed +if ! command -v docker &> /dev/null; then + echo "[Error] Docker is not installed! Please check the 'Docker Guide' for instruction." >&2; + exit 1 +fi + +# parse arguments +mode="$1" +# resolve mode +case $mode in + start) + echo "[INFO] Building the docker image and starting the container in the background..." + pushd ${SCRIPT_DIR} > /dev/null 2>&1 + docker compose --file docker-compose.yaml up --detach --build --remove-orphans + popd > /dev/null 2>&1 + ;; + enter) + echo "[INFO] Entering the existing 'orbit' container in a bash session..." + pushd ${SCRIPT_DIR} > /dev/null 2>&1 + docker exec --interactive --tty orbit bash + popd > /dev/null 2>&1 + ;; + copy) + # check if the container is running + if [ "$( docker container inspect -f '{{.State.Status}}' orbit 2> /dev/null)" != "running" ]; then + echo "[Error] The 'orbit' container is not running! It must be running to copy files from it." >&2; + exit 1 + fi + echo "[INFO] Copying artifacts from the 'orbit' container..." + echo -e "\t - /workspace/orbit/logs -> ${SCRIPT_DIR}/artifacts/logs" + echo -e "\t - /workspace/orbit/docs/_build -> ${SCRIPT_DIR}/artifacts/docs/_build" + # enter the script directory + pushd ${SCRIPT_DIR} > /dev/null 2>&1 + # We have to remove before copying because repeated copying without deletion + # causes strange errors such as nested _build directories + # warn the user + echo -e "[WARN] Removing the existing artifacts...\n" + rm -rf ./artifacts/logs ./artifacts/docs/_build + # create the directories + mkdir -p ./artifacts/docs + # copy the artifacts + docker cp orbit:/workspace/orbit/logs ./artifacts/logs + docker cp orbit:/workspace/orbit/docs/_build ./artifacts/docs/_build + echo -e "\n[INFO] Finished copying the artifacts from the container." + popd > /dev/null 2>&1 + ;; + stop) + echo "[INFO] Stopping the launched docker container..." + pushd ${SCRIPT_DIR} > /dev/null 2>&1 + docker compose --file docker-compose.yaml down + popd > /dev/null 2>&1 + ;; + *) + echo "[Error] Invalid argument provided: $1" + print_help + exit 1 + ;; +esac diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml new file mode 100644 index 0000000000..660e52c7f6 --- /dev/null +++ b/docker/docker-compose.yaml @@ -0,0 +1,95 @@ +services: + # This service is used to build the Docker image + # The docker image is built from the root directory + orbit: + build: + context: ../ + dockerfile: docker/Dockerfile + args: + - ISAACSIM_VERSION=${ISAACSIM_VERSION} + - ISAACSIM_PATH=${DOCKER_ISAACSIM_PATH} + image: orbit + container_name: orbit + env_file: + - .env + # We set DOCKER_ISAACSIM_PATH and then forward it to ISAACSIM_PATH within + # the container to avoid collision with pre-existing ISAACSIM_PATH env vars + # that could come from installing Orbit on the local machine, causing build errors + environment: + - ISAACSIM_PATH=${DOCKER_ISAACSIM_PATH} + - DISPLAY=${DISPLAY} + volumes: + # These volumes follow from this page + # https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/install_faq.html#save-isaac-sim-configs-on-local-disk + - type: volume + source: isaac-cache + target: ${DOCKER_ISAACSIM_PATH}/kit/cache/Kit + - type: volume + source: isaac-cache + target: ${DOCKER_USER_HOME}/.cache/ov + - type: volume + source: isaac-cache + target: ${DOCKER_USER_HOME}/.cache/pip + - type: volume + source: isaac-cache + target: ${DOCKER_USER_HOME}/.cache/nvidia/GLCache + - type: volume + source: isaac-cache + target: ${DOCKER_USER_HOME}/.nv/ComputeCache + - type: volume + source: isaac-logs + target: ${DOCKER_USER_HOME}/.nvidia-omniverse/logs + - type: volume + source: isaac-data + target: ${DOCKER_USER_HOME}/.local/share/ov/data + - type: volume + source: isaac-docs + target: ${DOCKER_USER_HOME}/Documents + # These volumes allow X11 Forwarding + - type: bind + source: /tmp/.X11-unix + target: /tmp/.X11-unix + - type: bind + source: ${HOME}/.Xauthority + target: ${DOCKER_USER_HOME}/.Xauthority + # This overlay allows changes on the local files to + # be reflected within the container immediately + - type: bind + source: ../source + target: /workspace/orbit/source + - type: bind + source: ../docs + target: /workspace/orbit/docs + # The effect of these volumes is twofold: + # 1. Prevent root-owned files from flooding the _build and logs dir + # on the host machine + # 2. Preserve the artifacts in persistent volumes for later copying + # to the host machine + - type: volume + source: orbit-docs + target: /workspace/orbit/docs/_build/ + - type: volume + source: orbit-logs + target: /workspace/orbit/logs + network_mode: host + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [ gpu ] + # This is the entrypoint for the container + entrypoint: bash + stdin_open: true + tty: true + +volumes: + # isaac-sim + isaac-cache: + isaac-logs: + isaac-data: + isaac-docs: + # orbit + orbit-docs: + orbit-logs: diff --git a/docs/index.rst b/docs/index.rst index 6d764f5be4..cb47ac2f06 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -35,6 +35,7 @@ If you use ``orbit`` in your work, please cite the `paper + + +Directory Organization +---------------------- + +The root of the Orbit repository contains the ``docker`` directory that has various files and scripts +needed to run Orbit inside a Docker container. These are summarized below: + +* ``Dockerfile``: Defines orbit image by overlaying Orbit dependencies onto the Isaac Sim Docker image. +* ``docker-compose.yaml``: Creates mounts to allow direct editing of Orbit code from the host machine that runs + the container along with X11 forwarding. It also creates several named volumes such as ``isaac-cache`` to store frequently + re-used resources compiled by Isaac Sim, such as shaders, and to retain logs, data, and documents. +* ``.env``: Stores environment variables required for the build process and the container itself. +* ``container.sh``: A script that wraps the ``docker-compose`` command to build the image and run the container. + +Running the Container +--------------------- + +.. note:: + + The docker container copies all the files from the repository into the container at the + location ``/workspace/orbit``. This means that any changes made to the files in the container will not + be reflected in the repository. + + To deal with this, we mount the following directories in the Orbit repository into the container + so that you can edit their files from the host machine: + + * ``source``: This is the directory that contains the Orbit source code. + * ``docs``: This is the directory that contains the source code for Orbit documentation. This is overlaid except + for the ``_build`` subdirectory where build artifacts are stored. + + +The script ``container.sh`` wraps around three basic ``docker-compose`` commands: + +1. ``start``: This builds the image and brings up the container in detached mode (i.e. in the background). +2. ``enter``: This begins a new bash process in an existing orbit container, and which can be exited + without bringing down the container. +3. ``copy``: This copies the ``logs`` and ``docs/_build`` artifacts, from the ``orbit-logs`` and ``orbit-docs`` + volumes respectively, to the ``docker/artifacts`` directory. These artifacts persist between docker + container instances. +4. ``stop``: This brings down the container and removes it. + +Following shows how to launch the container in a detached state and enter it: + +.. code:: bash + + # Launch the container in detached mode + ./docker/container.sh start + # Enter the container + ./docker/container.sh enter + +To copy files from the container to the host machine, you can use the following command: + +.. code:: bash + + # Copy the file /workspace/orbit/logs to the current directory + docker cp orbit:/workspace/orbit/logs . + +The script ``container.sh`` provides a wrapper around this command to copy the ``logs`` and ``docs/_build`` +directories to the ``docker/artifacts`` directory. This is useful for copying the logs and documentation: + +.. code:: bash + + # Copy the logs and docs/_build directories to the docker/artifacts directory + ./docker/container.sh copy + +To stop the container, you can use the following command: + +.. code:: bash + + # stop the container + ./docker/container.sh stop + + +Frequently Asked Questions +-------------------------- + +Python Interpreter +~~~~~~~~~~~~~~~~~~ + +The container uses the Python interpreter provided by Isaac Sim. This interpreter is located at +``/isaac-sim/python.sh``. We set aliases inside the container to make it easier to run the Python +interpreter. You can use the following commands to run the Python interpreter: + +.. code:: bash + + # Run the Python interpreter -> points to /isaac-sim/python.sh + python + + +Understanding the mounted volumes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``docker-compose.yaml`` file creates several named volumes that are mounted to the container. +These are summarized below: + +* ``isaac-cache``: This volume is used to store frequently re-used resources compiled by Omniverse, such as shaders. +* ``isaac-logs``: This volume is used to store logs generated by Omniverse. +* ``isaac-data``: This volume is used to store data generated by Omniverse. +* ``isaac-docs``: This volume is used to store documents generated by Omniverse. +* ``orbit-docs``: This volume is used to store documentation of Orbit when built inside the container. +* ``orbit-logs``: This volume is used to store logs generated by Orbit workflows when ran inside the container. + +To view the contents of these volumes, you can use the following command: + +.. code:: bash + + # list all volumes + docker volume ls + # inspect a specific volume, e.g. isaac-cache + docker volume inspect isaac-cache + + +Invalid mount config for type "bind" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you see the following error when building the container: + +.. code:: text + + ⠋ Container orbit Creating 0.0s + Error response from daemon: invalid mount config for type "bind": bind source path does not exist: ${HOME}/.Xauthority + +This means that the ``.Xauthority`` file is not present in the home directory of the host machine. +This file is required for X11 forwarding to work. To fix this, you can create an empty ``.Xauthority`` +file in your home directory. + +.. code:: bash + + touch ${HOME}/.Xauthority + +A similar error but requires a different fix: + +.. code:: text + + ⠋ Container orbit Creating 0.0s + Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /tmp/.X11-unix + +This means that the folder/files are either not present or not accessible on the host machine. This usually happens +when you have multiple docker versions installed on your machine. To fix this, you can try the following: + +* Remove all docker versions from your machine. + + .. code:: bash + + sudo apt remove docker* + sudo apt remove docker docker-engine docker.io containerd runc docker-desktop docker-compose-plugin + sudo snap remove docker + sudo apt clean autoclean && sudo apt autoremove --yes + +* Install the latest version of docker based on the instructions in the setup section. + + +Known Issues +------------ + +WebRTC and WebSocket Streaming +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When streaming the GUI from Isaac Sim, there are `several options`_ available. There is a `known issue`_ when +attempting to use WebRTC streaming client on Google Chrome and Safari while running Isaac Sim inside a container. +To avoid this problem, we suggest using either the Native Streaming Client or WebSocket options, or using the +Mozilla Firefox browser on which WebRTC works. + + +.. _`NVIDIA Omniverse EULA`: https://docs.omniverse.nvidia.com/app_isaacsim/common/NVIDIA_Omniverse_License_Agreement.html +.. _`container installation`: https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/install_container.html +.. _`Docker website`: https://docs.docker.com/desktop/install/linux-install/ +.. _`docker-compose`: https://docs.docker.com/compose/install/linux/#install-using-the-repository +.. _`NVIDIA Container Toolkit`: https://github.com/NVIDIA/nvidia-container-toolkit +.. _`Container Toolkit website`: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html +.. _`post-installation steps`: https://docs.docker.com/engine/install/linux-postinstall/ +.. _`Isaac Sim container`: https://catalog.ngc.nvidia.com/orgs/nvidia/containers/isaac-sim +.. _`NGC API key`: https://docs.nvidia.com/ngc/gpu-cloud/ngc-user-guide/index.html#generating-api-key +.. _several options: https://docs.omniverse.nvidia.com/app_isaacsim/app_isaacsim/manual_livestream_clients.html +.. _known issue: https://forums.developer.nvidia.com/t/unable-to-use-webrtc-when-i-run-runheadless-webrtc-sh-in-remote-headless-container/222916 diff --git a/docs/source/setup/installation.rst b/docs/source/setup/installation.rst index 5759dab8e8..d3ef9567a0 100644 --- a/docs/source/setup/installation.rst +++ b/docs/source/setup/installation.rst @@ -153,7 +153,7 @@ utilities to manage extensions: ./orbit.sh --help - usage: orbit.sh [-h] [-i] [-e] [-f] [-p] [-s] [-v] [-d] [-c] -- Utility to manage extensions in Orbit. + usage: orbit.sh [-h] [-i] [-e] [-f] [-p] [-s] [-o] [-v] [-d] [-c] -- Utility to manage extensions in Orbit. optional arguments: -h, --help Display the help content. @@ -162,6 +162,7 @@ utilities to manage extensions: -f, --format Run pre-commit to format the code and check lints. -p, --python Run the python executable (python.sh) provided by Isaac Sim. -s, --sim Run the simulator executable (isaac-sim.sh) provided by Isaac Sim. + -o, --docker Run the docker container helper script (docker/container.sh). -v, --vscode Generate the VSCode settings file from template. -d, --docs Build the documentation from source using sphinx. -c, --conda [NAME] Create the conda environment for Orbit. Default name is 'orbit'. diff --git a/orbit.sh b/orbit.sh index 77185e2d75..7b7a6c56c7 100755 --- a/orbit.sh +++ b/orbit.sh @@ -177,7 +177,7 @@ update_vscode_settings() { # print the usage description print_help () { - echo -e "\nusage: $(basename "$0") [-h] [-i] [-e] [-f] [-p] [-s] [-v] [-d] [-c] -- Utility to manage extensions in Orbit." + echo -e "\nusage: $(basename "$0") [-h] [-i] [-e] [-f] [-p] [-s] [-o] [-v] [-d] [-c] -- Utility to manage extensions in Orbit." echo -e "\noptional arguments:" echo -e "\t-h, --help Display the help content." echo -e "\t-i, --install Install the extensions inside Isaac Orbit." @@ -185,6 +185,7 @@ print_help () { echo -e "\t-f, --format Run pre-commit to format the code and check lints." echo -e "\t-p, --python Run the python executable (python.sh) provided by Isaac Sim." echo -e "\t-s, --sim Run the simulator executable (isaac-sim.sh) provided by Isaac Sim." + echo -e "\t-o, --docker Run the docker container helper script (docker/container.sh)." echo -e "\t-v, --vscode Generate the VSCode settings file from template." echo -e "\t-d, --docs Build the documentation from source using sphinx." echo -e "\t-c, --conda [NAME] Create the conda environment for Orbit. Default name is 'orbit'." @@ -214,6 +215,9 @@ while [[ $# -gt 0 ]]; do # this does not check dependencies between extensions export -f extract_python_exe export -f install_orbit_extension + # downgrade setuptools to avoid issues with OpenAI Gym + # Check the `Known Issues` section in the documentation + $(extract_python_exe) -m pip install --upgrade setuptools==66 # source directory find -L "${ORBIT_PATH}/source/extensions" -mindepth 1 -maxdepth 1 -type d -exec bash -c 'install_orbit_extension "{}"' \; # unset local variables @@ -289,6 +293,15 @@ while [[ $# -gt 0 ]]; do # exit neatly break ;; + -o|--docker) + # run the docker container helper script + docker_script=${ORBIT_PATH}/docker/container.sh + echo "[INFO] Running docker utility script from: ${docker_script}" + shift # past argument + bash ${docker_script} $@ + # exit neatly + break + ;; -v|--vscode) # update the vscode settings update_vscode_settings