# Triton Inference Server

**NVIDIA Triton 推理服务器** 是一个面向生产、开源的推理服务平台，支持几乎所有主流的机器学习框架。Triton 为高吞吐、低延迟的服务而设计，能够处理 PyTorch、TensorFlow、ONNX、TensorRT、OpenVINO 等——全部在单个服务器进程中运行。将其部署在 Clore.ai 的 GPU 云上以获得可扩展且具有成本效益的推理基础设施。

***

## 什么是 Triton 推理服务器？

Triton 是 NVIDIA 针对大规模部署机器学习模型服务这一挑战的解决方案：

* **多框架：** PyTorch、TensorFlow、TensorRT、ONNX、OpenVINO、Python 自定义后端
* **并发执行：** 多个模型，每个 GPU 上多个实例
* **动态批处理：** 自动对请求进行批处理以提高吞吐量
* **gRPC + HTTP：** 开箱即用的行业标准协议
* **指标：** 兼容 Prometheus 的指标端点
* **模型仓库：** 基于文件系统的模型管理

**使用的端口：**

| 端口   | 协议   | 用途            |
| ---- | ---- | ------------- |
| 8000 | HTTP | REST 推理 API   |
| 8001 | gRPC | gRPC 推理 API   |
| 8002 | HTTP | Prometheus 指标 |

***

## 先决条件

| 要求       | 最低                         | 推荐              |
| -------- | -------------------------- | --------------- |
| GPU 显存   | 8 GB                       | 16–24 GB        |
| GPU      | 任何支持 CUDA 11+ 的 NVIDIA GPU | RTX 4090 / A100 |
| 内存 (RAM) | 16 GB                      | 32 GB           |
| 存储       | 20 GB                      | 50 GB           |

{% hint style="info" %}
Triton 也支持仅 CPU 的推理以处理无 CUDA 的工作负载。对于不需要 GPU 的批处理作业，可使用 `仅 CPU` 变体的 Docker 镜像以节省成本。
{% endhint %}

***

## 步骤 1 — 在 Clore.ai 上租用 GPU

1. 登录到 [clore.ai](https://clore.ai).
2. 点击 **市场** 并筛选 VRAM ≥ 16 GB。
3. 选择一台服务器并点击 **配置**.
4. 设置 Docker 镜像： **`nvcr.io/nvidia/tritonserver:24.01-py3`**
   * （将 `24.01` 替换为最新版本 — 请检查 [NGC 目录](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/tritonserver))
5. 设置开放端口： `22` （SSH）， `8000` （HTTP）， `8001` （gRPC）， `8002` （指标）。
6. 点击 **租用**.

{% hint style="warning" %}
Triton Docker 镜像较大（约 15–20 GB）。首次启动时拉取需 3–5 分钟。后续启动较快。
{% endhint %}

***

## 步骤 2 — 自定义 Dockerfile（包含 SSH）

官方 Triton 镜像不包含 SSH 服务。使用此 Dockerfile：

```dockerfile
FROM nvcr.io/nvidia/tritonserver:24.01-py3

RUN apt-get update && apt-get install -y \
    openssh-server \
    wget curl \
    && rm -rf /var/lib/apt/lists/*

# 配置 SSH
RUN mkdir /var/run/sshd && \
    echo 'root:clore123' | chpasswd && \
    sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# 安装 Python 客户端库
RUN pip install tritonclient[all] numpy Pillow

RUN mkdir -p /models

EXPOSE 22 8000 8001 8002

CMD service ssh start && \
    tritonserver \
        --model-repository=/models \
        --log-verbose=0 \
        --http-port=8000 \
        --grpc-port=8001 \
        --metrics-port=8002
```

***

## 步骤 3 — 了解模型仓库

Triton 从 **模型仓库** 加载模型 — 一个具有特定结构的目录：

```
/models/
├── model_name_1/
│   ├── config.pbtxt          # 模型配置
│   ├── 1/                    # 版本 1
│   │   └── model.pt          # 模型文件
│   └── 2/                    # 版本 2（可选）
│       └── model.pt
├── model_name_2/
│   ├── config.pbtxt
│   └── 1/
│       └── model.onnx
```

每个模型需要：

1. 一个以模型名称命名的目录
2. 一个 `config.pbtxt` 配置文件
3. 至少一个版本子目录（例如， `1/`）并包含模型文件

***

## 步骤 4 — 部署 PyTorch 模型

### 导出模型为 TorchScript

```python
import torch
import torchvision

# 加载预训练的 ResNet50
model = torchvision.models.resnet50(pretrained=True)
model.eval()

# 导出为 TorchScript
example_input = torch.randn(1, 3, 224, 224)
traced_model = torch.jit.trace(model, example_input)

# 保存
traced_model.save("/tmp/resnet50.pt")
print("模型导出成功")
```

### 设置模型仓库

