Tencent Cloud deployment plugin for MLflow
Project description
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 重启、调试 Shell
- ✅ 三段式 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
debug_pod_shell — 调试 Pod
shell_info = client.debug_pod_shell(
service_id="svc-xxxx",
pod_name="ms-xxxx-0",
)
# 返回 WebShell URL 等信息
调用的云 API:CreateModelServicePodUrl
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mlflow_tcdeploy_plugin-1.0.2.tar.gz.
File metadata
- Download URL: mlflow_tcdeploy_plugin-1.0.2.tar.gz
- Upload date:
- Size: 28.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e554c58df8ed4f21d13604d4b1bc6b0440cbd6b781091d407e8c5804b7a3f60
|
|
| MD5 |
6190145b3d5f20e536d51e784248564b
|
|
| BLAKE2b-256 |
6509e5fbfaaaf1eed14a75d07da78bb65ec885d7cf8d518cf362884cacf4cb89
|
File details
Details for the file mlflow_tcdeploy_plugin-1.0.2-py3-none-any.whl.
File metadata
- Download URL: mlflow_tcdeploy_plugin-1.0.2-py3-none-any.whl
- Upload date:
- Size: 27.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
42074a0e53f6b7e8ec793047780b2214d9815d067de82f121dbc668001ece55f
|
|
| MD5 |
1572fac65ac3f51017b4d2da646115e2
|
|
| BLAKE2b-256 |
c31badf0a6eb9e409c1308ed66bdaefc73f4751b3b7855f83a5188159e3ee1df
|