注意:
- 本教程需要访问 Oracle Cloud。要注册免费账户,请参阅开始使用 Oracle Cloud Infrastructure 免费套餐。
- 它使用 Oracle Cloud Infrastructure 身份证明、租户和区间的示例值。完成实验室时,请将这些值替换为特定于云环境的值。
在 Oracle Kubernetes 引擎 (OKE) 上部署 OpenAI vLLM 生产堆栈
简介
为生产工作负载采用大型语言模型 (LLM) 的企业面临着关键的基础设施决策:依赖第三方推理 API 或部署自托管推理堆栈。自托管部署具有显著优势:全面的数据隐私和合规性控制;消除网络往返延迟,实现低于 100 毫秒的推断延迟;大规模的可预测成本;无需供应商锁定即可自由地微调和服务任何开源模型。
但是,从头开始构建生产级 LLM 推断堆栈非常复杂。它需要具备 GPU 感知的容器编排、跨多个模型副本的智能请求路由、用于大型模型权重的持久存储以及持续监视—所有这些都经过可靠集成和运行。
Oracle Cloud Infrastructure 提供了多种 AI 推断路径。OCI Generative AI 服务通过与租户隔离的专用 AI 集群提供完全托管的体验,非常适合希望快速开始使用受支持模型的团队。本教程采用替代方法:在 OKE 上部署自己的推理堆栈。此路径专为需要对 GPU 驱动程序、CUDA 版本、模型配置和服务参数进行精确控制的团队,或者需要训练和微调定制模型并希望直接为其提供服务的团队而设计。OCI 为裸金属 GPU 实例提供 NVIDIA A10、A100 和 H100 GPU,并通过超低延迟 RDMA 集群网络连接,为您提供与本地部署相同的硬件控制水平,同时享受云弹性。
vLLM 生产堆栈通过提供基于 vLLM(Meta、Mistral AI 和 IBM 等组织在生产中使用的高吞吐量推断引擎)构建的开源 Kubernetes 本机平台来解决自托管推断的复杂性。与标准服务框架相比,它通过高效的 GPU 内存管理和 KV 高速缓存优化提供高达 24x 的吞吐量。结合 OKE 和 OCI GPU 配置,您可以获得一个生产就绪的推断平台,具有企业级网络、存储和安全性。本教程中使用的 OCI 部署脚本在 official vLLM production-stack repository 中提供和维护。
本教程将引导您完成在 OKE 上部署 vLLM 生产堆栈,从基础结构预配到运行第一个推断请求。
注:本教程使用 OCI CLI 分步预配资源,以帮助您了解 GPU 推断部署所需的 OCI 云资源的完整流。对于生产环境,建议使用 Terraform 或 OCI Resource Manager (Shepherd) 将此基础结构编纂为可重复的版本控制部署。

