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
Check if it is installed:
$ docker buildx version github.com/docker/buildx v0.10.5 86bdced
If not, run the following commands based on the official doc:
# 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 PATHSystem python: ensure that
$HOME/.local/bin
is added to yourPATH
. If using bash, the following command will make it persist across sessions: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:
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:
echo 'alias holoscan=<BUILD_OR_INSTALL_DIR>/bin/holoscan' >> ~/.bashrc
The NGC container has the CLI installed already, no additional steps are required.
The packager feature is also illustrated in the cli_packager and video_replayer_distributed examples.
Additional arguments are required when launching the container to enable the packaging of Holoscan applications inside the NGC Holoscan container. Please see the NGC Holoscan container page for additional details.
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 theapp->config(config_path)
function with the default configuration file. Theapp->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 variableHOLOSCAN_CONFIG_PATH
is set and uses that value as the source. If neither were set, the default configuration file (config_path
) is used.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 theapp.config(config_file_path)
function with the default configuration file. Theapp.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 variableHOLOSCAN_CONFIG_PATH
is set and uses that value as the source. If neither were set, the default configuration file (config_file_path
) is used.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 InputIn
Fragment1
, we try to set the input video directory with the value defined inHOLOSCAN_INPUT_PATH
. When we instantiate a new Video Stream Replayer operator, we pass in all configuration values from thefrom_config("replayer")
call. In addition, we includeargs
that we created with the value fromHOLOSCAN_INPUT_PATH
if available as the last argument to override thedirectory
setting.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 inHOLOSCAN_INPUT_PATH
. When we instantiate a new Video Stream Replayer operator, we pass in thevideo_path
along with allreplayer
configurations found in the configuration file.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")
Include a YAML configuration file as described in the Application Runner Configuration page.
Use the
holoscan package
command to create a HAP container image. For example:holoscan package --platform x64-workstation --tag my-awesome-app --config /path/to/my/awesome/application/config.yaml /path/to/my/awesome/application/
Common Issues When Using Holoscan Packager
DNS Name Resolution Error
The Holoscan Packager may be unable to resolve hostnames in specific networking environments and may show errors similar to the following:
curl: (6) Could not resolve host: github.com.
Failed to establish a new connection:: [Errno -3] Temporary failure in name solution...
To resolve these errors, edit the /etc/docker/daemon.json
file to include dns
and dns-serach
fields as follows:
{
"default-runtime": "nvidia",
"runtimes": {
"nvidia": {
"args": [],
"path": "nvidia-container-runtime"
}
},
"dns": ["IP-1", "IP-n"],
"dns-search": ["DNS-SERVER-1", "DNS-SERVER-n"]
}
You may need to consult your IT team and replace IP-x
and DNS-SERVER-x
with the provided values.
The packaged Holoscan application container image can run with the Holoscan App Runner:
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:
docker run -it my-container-image[:tag] help
The command should prints following:
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.
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:
# 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