打包 Holoscan 应用程序
作为 Holoscan CLI 的一部分包含的 Holoscan 应用程序打包器,以 package
命令的形式,允许您将 Holoscan 应用程序打包到符合 HAP 标准 的容器镜像中,以便分发和部署。
依赖项
确保在要运行 CLI 的环境中安装以下组件
带有 Docker 的 NVIDIA Container Toolkit
开发者套件 (aarch64):已包含在 IGX Software 和 JetPack 中
x86_64:已使用 NVIDIA Container Toolkit 1.13.3 和 Docker v24.0.1 进行测试
Docker BuildX 插件
检查是否已安装
$ docker buildx version github.com/docker/buildx v0.10.5 86bdced
如果未安装,请根据 官方文档 运行以下命令
# 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 (可选)
用于打包与主机架构不同的容器镜像(例如:x86_64 -> arm64)。
CLI 安装
Holoscan CLI 可作为 PyPI 包使用,可以使用以下命令安装
$ pip install holoscan-cli
验证安装
$ holoscan version
始终先安装 Holoscan SDK,然后在需要同时安装 SDK 和 CLI 的系统上安装 Holoscan CLI。这可确保正确安装所有必要的依赖项和软件包,从而使 Holoscan CLI 能够顺利运行。
打包器功能也在 cli_packager 和 video_replayer_distributed 示例中进行了说明。
启动容器时,需要额外的参数才能在 NGC Holoscan 容器内启用 Holoscan 应用程序的打包。有关更多详细信息,请参阅 NGC Holoscan 容器页面。
在访问数据时,请尽可能使用 HAP 环境变量。例如
让我们看一下分布式视频重放器示例(
examples/video_replayer_distributed
)。使用应用程序配置文件
在
main
函数中,我们使用默认配置文件调用app->config(config_path)
函数。app->config(...)
检查应用程序是否首先使用--config
参数执行。如果设置了--config
,则该方法使用来自--config
参数的配置文件。否则,它会检查是否设置了环境变量HOLOSCAN_CONFIG_PATH
,并将该值用作源。如果两者均未设置,则使用默认配置文件 (config_path
)。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; }
在
main
函数中,我们使用默认配置文件调用app.config(config_file_path)
函数。app.config(...)
方法检查应用程序是否首先使用--config
参数执行。如果设置了--config
,则该方法使用来自--config
参数的配置文件。否则,它会检查是否设置了环境变量HOLOSCAN_CONFIG_PATH
,并将该值用作源。如果两者均未设置,则使用默认配置文件 (config_file_path
)。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()
使用环境变量
HOLOSCAN_INPUT_PATH
进行数据输入在
Fragment1
中,我们尝试使用HOLOSCAN_INPUT_PATH
中定义的值设置输入视频目录。当我们实例化新的视频流重放器操作器时,我们从from_config("replayer")
调用中传入所有配置值。此外,如果HOLOSCAN_INPUT_PATH
中的值可用,我们将使用该值创建的args
作为最后一个参数包含在内,以覆盖directory
设置。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); } };
在
Fragment1
中,我们尝试使用HOLOSCAN_INPUT_PATH
中定义的值设置输入视频目录。当我们实例化新的视频流重放器操作器时,我们传入video_path
以及在配置文件中找到的所有replayer
配置。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")
包含 YAML 配置文件,如 应用程序运行器配置 页面中所述。
使用
holoscan package
命令创建 HAP 容器镜像。例如holoscan package --platform x64-workstation --tag my-awesome-app --config /path/to/my/awesome/application/config.yaml /path/to/my/awesome/application/
非分布式应用程序的附加要求
除了使用 HAP 定义的环境变量之外,应用程序在打包非分布式应用程序时还必须处理 —config
参数的解析。
处理
--config
参数定义一个
parse_arguments
函数来处理数据路径和配置文件路径的解析bool parse_arguments(int argc, char** argv, std::string& data_path, std::string& config_path) { static struct option long_options[] = { {"data", required_argument, 0, 'd'}, {"config", required_argument, 0, 'c'}, {0, 0, 0, 0}}; while (int c = getopt_long(argc, argv, "d:c:", long_options, NULL)) { if (c == -1 || c == '?') break; switch (c) { case 'c': config_path = optarg; break; case 'd': data_path = optarg; break; default: holoscan::log_error("Unhandled option '{}'", static_cast<char>(c)); return false; } } return true; }
在
main
函数中使用上面定义的parse_arguments()
函数int main(int argc, char** argv) { // Parse the arguments std::string config_path = ""; std::string data_directory = ""; if (!parse_arguments(argc, argv, data_directory, config_path)) { return 1; } if (data_directory.empty()) { // Get the input data environment variable auto input_path = std::getenv("HOLOSCAN_INPUT_PATH"); if (input_path != nullptr && input_path[0] != '\0') { data_directory = std::string(input_path); } else { HOLOSCAN_LOG_ERROR( "Input data not provided. Use --data or set HOLOSCAN_INPUT_PATH environment variable."); exit(-1); } } if (config_path.empty()) { // Get the input data environment variable auto config_file_path = std::getenv("HOLOSCAN_CONFIG_PATH"); if (config_file_path == nullptr || config_file_path[0] == '\0') { auto config_file = std::filesystem::canonical(argv[0]).parent_path(); config_path = config_file / std::filesystem::path("app-config.yaml"); } else { config_path = config_file_path; } } auto app = holoscan::make_application<App>(); HOLOSCAN_LOG_INFO("Using configuration file from {}", config_path); app->config(config_path); HOLOSCAN_LOG_INFO("Using input data from {}", data_directory); app->set_datapath(data_directory); app->run(); return 0; }
定义一个
parse_arguments
函数来解析参数def parse_arguments() -> argparse.Namespace: parser = argparse.ArgumentParser(description="My Application") parser.add_argument( "--data", type=str, required=False, default=os.environ.get("HOLOSCAN_INPUT_PATH", None), help="Input dataset", ) parser.add_argument( "--config", type=str, required=False, default=os.environ.get( "HOLOSCAN_CONFIG_PATH", os.path.join(os.path.dirname(__file__), "app-config.yaml"), ), help="Application configurations", ) args, _ = parser.parse_known_args() return args
使用上面定义的
parse_arguments()
解析参数并启动应用程序if __name__ == "__main__": args = parse_arguments() if args.data is None: logger.error( "Input data not provided. Use --data or set HOLOSCAN_INPUT_PATH environment variable." ) sys.exit(-1) app = MyApp(args.data) app.config(args.config) app.run()
使用 Holoscan 打包器时的常见问题
DNS 名称解析错误
Holoscan 打包器可能无法在特定的网络环境中解析主机名,并可能显示类似于以下的错误
curl: (6) Could not resolve host: github.com.
Failed to establish a new connection:: [Errno -3] Temporary failure in name solution...
要解决这些错误,请编辑 /etc/docker/daemon.json
文件以包含 dns
和 dns-serach
字段,如下所示
{
"default-runtime": "nvidia",
"runtimes": {
"nvidia": {
"args": [],
"path": "nvidia-container-runtime"
}
},
"dns": ["IP-1", "IP-n"],
"dns-search": ["DNS-SERVER-1", "DNS-SERVER-n"]
}
您可能需要咨询您的 IT 团队,并将 IP-x
和 DNS-SERVER-x
替换为提供的值。
打包的 Holoscan 应用程序容器镜像可以使用 Holoscan 应用程序运行器 运行
holoscan run -i /path/to/my/input -o /path/to/application/generated/output my-application:1.0.1
由于打包的 Holoscan 应用程序容器镜像符合 OCI 标准,因此它们也与 Docker、Kubernetes 和 containerd 兼容。
每个打包的 Holoscan 应用程序容器镜像都包含用于提取嵌入式应用程序、清单文件、模型等的工具。要访问该工具并查看所有可用选项,请运行以下命令
docker run -it my-container-image[:tag] help
该命令应打印以下内容
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.
这些工具也可以通过 /var/holoscan/tools
在 Docker 容器内访问。
例如,运行以下命令以提取清单文件和应用程序配置文件
# 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