```bash
# SSH 登录到你的 Clore.ai 实例
ssh root@<clore-host> -p <port>

# 创建目录结构
mkdir -p /models/resnet50/1

# 上传模型
#（从你的本地机器）
scp -P <port> /tmp/resnet50.pt root@<clore-host>:/models/resnet50/1/model.pt
```

### 创建 config.pbtxt

```bash
cat > /models/resnet50/config.pbtxt << 'EOF'
name: "resnet50"
platform: "pytorch_libtorch"
max_batch_size: 32

input [
  {
    name: "input__0"
    data_type: TYPE_FP32
    dims: [3, 224, 224]
  }
]

output [
  {
    name: "output__0"
    data_type: TYPE_FP32
    dims: [1000]
  }
]

dynamic_batching {
  preferred_batch_size: [8, 16, 32]
  max_queue_delay_microseconds: 100
}

instance_group [
  {
    count: 2
    kind: KIND_GPU
    gpus: [0]
  }
]
EOF
```

***

## 步骤 5 — 部署 ONNX 模型

### 导出为 ONNX

```python
import torch
import torchvision
import torch.onnx

model = torchvision.models.resnet50(pretrained=True)
model.eval()

dummy_input = torch.randn(1, 3, 224, 224)

torch.onnx.export(
    model,
    dummy_input,
    "/tmp/resnet50.onnx",
    opset_version=13,
    input_names=["images"],
    output_names=["logits"],
    dynamic_axes={
        "images": {0: "batch_size"},
        "logits": {0: "batch_size"}
    }
)
```

### ONNX 配置

```bash
mkdir -p /models/resnet50_onnx/1
scp -P <port> /tmp/resnet50.onnx root@<clore-host>:/models/resnet50_onnx/1/model.onnx

cat > /models/resnet50_onnx/config.pbtxt << 'EOF'
name: "resnet50_onnx"
platform: "onnxruntime_onnx"
max_batch_size: 32

input [
  {
    name: "images"
    data_type: TYPE_FP32
    dims: [3, 224, 224]
  }
]

output [
  {
    name: "logits"
    data_type: TYPE_FP32
    dims: [1000]
  }
]

dynamic_batching {
  preferred_batch_size: [8, 16, 32]
  max_queue_delay_microseconds: 100
}
EOF
```

***

## 步骤 6 — 部署 Python 自定义后端

对于不适合标准后端的模型（自定义预处理、集成逻辑）：

```bash
mkdir -p /models/custom_model/1

cat > /models/custom_model/1/model.py << 'EOF'
import triton_python_backend_utils as pb_utils
import numpy as np
import torch

class TritonPythonModel:
    def initialize(self, args):
        self.model = torch.nn.Linear(10, 5).cuda()
        self.model.eval()
    
    def execute(self, requests):
        responses = []
        for request in requests:
            input_tensor = pb_utils.get_input_tensor_by_name(request, "INPUT")
            input_np = input_tensor.as_numpy()
            
            with torch.no_grad():
                inp = torch.from_numpy(input_np).float().cuda()
                out = self.model(inp).cpu().numpy()
            
            output_tensor = pb_utils.Tensor("OUTPUT", out.astype(np.float32))
            responses.append(pb_utils.InferenceResponse(output_tensors=[output_tensor]))
        
        return responses
    
    def finalize(self):
        pass
EOF

cat > /models/custom_model/config.pbtxt << 'EOF'
name: "custom_model"
backend: "python"
max_batch_size: 64

input [
  {
    name: "INPUT"
    data_type: TYPE_FP32
    dims: [10]
  }
]

output [
  {
    name: "OUTPUT"
    data_type: TYPE_FP32
    dims: [5]
  }
]
EOF
```

***

## 步骤 7 — 启动 Triton 并测试

### 启动 Triton 服务器

```bash
# 启动（如果使用 Dockerfile 的 CMD，则会自动启动）
tritonserver \
    --model-repository=/models \
    --http-port=8000 \
    --grpc-port=8001 \
    --metrics-port=8002 \
    --log-verbose=0 &

# 等待服务器就绪
sleep 5
curl -s http://localhost:8000/v2/health/ready
# 预期：{"live": true}
```

### 检查可用模型

```bash
curl http://<clore-host>:<public-8000>/v2/models
```

### 通过 HTTP 运行推理

```python
import tritonclient.http as httpclient
import numpy as np

client = httpclient.InferenceServerClient(
    url="<clore-host>:<public-port-8000>",
    ssl=False
)

# 检查服务器健康状况
print("服务器就绪：", client.is_server_ready())
print("模型就绪：", client.is_model_ready("resnet50_onnx"))

# 创建输入
image = np.random.rand(1, 3, 224, 224).astype(np.float32)
input_tensor = httpclient.InferInput("images", image.shape, "FP32")
input_tensor.set_data_from_numpy(image)

# 运行推理
outputs = [httpclient.InferRequestedOutput("logits")]
response = client.infer("resnet50_onnx", [input_tensor], outputs=outputs)

logits = response.as_numpy("logits")
predicted_class = np.argmax(logits[0])
print(f"预测类别：{predicted_class}")
```

