Packaging Holoscan Applications

The Holoscan App Packager, included as part of the Holoscan CLI as the package command, allows you to package your Holoscan applications into a HAP-compliant container image for distribution and deployment.

Dependencies

Ensure the following are installed in the environment where you want to run the CLI:

  • PIP dependencies (automatically installed with the holoscan python wheel)

  • NVIDIA Container Toolkit with Docker

    • Developer Kits (aarch64): already included in IGX Software and JetPack

    • x86_64: tested with NVIDIA Container Toolkit 1.13.3 w/Docker v24.0.1

  • Docker BuildX plugin

    1. Check if it is installed:

      Copy
      Copied!
                  

      $ docker buildx version github.com/docker/buildx v0.10.5 86bdced

    2. If not, run the following commands based on the official doc:

      Copy
      Copied!
                  

      # Install Docker dependencies sudo apt-get update sudo apt-get install ca-certificates curl gnupg # Add Docker Official GPG Key sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg # Configure Docker APT Repository echo \ "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # Install Docker BuildX Plugin sudo apt-get update sudo apt-get install docker-buildx-plugin

  • QEMU (Optional)

    • used for packaging container images of different architectures than the host (example: x86_64 -> arm64)

CLI Installation

The Holoscan CLI is installed as part of the Holoscan SDK and can be called with the following instructions depending on your installation:

  • In a virtual environment: the holoscan CLI should already be in the PATH

  • System python: ensure that $HOME/.local/bin is added to your PATH. If using bash, the following command will make it persist across sessions:

    Copy
    Copied!
                

    echo 'export PATH=$HOME/.local/bin:$PATH' >> ~/.bashrc

Ensure that /opt/nvidia/holoscan/ is added to your PATH. If using bash, the following command will make it persist across sessions:

Copy
Copied!
            

echo 'alias holoscan=/opt/nvidia/holoscan/bin/holoscan' >> ~/.bashrc

If building the SDK from source and starting the build container with run launch, the holoscan CLI should already be in the PATH.

If building bare-metal (advanced), ensure that <BUILD_OR_INSTALL_DIR>/bin is added to your PATH. If using bash, the following command will make it persist across sessions:

Copy
Copied!
            

echo 'alias holoscan=<BUILD_OR_INSTALL_DIR>/bin/holoscan' >> ~/.bashrc

The NGC container has the CLI installed already, no additional steps are required.

Tip

