NVIDIA Triton 开源项目的核心由六个主要仓库构成, 分别是server、core、client、backend model_analyzer、model_navigator,除此之外,还有一个专门教程仓库-tutorials,以及15个 用于后端扩展应用的仓库和多个附加管理工具仓库。这些组件共同构成了一个不断发展和扩展的生态系统,同时也提供了全面且高效的机器学习模型部署了解决方法。
NVIDIA Triton Server是一个高性能的推断服务器,具有以下特点:
-
高性能:Triton Server为使用GPU进行推断的工作负载提供了高性能和低延迟。它能够在高吞吐量和低延迟的情况下同时服务多个模型。
-
内存管理:大模型通常需要大量的显存来进行推断。Triton Server具有灵活的内存管理机制,可以有效地管理和分配显存,确保大模型的推断可以高效地进行。
-
可扩展性:Triton Server通过并行处理和异步推断支持高度并发的推断请求。它可以根据负载的需要自动扩展和收缩。
-
多模型支持:Triton Server能够同时部署和管理多个模型。这允许您共享服务器资源并以一致的方式部署和管理不同的模型。
-
灵活性:Triton Server支持多种模型格式和推断框架,包括TensorFlow、PyTorch、ONNX等。您可以使用您喜欢的模型和工具进行模型开发和训练,并将其轻松部署到Triton Server上。
-
高级特性:Triton Server提供了许多高级特性,例如模型版本管理、请求并发控制、动态批处理大小优化、请求时间跟踪等。这些特性增强了模型的部署和管理能力。
官方文档:https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/getting_started/quickstart.html
Triton架构允许在同一系统上并行执行多个模型和/或同一模型的多个实例
http 使用的是 libevent 和libevhtp库
编译server
前言:triton server 在编译过程中需要拉取太多git的依赖库,没有好的科学上网是真的很痛苦。
注意:/root下容量最好有150G以上,不然会出现在docker中编译容量不够用的情况
首先通过–dryrun 命令看清楚build.py的中间过程
##这里并没有enable 很多,只是目前所需的
./build.py -v --dryrun --enable-logging --enable-stats --enable-tracing --enable-metrics --enable-gpu-metrics --enable-gpu --endpoint=http --endpoint=grpc --backend=pytorch --backend=onnxruntime --build-type=Debug
执行该命令后会在build 目录下出现5个文件
cmake_build docker_build Dockerfile Dockerfile.buildbase Dockerfile.cibase
其中docker_build 为这几个文件的入口,可以查看脚本,
根据docker_build 脚本信息,可以手动来执行相应命令,一来熟悉编译流程,二来可以出现问题可以逐一解决,先创建一个镜像,自定义Dockerfile文件如下:
ARG TRITON_VERSION=2.45.0dev
ARG TRITON_CONTAINER_VERSION=24.04dev
ARG BASE_IMAGE=nvcr.io/nvidia/tritonserver:24.03-py3-min
FROM ${BASE_IMAGE}
ARG TRITON_VERSION
ARG TRITON_CONTAINER_VERSION
# Ensure apt-get won't prompt for selecting options
ENV DEBIAN_FRONTEND=noninteractive
#根据docker_build文件 观察,将Dockerfile.buildbase 中前几行拿出生成一个Dockerfile,再根据这个Dockerfile 构建一个镜像,
docker build -t tritonserver_buildbase -f /path/to/triton-inference-server/Dockerfile --pull --cache-from=tritonserver_buildbase --cache-from=tritonserver_buildbase_cache0 --cache-from=tritonserver_buildbase_cache1 .
#利用镜像启动一个容器,然后根据文件中的内容,一行行执行命令,生成依赖环境
docker run -w /workspace/build --name tritonserver_builder --net=host -it -e http_proxy=http://127.0.0.1:7890 -e https_proxy=http://127.0.0.1:7890 -v /var/run/docker.sock:/var/run/docker.sock -v /home/allin-sds/sharing:/home/allin-sds/shaing tritonserver_buildbase
#进入容器可以配置相应http_proxy 和https_proxy相应代理
如果编译拉去过程中出现github 库拉不下情况下,可以尝试更改拉去脚本,比如grpc ,可以更改/tmp/tritonbuild/tritonserver/build/_deps/repo-third-party-build/grpc-repo/tmp/grpc-repo-gitclone.cmake 逻辑,比如将github链接换成gitee,submodule如果也拉取不下来,可以试试更改.gitmodule 文件
#更改了submodule 需要新的URL更新到文件.git/config
git submodule sync
git submodule update --recursive --init
根据Dockerfile.buildbase 文件安装好依赖环境之后,设置好脚本中的环境变量
export PATH=${PATH}:/opt/conda/bin
export DCGM_VERSION=3.2.6
export TRITON_SERVER_VERSION=2.45.0dev
export NVIDIA_TRITON_SERVER_VERSION=24.04dev
这个时候可以根据cmake_build文件编译server
server 编译过程中可以少一点线程,虽然慢一点,但是出问题了好定位问题
cmake --build . --config Debug -j1 -v -t install
编译server 完成以后,根据脚本进行一些复制操作。
编译backend
backend 和server 是分开的,可以根据自己的需要去编译对应的backend。同样根据cmake_build 文件脚本命令编译对应的backend。编译完成backend之后需要根据命令复制到对应位置。
运行实例
现在server 和backend 编译出来了,就可以尝试运行实例了
代码里面的backend 默认存放位置/opt/tritonserver/backends,如果你的backend 不在该目录下可通过tritonserver 参数指定路径。
tritonserver二进制可执行程序是需要依赖libtritonserver.so文件的,所以需要配置LD_LIBRARY_PATH。
配置好模型仓库之后,就可以运行了,可参考https://zhuanlan.zhihu.com/p/634444666 运行一个pytorch 后端的resnet50模型。
命令参考如下:
./tritonserver --model-repository=/home/model_repository --backend-director=/tmp/tritonbuild/install/backends --log-info=True --log-verbose=1
显示如下
检查健康状态
curl -v localhost:8000/v2/health/ready
curl -v http://127.0.0.1:8000/v2/health/ready
源码分析
backend 库程序启动时动态加载的so 文件,具体可以查看shared_library.cc代码
Status
SharedLibrary::OpenLibraryHandle(const std::string& path, void** handle)
{
LOG_VERBOSE(1) << "OpenLibraryHandle: " << path;
....
*handle = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
if (*handle == nullptr) {
return Status(
Status::Code::NOT_FOUND,
"unable to load shared library: " + std::string(dlerror()));
}
....