### 通过 gRPC 运行推理

```python
import tritonclient.grpc as grpcclient
import numpy as np

client = grpcclient.InferenceServerClient(
    url="<clore-host>:<public-port-8001>"
)

image = np.random.rand(1, 3, 224, 224).astype(np.float32)
input_tensor = grpcclient.InferInput("images", image.shape, "FP32")
input_tensor.set_data_from_numpy(image)

outputs = [grpcclient.InferRequestedOutput("logits")]
response = client.infer("resnet50_onnx", [input_tensor], outputs=outputs)

logits = response.as_numpy("logits")
print(f"输出形状：{logits.shape}")
```

***

## 使用 Prometheus 进行监控

Triton 在端口 8002 暴露指标：

```bash
curl http://<clore-host>:<public-port-8002>/metrics
```

关键指标：

```
# 推理吞吐量
nv_inference_request_success{model="resnet50_onnx", version="1"}
# 平均推理时间
nv_inference_compute_infer_duration_us{model="resnet50_onnx", version="1"}
# GPU 利用率
nv_gpu_utilization{gpu_uuid="..."}
# GPU 内存
nv_gpu_memory_used_bytes{gpu_uuid="..."}
```

***

## 动态批处理配置

```protobuf
dynamic_batching {
  preferred_batch_size: [4, 8, 16, 32]
  max_queue_delay_microseconds: 5000
  preserve_ordering: true
  
  priority_levels: 3
  default_priority_level: 2
  default_queue_policy {
    timeout_action: REJECT
    default_timeout_microseconds: 10000
    allow_timeout_override: true
    max_queue_size: 100
  }
}
```

***

## RuntimeError: CUDA out of memory

### 模型加载失败

```
加载模型失败：无法找到模型文件
```

**解决方案：** 检查目录结构和权限：

```bash
ls -la /models/resnet50/1/
# 必须包含 model.pt（PyTorch）或 model.onnx（ONNX）
chmod -R 755 /models/
```

### CUDA 不兼容

**解决方案：** 将 Triton 镜像版本与您的 CUDA 驱动匹配：

```bash
nvidia-smi  # 查看 CUDA 版本
# 使用匹配的 tritonserver 标签，例如针对 CUDA 12.2 的 23.10
```

### 端口不可访问

**解决方案：** 确认在 Clore.ai 中已转发所有三个端口（8000、8001、8002）。逐个测试：

```bash
curl http://<host>:<port>/v2/health/live
```

### 模型加载时 OOM（内存不足）

**解决方案：** 减少实例数量或对部分模型使用 CPU 实例：

```protobuf
instance_group [
  {
    count: 1       # 从默认值减少
    kind: KIND_GPU
  }
]
```

***

## 成本估算

| GPU       | 显存 (VRAM) | 预计价格       | 吞吐量（ResNet50） |
| --------- | --------- | ---------- | ------------- |
| RTX 3080  | 10 GB     | 约 $0.10/小时 | 约 500 请求/秒    |
| RTX 4090  | 24 GB     | 约 $0.35/小时 | 约 1500 请求/秒   |
| A100 40GB | 40 GB     | 约 $0.80/小时 | 约 3000 请求/秒   |
| H100      | 80 GB     | 约 $2.50/小时 | 约 8000 请求/秒   |

***

## 有用的资源

* [Triton GitHub](https://github.com/triton-inference-server/server)
* [NGC 容器注册表](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/tritonserver)
* [Triton 客户端库](https://github.com/triton-inference-server/client)
* [Triton 模型配置参考](https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/user_guide/model_configuration.html)
* [Triton Python 后端](https://github.com/triton-inference-server/python_backend)
* [Triton 性能分析器](https://github.com/triton-inference-server/client/blob/main/src/c%2B%2B/perf_analyzer/README.md)

***

## 推荐 GPU

| 在 Clore.ai 上的预估费用 | 开发/测试             | RTX 3090（24GB） |
| ----------------- | ----------------- | -------------- |
| \~$0.12/每 GPU/每小时 | 生产                | RTX 4090（24GB） |
| 生产级推理             | 大规模               | A100 80GB      |
| 大型模型（70B+）        | 💡 本指南中的所有示例均可部署在 | Clore.ai       |

> GPU 服务器上。浏览可用 GPU 并按小时租用 — 无需承诺，提供完整的 root 访问权限。 [Clore.ai](https://clore.ai/marketplace) GPU 服务器。浏览可用 GPU 并按小时租用 — 无需承诺，提供完整的 root 访问权限。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.clore.ai/guides/guides_v2-zh/mlops-yu-bu-shu/triton-inference-server.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