本教程使用以下 OCI 服务:
| 服务 | 用途 |
|---|---|
| Oracle Kubernetes 引擎 (OKE) | 用于容器编排和 GPU 工作负载调度的托管 Kubernetes 集群 |
| OCI 计算(GPU 配置) | 用于模型推断的 NVIDIA A10 (24GB) 和 A100 (80GB) GPU 实例 |
| OCI 块存储卷 | 具有可配置性能层的模型权重的持久存储 |
| OCI Virtual Cloud Network, VCN | 网络基础设施,包括子网、网关和安全列表 |
| OCI 负载均衡器 | 对推断端点的外部访问 |
| OCI 堡垒 | 用于专用集群访问的托管 SSH 隧道 |
| OCI 对象存储 | 使用预先验证的请求 (PAR) URL 的替代模型源 |
目标
在本教程里,您将:
- 使用 OCI CLI 部署具有启用了 GPU 的节点池的 OKE 集群
- 为 Kubernetes 工作负载配置 OCI 网络(VCN、子网和网关)
- 安装和配置用于 GPU 调度的 NVIDIA 设备插件
- 展开 GPU 节点文件系统以使用完全引导卷容量
- 使用与 OpenAI 兼容的推断端点部署 vLLM 生产堆栈
- 对 OpenAI GPT-OSS-20B 模型的 API 请求测试 LLM 推断
- 为裸金属配置上的较大模型配置多 GPU 张量并行度
- 使用 OCI Object Storage 作为替代模型源
- 清理所有 OCI 资源以避免持续收费
Prerequisites
- 具有以下功能的 Oracle Cloud Infrastructure 账户:
- 具有创建和管理 OKE 集群的权限的区间
- 所需配置的 GPU 计算配额(例如
VM.GPU.A10.1或BM.GPU.A100-v2.8)。如果您没有 GPU 配额,请通过支持请求单请求 - 创建 VCN、子网、互联网网关和负载平衡器的权限
- 访问 OCI Block Volumes 以获得持久性存储
- 访问 OCI 对象存储(用于高级模型加载部分)
注:本教程中的示例输出和屏幕截图使用 us-chicago-1 。您可以通过设置
OCI_REGION在任何受支持的区域中部署。GPU 容量因区域和可用性域而异,因此请确认您的目标 GPU 配置在部署之前可用。检查按区域划分的 GPU 配置可用性,如果出现容量错误,请准备好尝试其他可用性域 (GPU_AD_INDEX)。
注:本教程预配付费 GPU 资源(例如
VM.GPU.A10.1)。它不是 OCI Always Free 工作负载。完成后始终运行清理步骤,以避免持续收费。
-
OCI CLI 安装并配置有
oci setup config -
已安装 jq 进行 JSON 解析
-
已安装 kubectl
-
已安装 Helm
-
用于堡垒访问的 SSH 密钥对(例如
~/.ssh/id_rsa和~/.ssh/id_rsa.pub)。如果需要,使用ssh-keygen -t rsa -b 4096生成一个 -
熟悉 Kubernetes 概念(云池、服务、部署、节点池)
注:本教程部署
openai/gpt-oss-20b,这是 OpenAI 中 Apache 2.0 许可的型号。不需要 Hugging Face 标记。如果要部署受限模型(例如 Meta Llama 3.1),则需要具有 API 标记的拥抱面账户。
任务 1:配置环境变量
在部署基础结构之前设置所需的 OCI 配置。
-
在 OCI 控制台中查找区间 OCID。导航到身份和安全 > 区间,然后单击目标区间并复制 OCID。
oci iam compartment list --query 'data[].{name:name,id:id}' --output table
-
导出所需的环境变量。
export OCI_COMPARTMENT_ID="ocid1.compartment.oc1..xxxxx" -
(可选)通过设置以下任一环境变量覆盖默认配置。
变量 Default 说明 OCI_REGIONus-ashburn-1用于部署的 OCI 区域 OCI_PROFILEDEFAULTOCI CLI 配置概要信息 CLUSTER_NAMEproduction-stackOKE 集群的名称 GPU_SHAPEVM.GPU.A10.1节点池的 GPU 计算配置 GPU_NODE_COUNT1池中的 GPU 节点数 GPU_BOOT_VOLUME_GB200GPU 节点的引导卷的大小 (GB) CPU_BOOT_VOLUME_GB100CPU 节点的引导卷的大小 (GB) GPU_AD_INDEX1用于放置 GPU 的可用性域索引(基于 0) PRIVATE_CLUSTERtrue对于公共 Kubernetes API 端点,设置为 falseKUBERNETES_VERSIONv1.31.10OKE 集群的 Kubernetes 版本 例如,要使用两个 A100 GPU 节点进行部署:
export OCI_COMPARTMENT_ID="ocid1.compartment.oc1..xxxxx" export GPU_SHAPE="BM.GPU.A100-v2.8" export GPU_NODE_COUNT="2" -
查看可用的 GPU 配置,并根据模型大小要求选择一个配置。
形状 GPU GPU 类型 GPU 内存 建议用于 VM.GPU.A10.11 NVIDIA A10 24 GB 7B – 13B 参数模型 VM.GPU.A10.22 NVIDIA A10 48 GB 与小型模型平行的张量 BM.GPU4.88 NVIDIA A100 40 GB 320 GB 70B 模型,经济高效 BM.GPU.A100-v2.88 NVIDIA A100 80 GB 640 GB 70B+ 参数模型 BM.GPU.H100.88 NVIDIA H100 640 GB 最大的型号,RDMA 支持 注:裸金属配置 (
BM.*) 提供没有虚拟化开销的专用硬件,并支持多 GPU 张量并行。对于较小的型号,虚拟机配置 (VM.*) 更具成本效益。注:本教程使用
VM.GPU.A10.1(具有 24 GB GPU 内存的单个 NVIDIA A10)来部署openai/gpt-oss-20b,这是一种专家 (MoE) 模型,其中包含 3.6B 活动参数,通常适用于单个 A10 GPU。高级部分演示了使用BM.GPU.H100.8的多 GPU 配置,适用于大型模型,例如 Llama 3.1 70B。
任务 2:使用自动化脚本进行部署(快速启动)
vLLM 生产堆栈包含一个自动化部署脚本,可预配所有 OCI 资源并使用单个命令部署推断堆栈。使用此方法可以快速部署。对于想要自定义流程的用户,任务 3 到 10 分别涵盖每个步骤。
-
克隆 vLLM 生产堆栈系统信息库。
git clone https://github.com/vllm-project/production-stack.git cd production-stack/deployment_on_cloud/oci -
导出区间 OCID。
export OCI_COMPARTMENT_ID="ocid1.compartment.oc1..xxxxx" -
运行部署脚本。
./entry_point.sh setup
对于公共群集 (
PRIVATE_CLUSTER=false),设置将创建所有基础结构并在单个命令中部署 vLLM 堆栈。将 Helm 值文件作为第二个参数传递:PRIVATE_CLUSTER=false ./entry_point.sh setup ./production_stack_specification.yaml对于专用集群(默认设置),设置将创建基础结构,但无法直接访问 Kubernetes API。打开一个单独的终端并启动隧道,然后部署:
# In a separate terminal, start the SSH tunnel (auto-reconnects on drops): ./entry_point.sh tunnel # Back in the first terminal, deploy vLLM: ./entry_point.sh deploy-vllm ./production_stack_specification.yaml -
验证部署是否正在运行。
kubectl get pods预期输出:
NAME READY STATUS RESTARTS AGE vllm-deployment-router-xxxxxxxxxx-xxxxx 1/1 Running 0 5m vllm-gpt-oss-deployment-vllm-xxxxxxxxxx-xxxxx 1/1 Running 0 5m
注:如果两个云池都显示
Running状态,您的部署就绪。跳转到 Task 10:Test the Inference Endpoint 。
注: GPU 实例受到 OCI 容量限制。如果脚本在“Waiting for GPU node(等待 GPU 节点)”循环中停留超过 15 分钟,则所选可用性域中可能无法使用 GPU 配置。使用
oci ce node-pool get检查节点池状态,并查找“Out of host capacity(主机容量不足)”错误。要解决此问题,请使用./entry_point.sh cleanup进行清理,然后使用其他可用性域(例如GPU_AD_INDEX=0或GPU_AD_INDEX=2)或其他 GPU 配置(例如GPU_SHAPE=VM.GPU.A10.2)重新部署。
注:部署脚本使用会产生大量成本的 GPU 实例(对于单个 A10 GPU,每天约 50 美元)。完成后,请始终运行
./entry_point.sh cleanup以避免持续收费。
任务 3:创建 VCN 和网络
创建 OKE 集群所需的 OCI 网络基础结构。这包括虚拟云网络 (Virtual Cloud Network,VCN)、网关、路由表、安全列表和子网。每个网络资源都是在几秒钟内创建的;完整的命令集在不到 2 分钟内完成。
-
创建具有
10.0.0.0/16CIDR 块的 VCN。VCN_ID=$(oci network vcn create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --display-name "${CLUSTER_NAME}-vcn" \ --cidr-blocks '["10.0.0.0/16"]' \ --dns-label "prodstack" \ --query "data.id" \ --raw-output) -
创建用于公共子网路由的 Internet 网关。
IGW_ID=$(oci network internet-gateway create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --vcn-id "${VCN_ID}" \ --display-name "${CLUSTER_NAME}-igw" \ --is-enabled true \ --query "data.id" \ --raw-output) -
为来自专用子网的出站流量创建 NAT 网关。
NAT_ID=$(oci network nat-gateway create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --vcn-id "${VCN_ID}" \ --display-name "${CLUSTER_NAME}-nat" \ --query "data.id" \ --raw-output) -
创建服务网关以访问 Oracle 服务网络。OKE 云控制器使用 Oracle 服务初始化 worker 节点(设置可用性域标签,删除初始化污点)。如果没有服务网关,GPU 节点可能会保持未初始化状态,块存储卷预配将失败。
SGW_SERVICE_ID=$(oci network service list \ --query "data[?contains(name, 'All') && contains(name, 'Services')].id | [0]" \ --raw-output) SGW_SERVICE_NAME=$(oci network service list \ --query "data[?contains(name, 'All') && contains(name, 'Services')].\"cidr-block\" | [0]" \ --raw-output) SGW_ID=$(oci network service-gateway create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --vcn-id "${VCN_ID}" \ --display-name "${CLUSTER_NAME}-sgw" \ --services "[{\"serviceId\": \"${SGW_SERVICE_ID}\"}]" \ --query "data.id" \ --raw-output) -
为专用子网和公共子网创建路由表。
PRIVATE_RT_ID=$(oci network route-table create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --vcn-id "${VCN_ID}" \ --display-name "${CLUSTER_NAME}-private-rt" \ --route-rules "[ {\"cidrBlock\": \"0.0.0.0/0\", \"networkEntityId\": \"${NAT_ID}\"}, {\"destination\": \"${SGW_SERVICE_NAME}\", \"destinationType\": \"SERVICE_CIDR_BLOCK\", \"networkEntityId\": \"${SGW_ID}\"} ]" \ --query "data.id" \ --raw-output) PUBLIC_RT_ID=$(oci network route-table create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --vcn-id "${VCN_ID}" \ --display-name "${CLUSTER_NAME}-public-rt" \ --route-rules "[{\"cidrBlock\": \"0.0.0.0/0\", \"networkEntityId\": \"${IGW_ID}\"}]" \ --query "data.id" \ --raw-output)注:专用路由表有两个规则:用于常规 Internet 访问的 NAT 网关路由(提取容器映像、下载模型)和用于直接访问 Oracle 服务网络的服务网关路由。服务网关路由至关重要。如果没有它,OKE 云控制器将无法初始化 worker 节点,这会阻止块存储卷预配。公共路由表使用互联网网关进行负载平衡器访问。
-
使用 OKE 所需的入站和出站规则创建安全列表。
SL_ID=$(oci network security-list create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --vcn-id "${VCN_ID}" \ --display-name "${CLUSTER_NAME}-sl" \ --egress-security-rules '[{"destination": "0.0.0.0/0", "protocol": "all", "isStateless": false}]' \ --ingress-security-rules '[ {"source": "0.0.0.0/0", "protocol": "6", "isStateless": false, "tcpOptions": {"destinationPortRange": {"min": 22, "max": 22}}, "description": "SSH access"}, {"source": "10.0.0.0/16", "protocol": "all", "isStateless": false, "description": "VCN internal traffic"}, {"source": "10.244.0.0/16", "protocol": "all", "isStateless": false, "description": "Kubernetes pods CIDR"}, {"source": "10.96.0.0/16", "protocol": "all", "isStateless": false, "description": "Kubernetes services CIDR"}, {"source": "0.0.0.0/0", "protocol": "1", "isStateless": false, "icmpOptions": {"type": 3, "code": 4}, "description": "Path MTU discovery"} ]' \ --query "data.id" \ --raw-output)安全注意:为了简单起见,此示例安全列表非常广泛。对于生产环境,将 SSH 限制为堡垒子网和 IP 范围,并首选每个子网单独的安全列表或 NSG,以便负载平衡器子网不允许从
0.0.0.0/0执行 SSH。安全默认设置:首先将 SSH 限制为公共 IP,然后将 SSH 规则仅附加到堡垒子网。您可以将 Kubernetes 云池/服务 CIDR 保留在 Worker 子网中,并且完全从负载平衡器子网省略 SSH。
可选(推荐)拆分:为堡垒子网创建一个小型的仅 SSH 安全列表,并为 worker/LB 子网创建一个单独的列表。
BASTION_SL_ID=$(oci network security-list create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --vcn-id "${VCN_ID}" \ --display-name "${CLUSTER_NAME}-bastion-sl" \ --egress-security-rules '[{"destination": "0.0.0.0/0", "protocol": "all", "isStateless": false}]' \ --ingress-security-rules '[ {"source": "YOUR_PUBLIC_IP/32", "protocol": "6", "isStateless": false, "tcpOptions": {"destinationPortRange": {"min": 22, "max": 22}}, "description": "SSH from your IP"} ]' \ --query "data.id" \ --raw-output) WORKER_SL_ID=$(oci network security-list create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --vcn-id "${VCN_ID}" \ --display-name "${CLUSTER_NAME}-worker-sl" \ --egress-security-rules '[{"destination": "0.0.0.0/0", "protocol": "all", "isStateless": false}]' \ --ingress-security-rules '[ {"source": "10.0.0.0/16", "protocol": "all", "isStateless": false, "description": "VCN internal traffic"}, {"source": "10.244.0.0/16", "protocol": "all", "isStateless": false, "description": "Kubernetes pods CIDR"}, {"source": "10.96.0.0/16", "protocol": "all", "isStateless": false, "description": "Kubernetes services CIDR"}, {"source": "0.0.0.0/0", "protocol": "1", "isStateless": false, "icmpOptions": {"type": 3, "code": 4}, "description": "Path MTU discovery"} ]' \ --query "data.id" \ --raw-output) LB_SL_ID=$(oci network security-list create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --vcn-id "${VCN_ID}" \ --display-name "${CLUSTER_NAME}-lb-sl" \ --egress-security-rules '[{"destination": "0.0.0.0/0", "protocol": "all", "isStateless": false}]' \ --ingress-security-rules '[ {"source": "10.0.0.0/16", "protocol": "all", "isStateless": false, "description": "VCN internal traffic"}, {"source": "0.0.0.0/0", "protocol": "6", "isStateless": false, "tcpOptions": {"destinationPortRange": {"min": 80, "max": 80}}, "description": "HTTP (public LB)"}, {"source": "0.0.0.0/0", "protocol": "6", "isStateless": false, "tcpOptions": {"destinationPortRange": {"min": 443, "max": 443}}, "description": "HTTPS (public LB)"} ]' \ --query "data.id" \ --raw-output)注:如果仅使用内部负载平衡器,请将上面的
0.0.0.0/0源替换为10.0.0.0/16(或您的 VCN CIDR)。用法:将BASTION_SL_ID连接到堡垒子网,将WORKER_SL_ID连接到 API/worker 子网,将LB_SL_ID连接到负载平衡器子网。注: GPU Worker 节点需要在集群中注册 Kubernetes 云池 CIDR (
10.244.0.0/16) 和服务 CIDR (10.96.0.0/16) 规则。ICMP 类型 3 代码 4 规则启用路径 MTU 搜索,从而防止数据包碎片问题。 -
创建子网。该集群需要四个子网:一个用于 Kubernetes API 端点,一个用于 worker 节点,一个用于负载平衡器,一个用于用于访问专用集群的堡垒主机。
API_SUBNET_ID=$(oci network subnet create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --vcn-id "${VCN_ID}" \ --display-name "${CLUSTER_NAME}-api-subnet" \ --cidr-block "10.0.0.0/28" \ --route-table-id "${PRIVATE_RT_ID}" \ --security-list-ids "[\"${SL_ID}\"]" \ --dns-label "kubeapi" \ --prohibit-public-ip-on-vnic true \ --query "data.id" \ --raw-output) WORKER_SUBNET_ID=$(oci network subnet create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --vcn-id "${VCN_ID}" \ --display-name "${CLUSTER_NAME}-worker-subnet" \ --cidr-block "10.0.10.0/24" \ --route-table-id "${PRIVATE_RT_ID}" \ --security-list-ids "[\"${SL_ID}\"]" \ --dns-label "workers" \ --prohibit-public-ip-on-vnic true \ --query "data.id" \ --raw-output) LB_SUBNET_ID=$(oci network subnet create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --vcn-id "${VCN_ID}" \ --display-name "${CLUSTER_NAME}-lb-subnet" \ --cidr-block "10.0.20.0/24" \ --route-table-id "${PUBLIC_RT_ID}" \ --security-list-ids "[\"${SL_ID}\"]" \ --dns-label "loadbalancers" \ --query "data.id" \ --raw-output) BASTION_SUBNET_ID=$(oci network subnet create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --vcn-id "${VCN_ID}" \ --display-name "${CLUSTER_NAME}-bastion-subnet" \ --cidr-block "10.0.30.0/24" \ --route-table-id "${PUBLIC_RT_ID}" \ --security-list-ids "[\"${SL_ID}\"]" \ --dns-label "bastion" \ --query "data.id" \ --raw-output)子网 CIDR 可见性 用途 API 端点 10.0.0.0/28专用 Kubernetes API 服务器 Worker 节点 10.0.10.0/24专用 GPU 计算节点 负载平衡器 10.0.20.0/24公共 外部服务访问 堡垒 10.0.30.0/24公共 用于专用集群访问的 SSH 隧道
任务 4:创建 OKE 群集
使用在任务 3 中创建的网络资源在 OKE 上部署托管 Kubernetes 集群。预配集群大约需要 10 分钟。本教程创建专用集群(脚本默认设置),它不使用 Kubernetes API 端点的预留公共 IP。对于生产工作负载,建议使用专用集群,因为 API 服务器未向公共 Internet 公开。
-
使用专用端点创建 OKE 集群。
oci ce cluster create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --name "${CLUSTER_NAME}" \ --vcn-id "${VCN_ID}" \ --kubernetes-version "${KUBERNETES_VERSION}" \ --endpoint-subnet-id "${API_SUBNET_ID}" \ --service-lb-subnet-ids "[\"${LB_SUBNET_ID}\"]" \ --endpoint-public-ip-enabled false该命令返回工作请求 ID。从群集列表中获取群集 ID。
CLUSTER_ID=$(oci ce cluster list \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --lifecycle-state CREATING \ --query 'data[0].id' \ --raw-output) echo "CLUSTER_ID=${CLUSTER_ID}"注:专用集群不需要预留公共 IP。Worker 节点仍可通过 NAT 网关访问互联网,以提取容器映像并下载模型。只有
kubectl访问需要通过堡垒的 SSH 隧道(在后续步骤中配置)。 -
等待群集变为 ACTIVE。此步骤大约需要 10 分钟。
oci ce cluster get \ --cluster-id "${CLUSTER_ID}" \ --query "data.\"lifecycle-state\"" \ --raw-output轮询命令,直到输出返回
ACTIVE。可选:显示简洁的状态概要(包括专用 API 端点)。
oci ce cluster get \ --cluster-id "${CLUSTER_ID}" \ --query 'data.{name:name,state:"lifecycle-state","k8s-version":"kubernetes-version",endpoint:endpoints."private-endpoint"}' \ --output table
kubectl get nodes -o wide
-
创建 OCI 堡垒以访问专用集群。堡垒提供连接到专用 Kubernetes API 端点的托管 SSH 隧道。
BASTION_ID=$(oci bastion bastion create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --bastion-type STANDARD \ --target-subnet-id "${BASTION_SUBNET_ID}" \ --name "${CLUSTER_NAME}-bastion" \ --client-cidr-list '["YOUR_PUBLIC_IP/32"]' \ --query "data.id" \ --raw-output)注:将
YOUR_PUBLIC_IP/32替换为您当前的公共 IP。对于共享网络,请改用公司 CIDR 块。等待堡垒变为 ACTIVE(大约 1 分钟)。
oci bastion bastion get \ --bastion-id "${BASTION_ID}" \ --query "data.\"lifecycle-state\"" \ --raw-output安全注意:对于生产环境,请勿使用
0.0.0.0/0。将--client-cidr-list限制为公共 IP 或公司 CIDR(例如"YOUR_PUBLIC_IP/32"),否则互联网上的任何人都可以尝试堡垒会话。 -
使用专用端点下载 kubeconfig。
oci ce cluster create-kubeconfig \ --cluster-id "${CLUSTER_ID}" \ --file "${HOME}/.kube/config" \ --region "${OCI_REGION}" \ --token-version 2.0.0 \ --kube-endpoint PRIVATE_ENDPOINT -
获取 SSH 隧道的专用端点 IP 地址。
PRIVATE_ENDPOINT=$(oci ce cluster get \ --cluster-id "${CLUSTER_ID}" \ --query "data.endpoints.\"private-endpoint\"" \ --raw-output) PRIVATE_IP="${PRIVATE_ENDPOINT%:*}" echo "Private endpoint IP: ${PRIVATE_IP}" -
创建堡垒端口转发会话。您将需要 SSH 公共密钥文件。
SESSION_ID=$(oci bastion session create-port-forwarding \ --bastion-id "${BASTION_ID}" \ --target-private-ip "${PRIVATE_IP}" \ --target-port 6443 \ --session-ttl 10800 \ --display-name "kubectl-tunnel" \ --ssh-public-key-file ~/.ssh/id_rsa.pub \ --query "data.id" \ --raw-output)等待会话变为 ACTIVE,然后获取 SSH 命令。
oci bastion session get \ --session-id "${SESSION_ID}" \ --query "data.{state:\"lifecycle-state\", ssh:\"ssh-metadata\".command}" 2>&1 -
打开一个单独的终端并使用上一步中的命令启动 SSH 隧道。隧道将本地端口 6443 转发到专用 Kubernetes API。
ssh -i ~/.ssh/id_rsa -o IdentitiesOnly=yes -N -L 6443:<PRIVATE_IP>:6443 \ -p 22 -o ServerAliveInterval=30 \ <SESSION_OCID>@host.bastion.<REGION>.oci.oraclecloud.com注:将
<PRIVATE_IP>、<SESSION_OCID>和<REGION>替换为上一步中的值。在会话期间将此终端保持打开状态。当 SSH 代理加载了多个密钥时,-o IdentitiesOnly=yes标志可防止“验证失败次数过多”错误。 -
更新 kubeconfig 以通过本地隧道进行连接。
CLUSTER_NAME_KUBE=$(kubectl config view --minify -o jsonpath='{.clusters[0].name}') kubectl config set-cluster "${CLUSTER_NAME_KUBE}" \ --server=https://127.0.0.1:6443 \ --insecure-skip-tls-verify=true注:需要
--insecure-skip-tls-verify标志,因为已为专用端点 IP(而不是127.0.0.1)签发了群集证书。这是安全的,因为流量是通过 SSH 隧道加密的。 -
如果您使用的是非默认 OCI CLI 配置文件(例如
API_KEY_AUTH),请更新 kubeconfig 以使用它。生成的 kubeconfig 缺省为用于生成令牌的DEFAULT配置文件。kubectl config set-credentials \ $(kubectl config view --minify -o jsonpath='{.users[0].name}') \ --exec-env=OCI_CLI_PROFILE=${OCI_PROFILE}提示:步骤 6-9 由
./entry_point.sh tunnel自动执行,如果 SSH 隧道在长时间运行的操作(如磁盘扩展)期间断开,也会自动重新连接。在单独的终端中运行它,并使其在会话期间运行。 -
验证群集访问。
kubectl get nodes
此时,输出将不显示任何节点,因为尚未添加 GPU 节点池。
No resources found
任务 5:添加 GPU 节点池
将具有 GPU 计算实例的节点池添加到 OKE 集群。
-
查找与 GPU 兼容的最新 OKE 节点映像。OKE 需要预先安装有 kubelet 和节点注册组件的特定映像。使用
node-pool-optionsAPI 查找适用于 Kubernetes 版本的正确映像。GPU_IMAGE_ID=$(oci ce node-pool-options get \ --node-pool-option-id all \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --query "data.sources[?contains(\"source-name\", 'GPU') && contains(\"source-name\", 'OKE-${KUBERNETES_VERSION#v}') && contains(\"source-name\", '8.10')].\"image-id\" | [0]" \ --raw-output) echo "GPU Image: ${GPU_IMAGE_ID}"注:与 Kubernetes 版本匹配的 Oracle Linux 8.10 GPU 映像的查询筛选器(例如
OKE-1.31.10)。如果需要基于 ARM 的映像,请将8.10替换为相应的筛选器。 -
确定具有 GPU 配置的可用性域。并非所有可用性域都具有 GPU 容量。
AD=$(oci iam availability-domain list \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --query "data[${GPU_AD_INDEX}].name" \ --raw-output) echo "Availability Domain: ${AD}"注: GPU 容量因区域和可用性域而异。如果节点池创建失败并出现“主机容量不足”错误,请尝试使用其他可用性域 (
GPU_AD_INDEX) 或 GPU 配置,或者通过常规 OCI 流程请求容量。 -
创建具有 200 GB 引导卷的 GPU 节点池。
oci ce node-pool create \ --compartment-id "${OCI_COMPARTMENT_ID}" \ --cluster-id "${CLUSTER_ID}" \ --name "${GPU_NODE_POOL_NAME:-gpu-pool}" \ --kubernetes-version "${KUBERNETES_VERSION}" \ --node-shape "${GPU_SHAPE}" \ --node-image-id "${GPU_IMAGE_ID}" \ --node-boot-volume-size-in-gbs "${GPU_BOOT_VOLUME_GB:-200}" \ --size "${GPU_NODE_COUNT}" \ --placement-configs "[{\"availabilityDomain\": \"${AD}\", \"subnetId\": \"${WORKER_SUBNET_ID}\"}]" \ --initial-node-labels '[{"key": "app", "value": "gpu"}, {"key": "nvidia.com/gpu", "value": "true"}]'注: vLLM Helm 图表稍后会使用节点标签
app=gpu和nvidia.com/gpu=true来调度 GPU 节点上的推断云池。200 GB 引导卷为 vLLM 容器映像 (container image,~10 GB) 和模型权重提供空间,但文件系统在使用前必须进行扩展(请参见任务 8)。 -
等待 GPU 节点变为就绪。这通常需要 5 – 10 分钟,而节点会预配、引导、安装 GPU 驱动程序以及向群集注册。
注: GPU 实例受容量约束。如果节点池处于 CREATING 状态,请在 OCI 控制台中或使用
oci ce node-pool get检查节点状态。“超出主机容量”错误表示该可用性域中没有可用的 GPU 实例。要解决此问题,请尝试使用其他可用性域(GPU_AD_INDEX=0或GPU_AD_INDEX=2),尝试使用其他 GPU 配置,或者通过 OCI 控制台或支持单请求容量预留。kubectl get nodes -w节点准备就绪后预期输出:
NAME STATUS ROLES AGE VERSION 10.0.10.x Ready node 5m v1.31.10 -
验证是否在节点上检测到 GPU。
kubectl get nodes -o=custom-columns=NAME:.metadata.name,GPUs:.status.capacity.'nvidia\.com/gpu'预期输出:
NAME GPUs 10.0.10.x 1 -
为 CoreDNS 打补丁以在 GPU 节点上调度。OKE GPU 节点具有
nvidia.com/gpu=present:NoSchedule污点。在仅具有 GPU 节点的集群中,像 CoreDNS 这样的系统云池无法调度,而不会对此污点进行容忍。如果没有 DNS,云池将无法解析外部主机名以下载模型。kubectl patch deployment coredns -n kube-system --type='json' \ -p='[{"op": "add", "path": "/spec/template/spec/tolerations/-", "value": {"key": "nvidia.com/gpu", "operator": "Exists", "effect": "NoSchedule"}}]' kubectl patch deployment kube-dns-autoscaler -n kube-system --type='json' \ -p='[{"op": "add", "path": "/spec/template/spec/tolerations/-", "value": {"key": "nvidia.com/gpu", "operator": "Exists", "effect": "NoSchedule"}}]'验证 CoreDNS 是否正在运行。
kubectl get pods -n kube-system | grep coredns注:如果群集具有用于系统工作负荷的专用 CPU 节点池,则不需要执行此步骤。仅当 GPU 节点是集群中的唯一节点时,才需要此补丁程序。
oci ce node-pool list --compartment-id "${OCI_COMPARTMENT_ID}" --cluster-id "${CLUSTER_ID}" \ --query 'data[].{name:name,state:"lifecycle-state",shape:"node-shape",size:"node-config-details".size}' --output table
任务 6:安装 NVIDIA 设备插件
安装 NVIDIA 设备插件,以便 Kubernetes 可以在 GPU 上检测和调度工作负载。
-
应用 NVIDIA 设备插件 DaemonSet。
kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.1/nvidia-device-plugin.yml -
等待插件云池准备就绪。
kubectl wait --for=condition=Ready pods -l name=nvidia-device-plugin-ds -n kube-system --timeout=300s注:某些 OKE GPU 节点映像包括预安装的 NVIDIA 设备插件 (
nvidia-gpu-device-plugin)。如果映像已包含该映像,则应用上游 DaemonSet 将创建另一个不会导致冲突的实例。自动脚本 (entry_point.sh deploy-vllm) 始终会安装它,以确保无论节点映像版本如何,GPU 检测都能正常工作。 -
确认 GPU 可由 Kubernetes 分配。
kubectl get nodes -o=custom-columns=NAME:.metadata.name,GPUs:.status.allocatable.'nvidia\.com/gpu'预期输出:
NAME GPUs 10.0.10.x 1 -
修补 CoreDNS 以容忍 GPU 节点污染。在 GPU 节点是唯一 worker 节点的集群中,CoreDNS 云池无法调度,因为 OKE GPU 节点携带
nvidia.com/gpu=present:NoSchedule污点。如果没有 DNS,云池将无法解析映像注册表或模型下载 URL。kubectl patch deployment coredns -n kube-system --type='json' -p='[ {"op": "add", "path": "/spec/template/spec/tolerations/-", "value": {"key": "nvidia.com/gpu", "operator": "Exists", "effect": "NoSchedule"}} ]' kubectl rollout status deployment/coredns -n kube-system --timeout=60s注:仅当 GPU 节点是集群中的唯一 worker 节点时,才需要执行此步骤。如果您具有用于系统工作负荷的专用 CPU 节点池,则默认情况下 CoreDNS 会调度该池,并且不需要此修补程序。
任务 7:配置存储
应用 OCI Block Volume StorageClass 为模型权重提供持久性存储。
-
应用 StorageClass 定义。
kubectl apply -f oci-block-storage-sc.yaml该文件定义两个性能层:
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: oci-block-storage-enc provisioner: blockvolume.csi.oraclecloud.com parameters: vpusPerGB: "10" reclaimPolicy: Delete volumeBindingMode: WaitForFirstConsumer allowVolumeExpansion: trueStorageClass 性能 用例 oci-block-storage-enc平衡 ( vpusPerGB: 10)适用于大多数模型的默认、经济高效 oci-block-storage-hp高性能 ( vpusPerGB: 20)更快的模型加载,适用于大型模型 -
验证 StorageClasses 是否可用。
kubectl get storageclass
注:对于需要跨多个云池共享存储的多节点部署,请使用 OCI 文件存储服务 (NFS) 和
ReadWriteMany访问模式,而不是块存储卷。
任务 8:扩展 GPU 节点文件系统
无论您指定的引导卷大小是多少,OCI 引导卷具有固定约 47 GB 的分区。仅 vLLM 容器映像大约为 10 GB,模型权重需要额外的空间。在部署 vLLM 之前,必须扩展文件系统以避免 DiskPressure 逐出。
注:这是特定于 OCI 的要求。引导卷预配为 200 GB,但默认情况下操作系统仅分区约 47 GB。剩余空间必须手动声明。
-
验证 GPU 节点上的当前文件系统大小。
GPU_NODE=$(kubectl get nodes -l app=gpu -o jsonpath='{.items[0].metadata.name}') kubectl run check-disk --rm -i --restart=Never \ --image=busybox:latest \ --overrides="{\"spec\":{\"nodeName\":\"${GPU_NODE}\",\"tolerations\":[{\"operator\":\"Exists\"}],\"containers\":[{\"name\":\"check\",\"image\":\"busybox:latest\",\"command\":[\"sh\",\"-c\",\"chroot /host df -h / | tail -1\"],\"securityContext\":{\"privileged\":true},\"volumeMounts\":[{\"name\":\"host\",\"mountPath\":\"/host\"}]}],\"volumes\":[{\"name\":\"host\",\"hostPath\":{\"path\":\"/\"}}]}}"输出将显示大约 47 GB 总数,确认需要进行扩展。
-
在 GPU 节点上创建一个特权 pod 以运行扩展命令。
kubectl run expand-disk --restart=Never \ --image=busybox:latest \ --overrides="{\"spec\":{\"nodeName\":\"${GPU_NODE}\",\"tolerations\":[{\"operator\":\"Exists\"}],\"containers\":[{\"name\":\"expand\",\"image\":\"busybox:latest\",\"command\":[\"sleep\",\"600\"],\"securityContext\":{\"privileged\":true},\"volumeMounts\":[{\"name\":\"host\",\"mountPath\":\"/host\"}]}],\"volumes\":[{\"name\":\"host\",\"hostPath\":{\"path\":\"/\"}}]}}"等待 pod 启动。
kubectl wait --for=condition=Ready pod/expand-disk --timeout=60s -
在一个
kubectl exec命令中运行所有四个扩展步骤。将它们一起运行可以避免kubectl exec在步骤之间返回退出代码 137 (SIGKILL) 的风险,这在主机上的重磁盘 I/O 过程中可能会发生。kubectl exec expand-disk -- chroot /host bash -c ' set -x growpart /dev/sda 3 || echo "growpart: partition may already be expanded" sleep 3 pvresize /dev/sda3 lvextend -l +100%FREE /dev/ocivolume/root || echo "lvextend: may already be extended" xfs_growfs / echo "EXPANSION_COMPLETE" df -h / '阶梯图 命令 用途 1 growpart /dev/sda 3展开分区 3 以使用完整磁盘 2 pvresize /dev/sda3调整 LVM 物理卷大小 3 lvextend -l +100%FREE /dev/ocivolume/root扩展逻辑卷 4 xfs_growfs /增长 XFS 文件系统以填充卷 注意:这四个操作都是幂等的。如果 exec 返回退出代码 137,则可以安全地重新运行整个块。在输出中查找
EXPANSION_COMPLETE以确认成功。 -
重新启动 kubelet,以便节点报告更新的可分配存储,然后验证并清除。
kubectl exec expand-disk -- nsenter -t 1 -m -p -- systemctl restart kubelet 2>/dev/null \ || echo "Warning: kubelet restart returned non-zero (non-critical)" kubectl exec expand-disk -- chroot /host df -h / kubectl delete pod expand-disk --force注:
nsenter命令将输入主机的 PID 名称空间以访问 systemd。普通chroot /host systemctl restart kubelet失败,因为它无法从 chroot 内连接到 systemd 总线。预期输出总量应约为 189 GB。
任务 9:部署 vLLM 生产堆栈
使用 Helm 安装 vLLM 推断堆栈。
-
添加 vLLM Helm 系统信息库。
helm repo add vllm https://vllm-project.github.io/production-stack helm repo update -
查看 Helm 值文件。
production_stack_specification.yaml为 OCI 配置模型、资源和存储。servingEngineSpec: runtimeClassName: "" modelSpec: - name: "gpt-oss" repository: "vllm/vllm-openai" tag: "latest" modelURL: "openai/gpt-oss-20b" replicaCount: 1 requestCPU: 4 requestMemory: "24Gi" requestGPU: 1 # No HF token needed - Apache 2.0 licensed OpenAI open source model pvcStorage: "100Gi" pvcAccessMode: - ReadWriteOnce storageClass: "oci-block-storage-enc" nodeSelector: app: gpu tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" extraArgs: - "--max-model-len=8192" - "--gpu-memory-utilization=0.90"注:
openai/gpt-oss-20b模型是专家 (MoE) 模型的混合体,每个正向传递具有 20B 总参数和 3.6B 活动参数。它是在 Apache 2.0 许可证下发布的,因此不需要 Hugging Face 令牌。vllm/vllm-openai容器映像提供与 OpenAI 兼容的 API 服务器,允许客户端对自托管端点使用标准 OpenAI SDK 调用。 -
部署堆栈。请不要在此处使用
--wait,因为路由器云池将 CrashLoop,直到在下一步中打补丁为止。helm upgrade -i \ vllm vllm/vllm-stack \ -f production_stack_specification.yaml等待 vLLM 引擎 pod 启动(接下来将为路由器打补丁)。
kubectl wait --for=condition=Ready pods -l model=gpt-oss --timeout=600s注:引擎云池需要几分钟时间才能变为“就绪”,因为它会在首次启动时下载模型权重。如果 pod 保持在
ContainerCreating中,容器映像 (~10 GB) 仍处于拉取状态。使用kubectl describe pod <pod-name>检查进度。 -
为路由器部署打补丁。路由器需要 GPU 迭代(因此,它可以安排 GPU 节点是唯一具有容量的节点时),并增加内存限制(默认的 500 Mi 可能导致 OOMKill)。
kubectl patch deployment vllm-deployment-router --type='json' -p='[ {"op": "add", "path": "/spec/template/spec/tolerations", "value": [{"key": "nvidia.com/gpu", "operator": "Exists", "effect": "NoSchedule"}]}, {"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/memory", "value": "512Mi"}, {"op": "replace", "path": "/spec/template/spec/containers/0/resources/limits/memory", "value": "1Gi"} ]'注:由于 OKE GPU 节点具有阻止非 GPU 工作负载调度的
nvidia.com/gpu=present:NoSchedule污点,因此需要 GPU 宽容。由于路由器不使用 GPU,但需要在某个地方运行,因此这种宽容允许它在 GPU 节点上调度。在具有专用 CPU 节点池的集群中,不需要此宽容。 -
确认已部署 Helm 发行版。
helm list
-
验证云池是否正在运行。
kubectl get pods预期输出:
NAME READY STATUS RESTARTS AGE vllm-deployment-router-xxxxxxxxxx-xxxxx 1/1 Running 0 5m vllm-gpt-oss-deployment-vllm-xxxxxxxxxx-xxxxx 1/1 Running 0 5mkubectl get pods
-
在云池日志中检查模型加载进度。
kubectl logs -f deployment/vllm-gpt-oss-deployment-vllm等到您看到一条消息,指示模型已加载并且服务器已准备好接受请求。
任务 10:测试推断端点
验证部署是否正在提供推断请求。vLLM 生产堆栈通过路由器服务公开与 OpenAI 兼容的 API,因此任何 OpenAI SDK 客户端或 curl 命令都可以与其交互。
下图显示了推断请求生命周期:从客户机请求到路由器的引擎选择逻辑,再到 vLLM 引擎的预填充和解码阶段,然后作为流响应返回。

