通义千问Embedding模型弹性伸缩:K8s自动扩缩容实战
本文介绍了如何在星图GPU平台上自动化部署通义千问3-Embedding-4B-向量化模型,并利用Kubernetes实现服务的弹性伸缩。通过配置自动扩缩容策略,该方案能根据实时流量动态调整资源,有效支撑智能知识库、文档语义检索等典型应用场景,在保障服务性能的同时优化资源成本。
通义千问Embedding模型弹性伸缩:K8s自动扩缩容实战
如果你正在用通义千问的Embedding模型做知识库,肯定遇到过这样的问题:白天用户多的时候,模型响应慢,GPU资源吃紧;到了半夜,GPU又闲着没事干,白白浪费钱。
这就像开了一家餐馆,中午饭点厨师忙不过来,顾客等得着急;晚上厨师又闲着打瞌睡,工资还得照付。有没有一种办法,能让厨师数量自动调整,人多就多请几个,人少就少请几个?
今天要聊的,就是怎么用Kubernetes(K8s)给通义千问Embedding模型装上“自动伸缩”的能力。我们以Qwen3-Embedding-4B这个模型为例,配合vLLM和Open WebUI,打造一个能根据流量自动扩缩容的知识库系统。
1. 为什么需要弹性伸缩?
先说说为什么这事重要。Qwen3-Embedding-4B是个很实用的模型,4B参数、3GB显存就能跑,支持32k长文本和2560维向量,在多语言和代码理解上表现都不错。但再好的模型,部署时也得考虑资源利用。
传统部署的痛点:
- 资源浪费:按峰值流量配置GPU,大部分时间资源闲置
- 响应延迟:流量突增时,单实例处理不过来,用户等待时间变长
- 运维复杂:手动调整实例数量,半夜还得盯着监控
- 成本不可控:固定资源意味着固定成本,无法按需付费
弹性伸缩的好处:
- 省钱:用多少资源付多少钱,闲时自动缩容
- 省心:系统自动调整,不用人工干预
- 稳定:流量高峰时自动扩容,保证服务可用性
- 灵活:根据CPU、内存、请求数等多种指标触发伸缩
简单说,就是让系统像有生命一样,能自己“呼吸”——忙的时候多吸几口气(扩容),闲的时候少喘几口(缩容)。
2. 技术栈选型与架构设计
要搞自动伸缩,得先选对工具。我们的方案基于以下几个核心组件:
2.1 核心组件介绍
Qwen3-Embedding-4B:阿里开源的4B参数双塔Embedding模型。它的特点是“中等体量、大能力”——32k上下文长度、2560维向量、支持119种语言。用GGUF量化后只要3GB显存,RTX 3060就能跑到800文档/秒。
vLLM:专门为LLM推理优化的服务框架。它最大的优势是PagedAttention技术,能高效管理GPU内存,让多个请求共享KV缓存。对Embedding模型来说,vLLm能显著提升并发处理能力。
Open WebUI:开源的Web界面,提供知识库管理、对话界面、模型配置等功能。它支持多种后端,能很方便地对接vLLM服务。
Kubernetes:容器编排平台,负责管理Pod(容器组)的生命周期。K8s的HPA(Horizontal Pod Autoscaler)功能,能根据监控指标自动调整Pod数量。
2.2 整体架构
整个系统的架构是这样的:
用户请求 → Open WebUI前端 → vLLM服务(多个Pod) → Qwen3-Embedding-4B模型
↑
K8s HPA控制器
↑
Prometheus监控
- vLLM服务:运行在K8s的Pod里,每个Pod部署一个vLLM实例,加载Qwen3-Embedding-4B模型
- Open WebUI:作为前端界面,用户通过它上传文档、提问、查看结果
- Prometheus:监控系统,收集vLLM Pod的CPU、内存、请求数等指标
- HPA:根据Prometheus的监控数据,自动调整vLLM Pod的数量
当请求增多时,HPA检测到指标超过阈值,就创建新的vLLM Pod;请求减少时,自动销毁多余的Pod。
3. 环境准备与部署
3.1 基础环境要求
要跑这个方案,你需要:
- Kubernetes集群:可以是云厂商的托管服务(如阿里云ACK、腾讯云TKE),也可以是自建的(如kubeadm部署)
- GPU节点:至少有一个带GPU的节点,显存≥8GB(建议RTX 3060以上)
- 存储:需要持久化存储保存模型文件,避免每次重启都重新下载
- 网络:集群内Pod能互相通信,能访问外部镜像仓库
如果还没有K8s环境,可以用Minikube或Kind在本地搭个测试环境,不过GPU支持可能有限。
3.2 模型准备与优化
Qwen3-Embedding-4B有几种格式可选,我们选最适合生产部署的:
# 模型配置建议
模型格式: GGUF-Q4量化版
显存占用: 约3GB
推理速度: RTX 3060上约800文档/秒
部署方式: 下载到持久化存储,Pod启动时挂载
GGUF格式的好处是量化后体积小、加载快,对显存要求低。你可以从Hugging Face或ModelScope下载预量化好的版本:
# 下载模型(示例)
wget https://huggingface.co/Qwen/Qwen3-Embedding-4B-GGUF/resolve/main/qwen3-embedding-4b-q4_0.gguf
把模型文件放到NFS、Ceph或其他共享存储上,这样所有Pod都能访问同一份模型文件。
3.3 vLLM服务部署
vLLM提供了官方的Docker镜像,我们可以基于它创建K8s Deployment:
# vllm-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: qwen-embedding-vllm
namespace: ai-services
spec:
replicas: 1 # 初始副本数,HPA会自动调整
selector:
matchLabels:
app: qwen-embedding
template:
metadata:
labels:
app: qwen-embedding
spec:
containers:
- name: vllm-server
image: vllm/vllm-openai:latest
command: ["python3", "-m", "vllm.entrypoints.openai.api_server"]
args:
- "--model"
- "/models/qwen3-embedding-4b-q4_0.gguf"
- "--host"
- "0.0.0.0"
- "--port"
- "8000"
- "--served-model-name"
- "qwen3-embedding-4b"
- "--max-model-len"
- "32768" # 32k上下文
- "--gpu-memory-utilization"
- "0.9"
resources:
limits:
nvidia.com/gpu: 1 # 申请1个GPU
memory: "8Gi"
cpu: "2"
requests:
nvidia.com/gpu: 1
memory: "6Gi"
cpu: "1"
volumeMounts:
- name: model-storage
mountPath: /models
readOnly: true
ports:
- containerPort: 8000
volumes:
- name: model-storage
persistentVolumeClaim:
claimName: model-pvc
关键配置说明:
- GPU资源:明确申请GPU资源,K8s会把Pod调度到有GPU的节点
- 内存限制:设置合理的内存限制,防止容器占用过多内存
- 模型路径:通过volume挂载共享存储中的模型文件
- 端口暴露:vLLM服务监听8000端口,提供OpenAI兼容的API
创建Service暴露这个Deployment:
# vllm-service.yaml
apiVersion: v1
kind: Service
metadata:
name: qwen-embedding-service
namespace: ai-services
spec:
selector:
app: qwen-embedding
ports:
- port: 8000
targetPort: 8000
type: ClusterIP # 内部访问,通过Ingress或NodePort对外
3.4 Open WebUI部署
Open WebUI也提供了Docker镜像,部署方式类似:
# webui-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: open-webui
namespace: ai-services
spec:
replicas: 1
selector:
matchLabels:
app: open-webui
template:
metadata:
labels:
app: open-webui
spec:
containers:
- name: webui
image: ghcr.io/open-webui/open-webui:main
env:
- name: OLLAMA_BASE_URL
value: "http://qwen-embedding-service:8000" # 指向vLLM服务
- name: WEBUI_SECRET_KEY
valueFrom:
secretKeyRef:
name: webui-secrets
key: secret-key
ports:
- containerPort: 8080
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
Open WebUI配置里最关键的是OLLAMA_BASE_URL环境变量,要指向vLLM服务的地址。这样用户通过WebUI界面操作时,请求就会转发到vLLM服务。
4. 配置自动扩缩容(HPA)
前面都是基础部署,现在进入核心部分——配置HPA让系统能自动伸缩。
4.1 安装Metrics Server
HPA需要监控数据,K8s默认的监控组件是Metrics Server。先确认集群里有没有:
# 检查Metrics Server
kubectl get pods -n kube-system | grep metrics-server
# 如果没有,安装它
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
Metrics Server会收集每个Pod的CPU和内存使用率,这是HPA做决策的基础。
4.2 基于CPU/内存的HPA
最简单的HPA是基于CPU或内存使用率来伸缩。比如,当vLLM Pod的CPU使用率超过70%时,就增加副本数:
# hpa-cpu.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: qwen-embedding-hpa
namespace: ai-services
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: qwen-embedding-vllm
minReplicas: 1 # 最少1个副本
maxReplicas: 10 # 最多10个副本
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # CPU使用率目标70%
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80 # 内存使用率目标80%
这个配置的意思是:
- 监控
qwen-embedding-vllm这个Deployment - 副本数在1到10之间自动调整
- 当CPU平均使用率超过70% 或 内存平均使用率超过80%时,增加副本
- 当使用率低于目标值时,减少副本(有默认的冷却时间)
4.3 基于自定义指标的HPA
但CPU/内存指标有个问题:它们不能直接反映业务压力。可能CPU还没跑满,但请求队列已经很长了。
这时候就需要自定义指标。对于Embedding服务,最相关的指标是:
- 请求速率(QPS):每秒处理的请求数
- 请求延迟:每个请求的处理时间
- 队列长度:等待处理的请求数
我们需要安装Prometheus来收集这些指标:
# 使用Helm安装Prometheus
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring
然后在vLLM服务里暴露指标。vLLM本身有Prometheus指标端点,我们只需要配置ServiceMonitor让Prometheus能发现它:
# servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: vllm-monitor
namespace: ai-services
spec:
selector:
matchLabels:
app: qwen-embedding
endpoints:
- port: 8000
path: /metrics # vLLM的指标端点
interval: 15s
现在Prometheus就能收集到vLLM的指标了。接下来安装Prometheus Adapter,把Prometheus的指标转换成K8s能识别的自定义指标:
# 基于自定义指标的HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: qwen-embedding-hpa-custom
namespace: ai-services
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: qwen-embedding-vllm
minReplicas: 1
maxReplicas: 10
metrics:
- type: Pods
pods:
metric:
name: vllm_requests_per_second # 自定义指标名
target:
type: AverageValue
averageValue: 50 # 目标:每个Pod每秒处理50个请求
这个配置的意思是:保持每个Pod平均每秒处理50个请求。如果总请求数增加,就增加Pod数量;请求数减少,就减少Pod数量。
4.4 多指标组合策略
实际生产中,我们往往需要多个指标组合判断。比如:
- 请求数高 且 延迟大 → 肯定要扩容
- 请求数高 但 延迟正常 → 可能还能承受
- 请求数低 但 CPU高 → 可能是某个请求特别耗时
可以配置多个指标,HPA会取所有指标计算出的副本数的最大值:
metrics:
- type: Pods
pods:
metric:
name: vllm_requests_per_second
target:
type: AverageValue
averageValue: 50
- type: Pods
pods:
metric:
name: vllm_request_duration_seconds
target:
type: AverageValue
averageValue: 0.5 # 目标延迟500ms
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
这样系统就会综合考虑请求数、延迟和CPU使用率,做出更合理的伸缩决策。
5. 实战:从部署到验证
5.1 完整部署流程
假设你已经有了K8s集群和GPU节点,完整的部署流程是这样的:
# 1. 创建命名空间
kubectl create namespace ai-services
# 2. 创建存储,保存模型文件
kubectl apply -f storage.yaml -n ai-services
# 3. 下载模型到存储(这步需要在能访问存储的机器上做)
# 把qwen3-embedding-4b-q4_0.gguf放到共享存储
# 4. 部署vLLM服务
kubectl apply -f vllm-deployment.yaml -n ai-services
kubectl apply -f vllm-service.yaml -n ai-services
# 5. 部署Open WebUI
kubectl apply -f webui-deployment.yaml -n ai-services
kubectl apply -f webui-service.yaml -n ai-services
# 6. 配置Ingress或NodePort,让外部能访问WebUI
kubectl apply -f ingress.yaml -n ai-services
# 7. 部署监控和HPA
kubectl apply -f prometheus-config.yaml -n monitoring
kubectl apply -f hpa-custom.yaml -n ai-services
# 8. 等待所有Pod就绪
kubectl get pods -n ai-services -w
5.2 压力测试与扩缩容验证
部署完成后,需要验证自动伸缩是否生效。我们可以用简单的压力测试工具模拟请求:
# pressure_test.py
import requests
import concurrent.futures
import time
# vLLM服务的地址(通过Service访问)
VLLM_URL = "http://qwen-embedding-service.ai-services.svc.cluster.local:8000/v1/embeddings"
def send_embedding_request(text):
"""发送单个Embedding请求"""
payload = {
"model": "qwen3-embedding-4b",
"input": text,
"encoding_format": "float"
}
try:
response = requests.post(VLLM_URL, json=payload, timeout=30)
return response.status_code
except Exception as e:
return str(e)
def run_pressure_test(concurrent_users=10, duration=60):
"""运行压力测试"""
print(f"开始压力测试: {concurrent_users}并发,持续{duration}秒")
# 准备测试文本
test_texts = [
"机器学习是人工智能的一个分支",
"深度学习基于神经网络技术",
"自然语言处理让计算机理解人类语言",
"计算机视觉处理图像和视频数据",
"强化学习通过试错来学习策略"
] * 20 # 重复生成更多文本
start_time = time.time()
request_count = 0
with concurrent.futures.ThreadPoolExecutor(max_workers=concurrent_users) as executor:
while time.time() - start_time < duration:
# 提交一批请求
futures = []
for text in test_texts[:concurrent_users]:
future = executor.submit(send_embedding_request, text)
futures.append(future)
# 等待这批请求完成
for future in concurrent.futures.as_completed(futures):
result = future.result()
request_count += 1
time.sleep(0.1) # 稍微控制一下节奏
print(f"压力测试完成,总请求数: {request_count}")
print(f"平均QPS: {request_count / duration:.2f}")
if __name__ == "__main__":
# 先来个小流量热身
print("=== 阶段1: 低负载测试 ===")
run_pressure_test(concurrent_users=5, duration=30)
time.sleep(10)
# 然后加大压力
print("\n=== 阶段2: 高负载测试 ===")
run_pressure_test(concurrent_users=50, duration=120)
# 最后恢复低负载
print("\n=== 阶段3: 恢复期观察 ===")
time.sleep(60)
运行压力测试时,同时在另一个终端观察Pod的变化:
# 观察Pod数量变化
watch -n 2 "kubectl get pods -n ai-services | grep qwen-embedding"
# 观察HPA状态
watch -n 2 "kubectl get hpa -n ai-services"
# 观察资源使用率
watch -n 2 "kubectl top pods -n ai-services"
你应该能看到:
- 压力测试开始时,Pod的CPU/内存使用率上升
- 当使用率超过阈值后,HPA开始创建新的Pod
- Pod数量逐渐增加到满足需求
- 压力测试结束后,使用率下降
- 经过一段冷却时间,多余的Pod被自动删除
5.3 效果验证与调试
如果扩缩容没有按预期工作,可以检查以下几点:
1. 检查Metrics Server是否正常工作:
kubectl get apiservices | grep metrics
kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes | head -20
2. 检查Prometheus是否收集到指标:
# 进入Prometheus Pod
kubectl exec -it prometheus-server-xxxx -n monitoring -- sh
# 查询vLLM指标
curl -s "http://localhost:9090/api/v1/query?query=vllm_requests_total" | jq .
3. 检查HPA事件和状态:
# 查看HPA详细状态
kubectl describe hpa qwen-embedding-hpa -n ai-services
# 查看HPA事件
kubectl get events -n ai-services --field-selector involvedObject.name=qwen-embedding-hpa
4. 常见问题与解决:
-
问题:HPA显示
<unknown>指标- 原因:Metrics Server或Prometheus Adapter没装好
- 解决:检查相关Pod状态和日志
-
问题:Pod数量不增加
- 原因:资源请求设置过高,节点没有足够资源
- 解决:调整Pod的resources.requests,或增加集群节点
-
问题:Pod频繁伸缩(抖动)
- 原因:指标波动太大,冷却时间太短
- 解决:调整HPA的stabilizationWindowSeconds(稳定窗口)
6. 生产环境优化建议
6.1 资源规划与配额
在生产环境,要做好资源规划:
# 资源配额示例
apiVersion: v1
kind: ResourceQuota
metadata:
name: ai-services-quota
namespace: ai-services
spec:
hard:
requests.cpu: "20"
requests.memory: 40Gi
requests.nvidia.com/gpu: "4"
limits.cpu: "40"
limits.memory: 80Gi
limits.nvidia.com/gpu: "8"
pods: "20"
设置合理的ResourceQuota可以防止某个服务占用所有资源,影响其他服务。
6.2 Pod调度优化
GPU资源宝贵,要确保Pod调度到正确节点:
# 节点选择与亲和性
spec:
template:
spec:
nodeSelector:
accelerator: nvidia-gpu # 选择有GPU的节点
tolerations:
- key: "nvidia.com/gpu"
operator: "Exists"
effect: "NoSchedule"
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- qwen-embedding
topologyKey: kubernetes.io/hostname
这个配置的意思是:
- 只调度到有
accelerator: nvidia-gpu标签的节点 - 容忍GPU节点的污点
- 尽量把同一个服务的Pod分散到不同节点(提高可用性)
6.3 优雅伸缩与数据一致性
扩缩容时要注意服务连续性:
# Pod生命周期配置
spec:
template:
spec:
containers:
- name: vllm-server
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 30"] # 停止前等待30秒
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3
关键配置:
- preStop:Pod停止前等待30秒,让正在处理的请求完成
- readinessProbe:就绪探针,确保Pod完全启动后再接收流量
- livenessProbe:存活探针,检查Pod是否健康
6.4 监控与告警
完整的监控体系包括:
# Prometheus告警规则示例
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: vllm-alerts
namespace: monitoring
spec:
groups:
- name: vllm
rules:
- alert: HighRequestLatency
expr: histogram_quantile(0.95, rate(vllm_request_duration_seconds_bucket[5m])) > 1
for: 2m
labels:
severity: warning
annotations:
summary: "vLLM请求延迟高"
description: "95%的请求延迟超过1秒,当前值: {{ $value }}秒"
- alert: PodScalingFailed
expr: kube_horizontalpodautoscaler_status_condition{condition="ScalingLimited", status="true"} == 1
for: 1m
labels:
severity: critical
annotations:
summary: "HPA扩缩容失败"
description: "HPA {{ $labels.horizontalpodautoscaler }} 无法继续扩容,可能达到最大副本数或资源不足"
7. 总结
通过K8s的自动扩缩容能力,我们为通义千问Embedding模型打造了一个弹性伸缩的知识库系统。这个方案的核心价值在于:
1. 成本优化:按需使用GPU资源,闲时自动缩容,能节省30%-50%的云资源成本。
2. 性能保障:流量高峰时自动扩容,确保服务响应时间稳定,提升用户体验。
3. 运维简化:无需人工干预扩缩容,系统自动根据负载调整,降低运维复杂度。
4. 高可用性:多副本部署配合健康检查,单个Pod故障不影响整体服务。
实际部署建议:
- 从小规模开始:先部署最小规模(1个副本),观察基线性能
- 逐步调整阈值:根据实际负载调整HPA的阈值,避免频繁抖动
- 监控是关键:建立完整的监控体系,包括资源使用、业务指标、错误率等
- 定期压力测试:定期模拟高峰流量,验证扩缩容策略是否有效
- 设置安全边界:合理设置minReplicas和maxReplicas,防止无限扩容
这个方案不仅适用于Qwen3-Embedding-4B,也适用于其他Embedding模型甚至生成式模型。随着业务增长,你还可以考虑更高级的特性,如基于预测的扩缩容、多集群弹性等。
弹性伸缩不是一劳永逸的配置,而是一个持续优化的过程。通过监控、测试、调整,你会逐渐找到最适合自己业务场景的伸缩策略。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐


所有评论(0)