本文介绍如何在 TKE 上部署 AI 大模型,以 DeepSeek-R1
为例,使用 Ollama
、vLLM
或 SGLang
运行大模型并暴露 API,然后使用 OpenWebUI
提供交互界面。
Ollama
提供是 Ollama API,部署架构:
vLLM
和 SGLang
都提供了兼容 OpenAI 的 API,部署架构:
AI 大模型通常占用体积较大,直接打包到容器镜像不太现实,如果启动时通过 initContainers
自动下载又会导致启动时间过长,因此建议使用共享存储来挂载 AI 大模型(先下发一个 Job 将模型下载到共享存储,然后再将共享存储挂载到运行大模型的 Pod 中),这样后面 Pod 启动时就无需下载模型了(虽然最终加载模型时同样也会经过CFS的网络下载,但只要 CFS 使用规格比较高,如 Turbo 类型,速度就会很快)。
在腾讯云上可使用 CFS 来作为共享存储,CFS 的性能和可用性都非常不错,适合 AI 大模型的存储。本文将使用 CFS 来存储 AI 大模型。
不同的机型使用的 GPU 型号不一样,机型与 GPU 型号的对照表参考 GPU 计算型实例 ,Ollama 相比 vLLM,支持的 GPU 型号更广泛,兼容性更好,建议根据事先调研自己所使用的工具和大模型,选择合适的 GPU 型号,再根据前面的对照表确定要使用的 GPU 机型,另外也注意下选择的机型在哪些地域在售,以及是否售罄,可通过 购买云服务器 页面进行查询(实例族选择GPU机型)。
本文中的示例使用的镜像都是官方提供的镜像,tag 为 latest,建议根据自身情况改成指定版本的 tag,可点击下面的连接查看镜像的 tag 列表:
官方镜像均在 DockerHub,且体积较大,在 TKE 环境 默认有免费的 DockerHub 镜像加速,国内地域也可以拉取 DockerHub 的镜像,但速度一般,镜像较大时等待的时间较长,建议将镜像同步至 容器镜像服务 TCR,再替换 YAML 中镜像地址,这样可极大加快镜像拉取速度。
登录 容器服务控制台,创建一个集群,集群类型选择TKE 标准集群。详情请参见 创建集群。
支持选择 CFS(腾讯云文件存储) 或 CFS Turbo(腾讯云高性能并行文件系统),本文以 CFS(腾讯云文件存储)为例。 CFS-Turbo 的性能更强,读写速度更快,但成本也更高。如果希望大模型运行和下载速度更快,可以考虑使用 CFS-Turbo。
该步骤选择项较多,因此本文示例通过容器服务控制台来创建 PVC。若您希望通过 YAML 来创建,可以先用控制台创建一个测试 PVC,然后复制生成的 YAML 文件
如果是新建 CFS-Turbo
StorageClass
,则需要在文件存储控制台先新建好 CFS-Turbo 文件系统,然后创建StorageClass
时引用对应的 CFS-Turbo 实例。
创建一个 CFS 类型的 PVC,用于存储 AI 大模型:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ai-model
labels:
app: ai-model
spec:
storageClassName: cfs-ai
accessModes:
- ReadWriteMany
resources:
requests:
storage: 100Gi
storageClassName
。storage
大小无所谓,可随意指定,按实际占用空间付费的。再创建一个 PVC 给 OpenWebUI 用,可使用同一个 storageClassName
:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: webui
labels:
app: webui
spec:
accessModes:
- ReadWriteMany
storageClassName: cfs-ai
resources:
requests:
storage: 100Gi
GPU 插件无需显式安装,如果使用普通节点或原生节点,配置了 GPU 机型,会自动安装 GPU 插件;如果使用超级节点,则无需安装 GPU 插件。
下发一个 Job,将需要用的 AI 大模型下载到 CFS 共享存储中,以下分别是 vLLM、SGLang 和 Ollama 的 Job 示例:
LLM_MODEL
以替换大语言模型。USE_MODELSCOPE
环境环境变量控制是否从 ModelScope 下载)。apiVersion: batch/v1
kind: Job
metadata:
name: vllm-download-model
labels:
app: vllm-download-model
spec:
template:
metadata:
name: vllm-download-model
labels:
app: vllm-download-model
annotations:
eks.tke.cloud.tencent.com/root-cbs-size: '100' # 如果用超级节点,默认系统盘只有 20Gi,vllm 镜像解压后会撑爆磁盘,用这个注解自定义一下系统盘容量(超过20Gi的部分会收费)。
spec:
containers:
- name: vllm
image: vllm/vllm-openai:latest
env:
- name: LLM_MODEL
value: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
- name: USE_MODELSCOPE
value: "1"
command:
- bash
- -c
- |
set -ex
if [[ "$USE_MODELSCOPE" == "1" ]]; then
exec modelscope download --local_dir=/data/$LLM_MODEL --model="$LLM_MODEL"
else
exec huggingface-cli download --local-dir=/data/$LLM_MODEL $LLM_MODEL
fi
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: ai-model
restartPolicy: OnFailure
apiVersion: batch/v1
kind: Job
metadata:
name: sglang-download-model
labels:
app: sglang-download-model
spec:
template:
metadata:
name: sglang-download-model
labels:
app: sglang-download-model
spec:
containers:
- name: sglang
image: lmsysorg/sglang:latest
env:
- name: LLM_MODEL
value: deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
- name: USE_MODELSCOPE
value: "1"
command:
- bash
- -c
- |
set -ex
if [[ "$USE_MODELSCOPE" == "1" ]]; then
exec modelscope download --local_dir=/data/$LLM_MODEL --model="$LLM_MODEL"
else
exec huggingface-cli download --local-dir=/data/$LLM_MODEL $LLM_MODEL
fi
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: ai-model
restartPolicy: OnFailure
apiVersion: batch/v1
kind: Job
metadata:
name: ollama-download-model
labels:
app: ollama-download-model
spec:
template:
metadata:
name: ollama-download-model
labels:
app: ollama-download-model
annotations:
eks.tke.cloud.tencent.com/root-cbs-size: '100' # 如果用超级节点,默认系统盘只有 20Gi,vllm 镜像解压后会撑爆磁盘,用这个注解自定义一下系统盘容量(超过20Gi的部分会收费)。
spec:
containers:
- name: ollama
image: ollama/ollama:latest
env:
- name: LLM_MODEL
value: deepseek-r1:7b
command:
- bash
- -c
- |
set -ex
ollama serve &
sleep 5 # sleep 5 seconds to wait for ollama to start
exec ollama pull $LLM_MODEL
volumeMounts:
- name: data
mountPath: /root/.ollama # ollama 的模型数据存储在 `/root/.ollama` 目录下,挂载 CFS 类型的 PVC 到该路径。
volumes:
- name: data
persistentVolumeClaim:
claimName: ai-model
restartPolicy: OnFailure
nvidia.com/gpu
资源,以便让 Pod 调度到 GPU 机型并分配 GPU 卡使用。eks.tke.cloud.tencent.com/gpu-type
指定 GPU 类型,可选 V100
、T4
、A10*PNV4
、A10*GNV4
,具体可参考 这里。eks.tke.cloud.tencent.com/root-cbs-size: '100'
这个注解提升系统盘容量(默认只有 20GB,镜像解压后可能撑爆磁盘)。通过 Deployment 部署 vLLM:
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm
labels:
app: vllm
spec:
selector:
matchLabels:
app: vllm
replicas: 1
template:
metadata:
labels:
app: vllm
spec:
containers:
- name: vllm
image: vllm/vllm-openai:latest
imagePullPolicy: Always
env:
- name: PYTORCH_CUDA_ALLOC_CONF
value: expandable_segments:True
- name: LLM_MODEL
value: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
command:
- bash
- -c
- |
vllm serve /data/$LLM_MODEL \
--served-model-name $LLM_MODEL \
--host 0.0.0.0 \
--port 8000 \
--trust-remote-code \
--enable-chunked-prefill \
--max_num_batched_tokens 1024 \
--max_model_len 1024 \
--enforce-eager \
--tensor-parallel-size 1
securityContext:
runAsNonRoot: false
ports:
- containerPort: 8000
readinessProbe:
failureThreshold: 3
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests:
cpu: 2000m
memory: 2Gi
nvidia.com/gpu: "1"
limits:
nvidia.com/gpu: "1"
volumeMounts:
- name: data
mountPath: /data
- name: shm
mountPath: /dev/shm
volumes:
- name: data
persistentVolumeClaim:
claimName: ai-model
# vLLM needs to access the host's shared memory for tensor parallel inference.
- name: shm
emptyDir:
medium: Memory
sizeLimit: "2Gi"
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: vllm-api
spec:
selector:
app: vllm
type: ClusterIP
ports:
- name: api
protocol: TCP
port: 8000
targetPort: 8000
--served-model-name
参数指定大模型名称,与前面下载 Job 中指定的名称要一致,注意替换。/data
目录下。通过 Deployment 部署 SGLang:
apiVersion: apps/v1
kind: Deployment
metadata:
name: sglang
labels:
app: sglang
spec:
selector:
matchLabels:
app: sglang
replicas: 1
template:
metadata:
labels:
app: sglang
spec:
containers:
- name: sglang
image: lmsysorg/sglang:latest
env:
- name: LLM_MODEL
value: deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
command:
- bash
- -c
- |
set -x
exec python3 -m sglang.launch_server \
--host 0.0.0.0 \
--port 30000 \
--model-path /data/$LLM_MODEL
resources:
limits:
nvidia.com/gpu: "1"
ports:
- containerPort: 30000
volumeMounts:
- name: data
mountPath: /data
- name: shm
mountPath: /dev/shm
volumes:
- name: data
persistentVolumeClaim:
claimName: ai-model
- name: shm
emptyDir:
medium: Memory
sizeLimit: 40Gi
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: sglang
spec:
selector:
app: sglang
type: ClusterIP
ports:
- name: api
protocol: TCP
port: 30000
targetPort: 30000
LLM_MODEL
环境变量指定大模型名称,与前面下载 Job 中指定的名称要一致,注意替换。/data
目录下。apiVersion: apps/v1
kind: Deployment
metadata:
name: ollama
labels:
app: ollama
spec:
selector:
matchLabels:
app: ollama
replicas: 1
template:
metadata:
labels:
app: ollama
spec:
containers:
- name: ollama
image: ollama/ollama:latest
imagePullPolicy: IfNotPresent
command: ["ollama", "serve"]
env:
- name: OLLAMA_HOST
value: ":11434"
resources:
requests:
cpu: 2000m
memory: 2Gi
nvidia.com/gpu: "1"
limits:
cpu: 4000m
memory: 4Gi
nvidia.com/gpu: "1"
ports:
- containerPort: 11434
name: ollama
volumeMounts:
- name: data
mountPath: /root/.ollama
volumes:
- name: data
persistentVolumeClaim:
claimName: ai-model
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: ollama
spec:
selector:
app: ollama
type: ClusterIP
ports:
- name: server
protocol: TCP
port: 11434
targetPort: 11434
/root/.ollama
目录下,挂载已经下载好 AI 大模型的 CFS 类型 PVC 到该路径。OLLAMA_HOST
环境变量,强制对外暴露 11434 端口。如果需要对 GPU 资源进行弹性伸缩,可以按照下面的方法进行配置。
GPU 的 Pod 会有一些监控指标,参考 GPU 监控指标,可以根据这些监控指标配置 HPA 实现 GPU Pod 的弹性伸缩,比如按照 GPU 利用率:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: vllm
spec:
minReplicas: 1
maxReplicas: 2
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: vllm
metrics: # 更多 GPU 指标参考 https://cloud.tencent.com/document/product/457/38929#gpu
- pods:
metric:
name: k8s_pod_rate_gpu_used_request # GPU利用率 (占 Request)
target:
averageValue: "80"
type: AverageValue
type: Pods
behavior:
scaleDown:
policies:
- periodSeconds: 15
type: Percent
value: 100
selectPolicy: Max
stabilizationWindowSeconds: 300
scaleUp:
policies:
- periodSeconds: 15
type: Percent
value: 100
- periodSeconds: 15
type: Pods
value: 4
selectPolicy: Max
stabilizationWindowSeconds: 0
需要注意的是,GPU 资源通常比较紧张,缩容后不一定还能再买回来,如不希望缩容,可以给 HPA 配置下禁止缩容:
behavior:
scaleDown:
selectPolicy: Disabled
如果使用原生节点或普通节点,还需对节点池启动弹性伸缩,否则 GPU Pod 扩容后没相应的 GPU 节点会导致 Pod 一直处于 Pending 状态。
节点池启用弹性伸缩的方法是编辑节点池,然后勾选弹性伸缩,配置一下节点数量范围,最后点击确认:
使用 Deployment 部署 OpenWebUI,并定义 Service 方便后续对外暴露访问。后端 API 可以由 vLLM、SGLang 或 Ollama 提供,以下提供这三种情况的 OpenWebUI 部署示例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: webui
spec:
replicas: 1
selector:
matchLabels:
app: webui
template:
metadata:
labels:
app: webui
spec:
containers:
- name: webui
image: imroc/open-webui:main # docker hub 中的 mirror 镜像,长期自动同步,可放心使用
env:
- name: OPENAI_API_BASE_URL
value: http://vllm-api:8000/v1 # vllm 的地址
- name: ENABLE_OLLAMA_API # 禁用 Ollama API,只保留 OpenAI API
value: "False"
tty: true
ports:
- containerPort: 8080
resources:
requests:
cpu: "500m"
memory: "500Mi"
limits:
cpu: "1000m"
memory: "1Gi"
volumeMounts:
- name: webui-volume
mountPath: /app/backend/data
volumes:
- name: webui-volume
persistentVolumeClaim:
claimName: webui
---
apiVersion: v1
kind: Service
metadata:
name: webui
labels:
app: webui
spec:
type: ClusterIP
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector:
app: webui
apiVersion: apps/v1
kind: Deployment
metadata:
name: webui
spec:
replicas: 1
selector:
matchLabels:
app: webui
template:
metadata:
labels:
app: webui
spec:
containers:
- name: webui
image: imroc/open-webui:main # docker hub 中的 mirror 镜像,长期自动同步,可放心使用
env:
- name: OPENAI_API_BASE_URL
value: http://sglang:30000/v1 # sglang 的地址
- name: ENABLE_OLLAMA_API # 禁用 Ollama API,只保留 OpenAI API
value: "False"
tty: true
ports:
- containerPort: 8080
resources:
requests:
cpu: "500m"
memory: "500Mi"
limits:
cpu: "1000m"
memory: "1Gi"
volumeMounts:
- name: webui-volume
mountPath: /app/backend/data
volumes:
- name: webui-volume
persistentVolumeClaim:
claimName: webui
---
apiVersion: v1
kind: Service
metadata:
name: webui
labels:
app: webui
spec:
type: ClusterIP
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector:
app: webui
apiVersion: apps/v1
kind: Deployment
metadata:
name: webui
spec:
replicas: 1
selector:
matchLabels:
app: webui
template:
metadata:
labels:
app: webui
spec:
containers:
- name: webui
image: imroc/open-webui:main # docker hub 中的 mirror 镜像,长期自动同步,可放心使用
env:
- name: OLLAMA_BASE_URL
value: http://ollama:11434 # ollama 的地址
- name: ENABLE_OPENAI_API # 禁用 OpenAI API,只保留 Ollama API
value: "False"
tty: true
ports:
- containerPort: 8080
resources:
requests:
cpu: "500m"
memory: "500Mi"
limits:
cpu: "1000m"
memory: "1Gi"
volumeMounts:
- name: webui-volume
mountPath: /app/backend/data
volumes:
- name: webui-volume
persistentVolumeClaim:
claimName: webui
---
apiVersion: v1
kind: Service
metadata:
name: webui
labels:
app: webui
spec:
type: ClusterIP
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector:
app: webui
OpenWebUI 的数据存储在
/app/backend/data
目录(如账号密码、聊天历史等数据),我们挂载 PVC 到这个路径。
如果只是本地测试,可以使用 kubectl port-forward
暴露服务:
kubectl port-forward service/webui 8080:8080
在浏览器中访问 http://127.0.0.1:8080
即可。
你还可以通过 Ingress 或 Gateway API 来暴露,下面给出示例。
注意: 使用 Gateway API 需要集群中装有 Gateway API 的实现,如 TKE 应用市场中的 EnvoyGateway,具体 Gateway API 用法参考 官方文档
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: ai
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
namespace: envoy-gateway-system
name: imroc
hostnames:
- "ai.imroc.cc"
rules:
- backendRefs:
- group: ""
kind: Service
name: webui
port: 8080
parentRefs
引用定义好的 Gateway
(通常一个 Gateway 对应一个 CLB)。hostnames
替换为你自己的域名,确保域名能正常解析到 Gateway 对应的 CLB 地址。backendRefs
指定 OpenWebUI 的 Service。apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webui
spec:
rules:
- host: "ai.imorc.cc"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webui
port:
number: 8080
host
替换为你自己的域名,确保域名能正常解析到 Ingress 对应的 CLB 地址。backend.service
指定 OpenWebUI 的 Service。最后在浏览器访问相应的地址即可进入 OpenWebUI 页面。
首次进入 OpenWebUI 会提示创建管理员账号密码,创建完毕后即可登录,然后默认会使用前面下载好的大模型进行对话。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。