The packager feature is also illustrated in the cli_packager and video_replayer_distributed examples.

  1. Ensure to use the HAP environment variables wherever possible when accessing data. For example:

    Let’s take a look at the distributed video replayer example (examples/video_replayer_distributed).

    • Using the Application Configuration File

      In the main function, we call the app->config(config_path) function with the default configuration file. The app->config(...) checks to see if the application was executed with --config argument first. If --config was set, the method uses the configuration file from the --config argument. Otherwise, it checks if the environment variable HOLOSCAN_CONFIG_PATH is set and uses that value as the source. If neither were set, the default configuration file (config_path) is used.

      Copy
      Copied!
                  

      int main(int argc, char** argv) { // Get the yaml configuration file auto config_path = std::filesystem::canonical(argv[0]).parent_path(); config_path /= std::filesystem::path("video_replayer_distributed.yaml"); auto app = holoscan::make_application<DistributedVideoReplayerApp>(); app->config(config_path); app->run(); return 0; }

      In the main function, we call the app.config(config_file_path) function with the default configuration file. The app.config(...) method checks to see if the application was executed with --config argument first. If --config was set, the method uses the configuration file from the --config argument. Otherwise, it checks if the environment variable HOLOSCAN_CONFIG_PATH is set and uses that value as the source. If neither were set, the default configuration file (config_file_path) is used.

      Copy
      Copied!
                  

      def main(): input_path = get_input_path() config_file_path = os.path.join(os.path.dirname(__file__), "video_replayer_distributed.yaml") logging.info(f"Reading application configuration from{config_file_path}") app = DistributedVideoReplayerApp(input_path) app.config(config_file_path) app.run()

    • Using Environment Variable HOLOSCAN_INPUT_PATH for Data Input

      In Fragment1, we try to set the input video directory with the value defined in HOLOSCAN_INPUT_PATH. When we instantiate a new Video Stream Replayer operator, we pass in all configuration values from the from_config("replayer") call. In addition, we include args that we created with the value from HOLOSCAN_INPUT_PATH if available as the last argument to override the directory setting.

      Copy
      Copied!
                  

      class Fragment1 : public holoscan::Fragment { public: void compose() override { using namespace holoscan; ArgList args; auto data_directory = std::getenv("HOLOSCAN_INPUT_PATH"); if (data_directory != nullptr && data_directory[0] != '\0') { auto video_directory = std::filesystem::path(data_directory); video_directory /= "racerx"; args.add(Arg("directory", video_directory.string())); HOLOSCAN_LOG_INFO("Using video from {}", video_directory.string()); } auto replayer = make_operator<ops::VideoStreamReplayerOp>("replayer", from_config("replayer"), args); add_operator(replayer); } };

      In Fragment1, we try to set the input video directory with the value defined in HOLOSCAN_INPUT_PATH. When we instantiate a new Video Stream Replayer operator, we pass in the video_path along with all replayer configurations found in the configuration file.

      Copy
      Copied!
                  

      class Fragment1(Fragment): def __init__(self, app, name): super().__init__(app, name) def __init__(self, app, name): super().__init__(app, name) def compose(self): # Set the video source video_path = self._get_input_path() logging.info( f"Using video from{video_path}" ) # Define the replayer and holoviz operators replayer = VideoStreamReplayerOp( self, name="replayer", directory=video_path, **self.kwargs("replayer") ) self.add_operator(replayer) def _get_input_path(self): path = os.environ.get( "HOLOSCAN_INPUT_PATH", os.path.join(os.path.dirname(__file__), "data") ) return os.path.join(path, "racerx")

  2. Include a YAML configuration file as described in the Application Runner Configuration page.

  3. Use the holoscan package command to create a HAP container image. For example:

    Copy
    Copied!
                

    holoscan package --platform x64-workstation --tag my-awesome-app --config /path/to/my/awesome/application/config.yaml /path/to/my/awesome/application/

The packaged Holoscan application container image can run with the Holoscan App Runner:

Copy
Copied!
            

holoscan run -i /path/to/my/input -o /path/to/application/generated/output my-application:1.0.1

Since the packaged Holoscan application container images are OCI-compliant, they’re also compatible with Docker, Kubernetes, and containerd.

Each packaged Holoscan application container image includes tools inside for extracting the embedded application, manifest files, models, etc. To access the tool and to view all available options, run the following:

Copy
Copied!
            

docker run -it my-container-image[:tag] help

The command should prints following:

Copy
Copied!
            

USAGE: /var/holoscan/tools [command] [arguments]... Command List extract --------------------------- Extract data based on mounted volume paths. /var/run/holoscan/export/app extract the application /var/run/holoscan/export/config extract app.json and pkg.json manifest files and application YAML. /var/run/holoscan/export/models extract models /var/run/holoscan/export/docs extract documentation files /var/run/holoscan/export extract all of the above IMPORTANT: ensure the directory to be mounted for data extraction is created first on the host system. and has the correct permissions. If the directory had been created by the container previously with the user and group being root, please delete it and manually create it again. show ----------------------------- Print manifest file(s): [app|pkg] to the terminal. app print app.json pkg print pkg.json env ------------------------- Print all environment variables to the terminal.

Note

The tools can also be accessed inside the Docker container via /var/holoscan/tools.

For example, run the following commands to extract the manifest files and the application configuration file:

Copy
Copied!
            

# create a directory on the host system first mkdir -p config-files # mount the directory created to /var/run/holoscan/export/config docker run -it --rm -v $(pwd)/config-files:/var/run/holoscan/export/config my-container-image[:tag] extract # include -u 1000 if the above command reports a permission error docker run -it --rm -u 1000 -v $(pwd)/config-files:/var/run/holoscan/export/config my-container-image[:tag] extract # If the permission error continues to occur, please check if the mounted directory has the correct permission. # If it doesn't, please recreate it or change the permissions as needed. # list files extracted ls config-files/ # output: # app.json app.yaml pkg.json

Previous Creating a Distributed Application
Next Creating Operators
© Copyright 2022-2024, NVIDIA. Last updated on Apr 23, 2024.