-
列出可用模型以确认部署运行状况良好。
kubectl get svc vllm-router-service路由器服务为所有已部署模型提供了 API 网关。由于集群使用专用端点,因此可以通过
kubectl port-forward访问服务。 -
从本地计算机启动端口转发到路由器服务。打开一个新终端(保持 SSH 隧道在另一个终端中运行)并运行:
kubectl port-forward svc/vllm-router-service 8080:80这会将计算机上的
localhost:8080映射到群集内路由器服务上的端口 80。安全注意:
kubectl port-forward在本地绑定,不会公开服务。通过堡垒隧道使用专用集群时,这是最安全的测试方法。注: port-forward 命令在前台运行。测试时保持此终端打开状态。按 Ctrl+C 完成后将其停止。
-
在另一个终端中,通过查询模型端点来验证模型是否可用。
curl -s http://localhost:8080/v1/models | python3 -m json.tool预期输出:
{ "object": "list", "data": [ { "id": "openai/gpt-oss-20b", "object": "model", "created": 1234567890, "owned_by": "vllm" } ] } -
发送文本完成请求。
curl -s http://localhost:8080/v1/completions \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-oss-20b", "prompt": "Oracle Cloud Infrastructure is", "max_tokens": 50 }' | python3 -m json.tool预期输出(缩写):
{ "id": "cmpl-xxxxxxxxxxxx", "object": "text_completion", "model": "openai/gpt-oss-20b", "choices": [ { "index": 0, "text": " a cloud computing platform that provides ...", "finish_reason": "length" } ], "usage": { "prompt_tokens": 5, "completion_tokens": 50, "total_tokens": 55 } } -
发送聊天完成请求。这是 OpenAI Python SDK 使用的相同格式,也是以编程方式与 LLM 进行交互的最常见方式。
curl -s http://localhost:8080/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-oss-20b", "messages": [{"role": "user", "content": "What is Oracle Cloud Infrastructure in one sentence?"}], "max_tokens": 100 }' | python3 -m json.tool预期输出(缩写):
{ "id": "chatcmpl-xxxxxxxxxxxx", "object": "chat.completion", "model": "openai/gpt-oss-20b", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "Oracle Cloud Infrastructure (OCI) is Oracle's enterprise-grade cloud platform that provides a full range of services for building, deploying, and managing applications and workloads..." }, "finish_reason": "length" } ], "usage": { "prompt_tokens": 71, "completion_tokens": 100, "total_tokens": 171 } }这两种响应都包括模型在
choices数组中生成的文本、令牌使用情况统计信息以及stop(模型自然完成)或length(输出在max_tokens中被截断)的finish_reason。注: API 请求中的模型名称是完整模型路径 (
openai/gpt-oss-20b),它与 Helm 值中的modelURL字段匹配。任何与 OpenAI 兼容的客户机都可以使用此端点,方法是将base_url设置为http://localhost:8080/v1。
-
针对短请求评估端到端延迟。
time curl -s http://localhost:8080/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"openai/gpt-oss-20b","messages":[{"role":"user","content":"What is Kubernetes?"}],"max_tokens":50}' > /dev/null在单个 A10 GPU 上,需要 1-3 秒的端到端完成。第一个令牌 (TTFT) 的时间通常为 50-200 毫秒,具体取决于提示长度。为了提高吞吐量,在 Helm 值中增加
replicaCount以在路由器后面添加更多引擎副本。
任务 11:通过 OCI 负载平衡器公开(可选)
使推断端点可通过 OCI 负载平衡器外部访问。
安全注意:默认情况下,这会将推理 API 公开给公共互联网。在没有 TLS、验证(API 密钥/JWT/mTLS)和 IP 允许列表或 WAF 控制的情况下,不要在生产环境中启用此功能。如果必须将其公开,请在它前面添加一个入口控制器或 API 网关,以强制实施验证和速率限制,或者使用内部负载平衡器。
-
为路由器服务打补丁以使用 LoadBalancer 类型。
kubectl patch svc vllm-router-service \ -p '{"spec": {"type": "LoadBalancer"}}'注:如果需要内部负载平衡器,请向服务添加 OCI 注释(示例如下)。这会将端点保持在 VCN 内。
kubectl annotate svc vllm-router-service \ "service.beta.kubernetes.io/oci-load-balancer-internal"="true" -
等待分配外部 IP。
kubectl get svc vllm-router-service -w预配负载平衡器后预计输出:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) vllm-router-service LoadBalancer 10.96.x.x 129.xxx.xxx.xx 80:xxxxx/TCPkubectl get svc
-
测试外部端点。
curl http://<EXTERNAL-IP>/v1/completions \ -H "Content-Type: application/json" \ -d '{ "model": "openai/gpt-oss-20b", "prompt": "Hello from OCI!", "max_tokens": 50 }'
任务 12:配置多 GPU Tensor 并行性(高级)
在裸金属配置上的多个 GPU 上部署更大的模型。
张量并行度在单个节点上的多个 GPU 上拆分模型。当模型的内存要求超过单个 GPU 时,这是必需的。例如,Meta Llama 3.1 70B 需要大约 140 GB 的 GPU 内存,这超过了任何单个 GPU 的容量,但适用于 8x A100 80 GB 或 8x H100 GPU。
-
使用 Hugging Face 令牌创建 Kubernetes 密钥。Llama 3.1 70B 等受限模型需要验证。
kubectl create secret generic hf-token-secret \ --from-literal=token=YOUR_HUGGINGFACE_TOKEN -
使用多 GPU 配置更新
production_stack_specification.yaml。servingEngineSpec: modelSpec: - name: "llama70b" repository: "vllm/vllm-openai" tag: "latest" modelURL: "meta-llama/Llama-3.1-70B-Instruct" replicaCount: 1 tensorParallelSize: 8 requestCPU: 32 requestMemory: "256Gi" requestGPU: 8 hf_token: secretName: "hf-token-secret" secretKey: "token" pvcStorage: "500Gi" pvcAccessMode: - ReadWriteOnce storageClass: "oci-block-storage-enc" nodeSelector: node.kubernetes.io/instance-type: "BM.GPU.H100.8" tolerations: - key: "nvidia.com/gpu" operator: "Exists" effect: "NoSchedule" extraArgs: - "--max-model-len=8192" - "--gpu-memory-utilization=0.95" - "--tensor-parallel-size=8" -
使用更新的值进行部署。与任务 9 一样,不要使用
--wait。路由器将 CrashLoop,直到打补丁为止。helm upgrade -i \ vllm vllm/vllm-stack \ -f production_stack_specification.yaml kubectl wait --for=condition=Ready pods -l model=llama70b --timeout=900s然后修补路由器(与任务 9、步骤 4 相同)并验证:
kubectl patch deployment vllm-deployment-router --type='json' -p='[ {"op": "add", "path": "/spec/template/spec/tolerations", "value": [{"key": "nvidia.com/gpu", "operator": "Exists", "effect": "NoSchedule"}]}, {"op": "replace", "path": "/spec/template/spec/containers/0/resources/requests/memory", "value": "512Mi"}, {"op": "replace", "path": "/spec/template/spec/containers/0/resources/limits/memory", "value": "1Gi"} ]' -
验证 pod 是否正在运行并且所有 GPU 都正在使用中。
kubectl get pods kubectl logs -f deployment/vllm-llama70b-deployment-vllm
注:在部署多 GPU 配置之前,请确保 OKE 集群具有具有具有相应裸金属 GPU 配置(例如
BM.GPU.H100.8)的节点池。
任务 13:将 OCI 对象存储用于模型(高级)
从 OCI Object Storage 加载模型权重,而不是从 Hugging Face 下载。这对于专用模型、OCI 中的更快下载或没有外部 Internet 访问的环境非常有用。
-
将模型权重上载到 OCI 对象存储桶。在 OCI 控制台中导航到 Storage(存储) > Object Storage(对象存储),如果您还没有存储桶,请创建一个存储桶。
-
为存储桶创建预先验证的请求 (PAR) URL。在 OCI 控制台中,选择存储桶,单击预先验证的请求,然后创建一个具有读取访问权限的新请求。
-
更新
production_stack_specification.yaml以使用 PAR URL。servingEngineSpec: modelSpec: - name: "custom-model" repository: "iad.ocir.io/YOUR_TENANCY/vllm-custom:latest" modelURL: "/models/custom-model" env: - name: BUCKET_PAR_URL value: "https://objectstorage.us-ashburn-1.oraclecloud.com/p/xxx/n/namespace/b/bucket/o" - name: MODEL_NAME value: "custom-model" -
使用更新的值进行部署(不使用
--wait)。有关原因,请参见任务 9)。helm upgrade -i \ vllm vllm/vllm-stack \ -f production_stack_specification.yaml kubectl wait --for=condition=Ready pods -l model=custom-model --timeout=600s -
通过检查云池日志来验证模型从对象存储加载。
kubectl logs -f deployment/vllm-custom-model-deployment-vllm
任务 14:清除资源
删除所有已部署的资源以避免持续收费。
-
使用清理脚本删除 Kubernetes 资源。
cd production-stack/deployment_on_cloud/oci ./clean_up.sh这将卸载 Helm 发行版,删除所有 PersistentVolumeClaims、PersistentVolumes 和定制 vLLM 资源。
-
删除 OKE 集群和所有 OCI 网络资源。
./entry_point.sh cleanup这将按顺序删除以下资源:
- GPU 节点池
- OKE 集群
- 堡垒主机(如果已创建)
- 子网(API、worker、负载平衡器、堡垒)
- 安全列表
- Service Gateway、NAT Gateway 和 Internet Gateway
- 路由表
- VCN
-
验证是否已在 OCI 控制台的开发人员服务 > Kubernetes 集群和网络 > 虚拟云网络下删除了所有资源。
./entry_point.sh cleanup
注:确保删除所有资源以避免持续收费。GPU 实例和块存储卷在闲置时也会产生成本。
下一步操作
本教程部署了一个功能推断堆栈。对于生产负载,请考虑以下增强功能:
- 监视: vLLM 在每个引擎云池的
/metrics上公开 Prometheus 度量。连接 Prometheus + Grafana 堆栈以跟踪到第一个令牌、每秒令牌、KV 高速缓存使用率和请求队列深度的时间。 - 自动缩放:使用定制度量(通过 DCGM 请求队列深度或 GPU 利用率)配置 Kubernetes 水平云池自动缩放器,以在负载下自动缩放引擎副本。
- 安全性强化:将
BASTION_CLIENT_CIDR限制在 IP 范围内,添加 Kubernetes 网络策略以隔离推断名称空间,并将模型凭证存储在 OCI Vault 中,而不是 Kubernetes 密钥中。 - 高可用性:跨多个可用性域分布 GPU 节点,并在 Helm 值中增加
replicaCount,以便路由器可以在引擎副本之间进行故障转移。 - 成本优化:不使用时使用
./entry_point.sh cleanup。对于开发/测试负载,请考虑可抢占的 GPU 实例。监视 GPU 利用率,调整节点池的大小。 - 基础设施即代码:为此部署使用 OCI Terraform 提供程序或 OCI 资源管理器进行编码,以实现可重复的版本控制基础设施。
相关链接
确认
- 作者 — Federico Kamelhar(Agentic AI 高级首席架构师)
更多学习资源
通过 docs.oracle.com/learn 浏览其他实验室,或者通过 Oracle Learning YouTube 频道访问更多免费学习内容。此外,请访问 education.oracle.com/learning-explorer 以成为 Oracle Learning Explorer。
有关产品文档,请访问 Oracle 帮助中心。
Deploy OpenAI vLLM Production Stack on Oracle Kubernetes Engine (OKE)
G50930-01