mlflow-tcdeploy-plugin
腾讯云模型服务部署插件 —— 将腾讯云 WeData 在线推理服务对接为 MLflow Deployments 后端。
与 mlflow-tclake-plugin(Model Registry)配套使用:
| 插件 | 职责 | 后端 |
|---|---|---|
mlflow-tclake-plugin |
模型注册与版本管理 | TCLake |
mlflow-tcdeploy-plugin |
模型在线部署与推理 | 腾讯云 WeData 推理服务 |
目录
特性
- ✅ 完整的 MLflow Deployments 生命周期:创建 / 更新 / 删除 / 查询 / 列举
- ✅ 在线推理调用(
predict) - ✅ 实例规格查询(直连 TIone SDK)
- ✅ Pod 运维:日志查看、Pod 重启
- ✅ 三段式 Model URI 解析(
models:/Catalog.Schema.Model/Version) - ✅ 自动 TC3 请求签名
- ✅ SSRF 防护:endpoint 域名白名单 + 请求路径严格校验
- ✅ snake_case ↔ PascalCase 自动转换
架构
┌─────────────────────────────────────────────────────┐
│ 用户代码 │
│ client = get_deploy_client("tcdeploy") │
│ client.create_deployment(...) │
└──────────────────┬──────────────────────────────────┘
│
┌──────────────▼──────────────┐
│ TcDeploymentClient │ ← MLflow BaseDeploymentClient
│ (client.py) │
│ - 参数校验 / SSRF 防护 │
│ - snake_case ↔ PascalCase │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ ModelServiceApiClient │ ← 封装 WeData 云 API
│ (api_client.py) │
│ - TC3 签名 (AbstractClient)│
│ - 错误码 → MlflowException │
└──────────────┬──────────────┘
│ HTTPS (TC3-HMAC-SHA256)
┌──────────────▼──────────────┐
│ WeData API │
│ (tencentcloudapi.com) │
└─────────────────────────────┘
核心模块说明:
| 模块 | 文件 | 说明 |
|---|---|---|
| Client | client.py |
实现 BaseDeploymentClient,对外暴露所有操作方法 |
| API Client | api_client.py |
封装 WeData / TIone 云 API 调用,继承腾讯云 SDK 的 AbstractClient 实现 TC3 签名 |
| Config | config.py |
环境变量 / 参数配置管理,含 endpoint 域名白名单校验 |
| Models | models.py |
请求体构建(Create/Update)、Model URI 解析、大小写转换 |
安装
pip install mlflow-tcdeploy-plugin
依赖:
mlflow >= 3.10.0, < 3.11.0tencentcloud-sdk-python >= 3.0.1478- Python >= 3.10
配置
插件通过环境变量获取配置(通常由 wedata-pre-execute 自动注入),也支持构造时直接传参:
| 环境变量 | 说明 | 必填 | 默认值 |
|---|---|---|---|
KERNEL_WEDATA_CLOUD_SDK_SECRET_ID |
腾讯云 SecretId | ✅ | — |
KERNEL_WEDATA_CLOUD_SDK_SECRET_KEY |
腾讯云 SecretKey | ✅ | — |
WEDATA_WORKSPACE_ID |
WeData 工作空间 ID | ✅ | — |
KERNEL_WEDATA_REGION |
地域 | ❌ | ap-guangzhou |
TENCENTCLOUD_ENDPOINT |
WeData API endpoint | ❌ | wedata.internal.tencentcloudapi.com |
KERNEL_WEDATA_CLOUD_SDK_SECRET_TOKEN |
临时凭证 Token(STS) | ❌ | — |
KERNEL_LOGIN_UIN |
子账号 UIN | ❌ | — |
QCLOUD_UIN |
主账号 UIN | ❌ | — |
安全提示:
TENCENTCLOUD_ENDPOINT受域名白名单保护,仅允许*.tencentcloudapi.com和*.tencentcloud.com后缀。
快速上手
from mlflow.deployments import get_deploy_client
# 初始化客户端(自动读取环境变量)
client = get_deploy_client("tcdeploy")
# 1. 查询可用规格
specs = client.list_instance_types(spec_type="CPU")
available = [s for s in specs if s["available"]]
print(available[0]["spec_name"]) # e.g. "TI.SA5.2XLARGE32.POST"
# 2. 创建部署
result = client.create_deployment(
name="my-service",
model_uri="models:/default.default.MyModel/1",
config={
"instance_type": "TI.SA5.2XLARGE32.POST",
"replicas": 1,
},
)
print(result["service_id"])
# 3. 查询部署详情(name 传入的是 ServiceId,非服务名称)
info = client.get_deployment(name="svc-xxxx")
print(info["status"])
# 4. 在线推理
response = client.predict(
deployment_name="grp-xxxx",
inputs={"text": "Hello, world!"},
)
print(response)
# 5. 更新部署(扩容到 2 副本,name = ServiceId)
client.update_deployment(name="svc-xxxx", config={"replicas": 2})
# 6. 删除部署(幂等,name = ServiceId)
client.delete_deployment(name="svc-xxxx")
API 参考
⚠️ 重要说明:在
update_deployment、delete_deployment、get_deployment等方法中,name参数传入的是腾讯云 ServiceId(如svc-xxxx),而不是服务名称。这是因为 MLflowBaseDeploymentClient接口规范使用name作为参数名,但在本插件的上下文中它对应的是 ServiceId —— 服务的唯一标识符。
create_deployment — 创建服务
client.create_deployment(
name="my-service", # 服务名称
model_uri="models:/Catalog.Schema.Model/1", # MLflow Model URI
config={ # 部署配置
"instance_type": "TI.SA5.2XLARGE32.POST", # 必填:实例规格
"replicas": 1, # 副本数(默认 1)
"charge_type": "POSTPAID_BY_HOUR", # 计费模式(默认按量后付费)
"scale_mode": "MANUAL", # 伸缩模式(默认手动)
"log_enable": False, # 是否启用日志
"authorization_enable": False, # 是否启用鉴权
"service_description": "...", # 服务描述
"log_config": {...}, # 日志配置
"scheduled_action": {...}, # 定时伸缩配置
"service_limit": {...}, # 限流配置
},
endpoint="grp-xxxx", # 可选:服务组 ID
)
调用的云 API:CreateMLModelServices
update_deployment — 更新服务
client.update_deployment(
name="svc-xxxx", # ⚠️ ServiceId(非服务名称)
model_uri="models:/Catalog.Schema.Model/2", # 可选:更新模型版本
config={ # 仅发送需更新的字段
"replicas": 3,
"instance_type": "TI.SA5.4XLARGE64.POST",
"service_port": 8080,
"status": "RUNNING",
},
)
注意:
name参数对应腾讯云的 ServiceId(如svc-xxxx),而非服务名称。受 MLflowBaseDeploymentClient接口约束,参数名为name,但语义上是服务唯一标识。
调用的云 API:UpdateMLModelService(扁平结构,非嵌套 Services 数组)
ServiceAction — 特殊更新行为
config 中可以通过 service_action 字段触发特殊操作。⚠️ 当指定 ServiceAction 时,请求中的其他更新字段会被服务端忽略。
service_action 值 |
含义 | 说明 |
|---|---|---|
"STOP" |
停止服务 | 将运行中的服务停止 |
"RESUME" |
重启服务 | 将已停止的服务恢复运行 |
"SCALE" |
扩缩容 | 触发服务扩缩容操作 |
示例:停止服务
client.update_deployment(
name="svc-xxxx",
config={"service_action": "STOP"},
)
示例:恢复服务
client.update_deployment(
name="svc-xxxx",
config={"service_action": "RESUME"},
)
示例:触发扩缩容
client.update_deployment(
name="svc-xxxx",
config={"service_action": "SCALE"},
)
delete_deployment — 删除服务
client.delete_deployment(name="svc-xxxx") # ⚠️ name = ServiceId;幂等:资源不存在时不报错
调用的云 API:DeleteMLModelService
get_deployment — 查询服务详情
info = client.get_deployment(name="svc-xxxx") # ⚠️ name = ServiceId
# info 是 snake_case 字典
print(info["status"], info["service_name"])
调用的云 API:GetMLModelService
list_deployments — 列举服务组
# 列举所有服务组
groups = client.list_deployments()
# 按服务组 ID 过滤
groups = client.list_deployments(endpoint="grp-xxxx")
# 每个 item 包含 "name" 字段(= ServiceGroupId),符合 MLflow 规范
for g in groups:
print(g["name"], g["service_group_name"])
调用的云 API:ListMLModelServiceGroups
predict — 在线推理
result = client.predict(
deployment_name="grp-xxxx", # 服务组 ID
inputs={"text": "classify this"}, # JSON 可序列化的输入
endpoint="/v1/predict", # 可选:相对路径(默认 /predict)
config={"auth_token": "xxx"}, # 可选:鉴权 Token
)
调用的云 API:ModelServiceInterfaceCallTest
endpoint参数会经过 SSRF 安全校验,防止注入恶意 URL。
list_instance_types — 查询可用规格
# 全部规格
all_specs = client.list_instance_types()
# 仅 GPU 规格
gpu_specs = client.list_instance_types(spec_type="GPU")
# 返回结构
# [{"spec_name": "TI.SA5.2XLARGE32.POST", "spec_alias": "...",
# "spec_type": "CPU", "available": True, "available_region": [...],
# "gpu_type": ""}]
调用的 SDK:直连 TIone SDK DescribeInferenceSpecs
restart_pod — 重启 Pod
result = client.restart_pod(
service_id="svc-xxxx",
pod_name="ms-xxxx-0",
)
print(result["request_id"])
调用的云 API:RebuildModelServicePod
get_pod_logs — 查询 Pod 日志
result = client.get_pod_logs(
service_id="svc-xxxx",
pod_name="ms-xxxx-*", # 支持通配符
limit=100, # 最大条数
start_time="2026-03-10T00:00:00+08:00",
end_time="2026-03-10T23:59:59+08:00",
context=None, # 翻页 token
)
for log in result["logs"]:
print(f"[{log['timestamp']}] {log['pod_name']}: {log['message']}")
# 翻页
next_page = client.get_pod_logs(service_id="svc-xxxx", pod_name="ms-xxxx-*", context=result["context"])
调用的云 API:ListMLServiceLogs
Model URI 格式
本插件使用 三段式 Model URI:
models:/CatalogName.SchemaName.ModelName/Version
- CatalogName:目录名(如
default) - SchemaName:Schema 名(如
default) - ModelName:模型名称
- Version:模型版本号
示例:models:/default.default.MyTextClassifier/3
当提供 api_client 时,插件会调用 ListModelVersions 自动解析模型的 Id 和 ModelPath。
也支持 runs:/ 格式的 URI,此时直接将 URI 作为 ModelPath 传递。
安全机制
Endpoint 域名白名单
config.py 中对 API endpoint 实施域名白名单校验:
- 仅允许
*.tencentcloudapi.com和*.tencentcloud.com后缀 - 拒绝包含 scheme(
https://)、路径(/api)、@等非法格式 - 防止通过篡改
TENCENTCLOUD_ENDPOINT环境变量将请求导向恶意服务器
请求路径 SSRF 防护
predict 方法的 endpoint 参数经过严格校验(_validate_relative_url):
| 攻击向量 | 示例 | 防御方式 |
|---|---|---|
| 绝对 URL 注入 | http://evil.com |
检测 :// |
| Protocol-relative | //evil.com |
检测 // 和 \ 开头 |
| Percent-encoded 绕过 | %2f%2fevil.com |
先 unquote 解码再校验 |
@ host-override |
/legit@evil.com |
拒绝含 @ 的路径 |
| 路径遍历 | /../internal-api |
逐段检测 .. 并拒绝 |
| CRLF / null-byte 注入 | /predict\r\nHost: evil |
正则匹配控制字符 |
项目结构
mlflow-tcdeploy-plugin/
├── mlflow_tcdeploy_plugin/
│ ├── __init__.py # 版本号
│ ├── client.py # TcDeploymentClient (MLflow Deployments 接口实现)
│ ├── api_client.py # ModelServiceApiClient (WeData 云 API 封装)
│ ├── config.py # TcDeployConfig (环境变量配置 + endpoint 安全校验)
│ └── models.py # 请求体构建、Model URI 解析、大小写转换
├── tests/
│ ├── test_client.py # Client 层单元测试 (含 SSRF 防护测试)
│ ├── test_api_client.py # API Client 层单元测试
│ ├── test_config.py # 配置层单元测试 (含 endpoint 白名单测试)
│ └── test_models.py # 模型层单元测试 (Create/Update 请求构建)
├── setup.py # 打包配置
├── build.sh # 构建 & 发布脚本
├── LICENSE.txt # MIT License
└── README.md # 本文件
开发
# 克隆并进入项目
cd mlflow-tcdeploy-plugin
# 创建虚拟环境
python3 -m venv .venv
source .venv/bin/activate
# 安装开发依赖
pip install -e ".[dev]"
# 运行单元测试
pytest tests/ -v
# 构建 & 发布
bash build.sh
License
MIT License — Tencent WeData Team
Release files for wedata-mlflow3-plugin 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| wedata_mlflow3_plugin-1.0.0.tar.gz | 40.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| wedata_mlflow3_plugin-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 79.7 kB
Release files / wedata_mlflow3_plugin-1.0.0.tar.gz
| Download URL | wedata_mlflow3_plugin-1.0.0.tar.gz |
|---|---|
| Size | 40.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a31e79f21b203383b5093ebcf957d93db1582856bd969ea2253d29848cca0659
|
|
BLAKE2b-256 checksum How to use checksums |
df2486e80d13ef1213b5fddce42a9ca0391e2fc20ac03631d9faa56431ee84ba
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / wedata_mlflow3_plugin-1.0.0-py3-none-any.whl
| Download URL | wedata_mlflow3_plugin-1.0.0-py3-none-any.whl |
|---|---|
| Size | 39.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
559519c0fc029f81c699916d92d62e8c1f38d918d40d757f1d5ce10999ba969f
|
|
BLAKE2b-256 checksum How to use checksums |
79128ca298fd83a306d8bec5698fabba4d33337e824078545b1f997446b02687
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|