Day 8:k3s 单节点 + NVIDIA Device Plugin + vLLM 跑 Qwen2.5-3B
Day 8 第二次尝试。第一次想把一台跨 WAN 的 A800-SXM4-40GB GPU 节点 kubeadm join 进 Day 1 主集群 —— Cilium 跨 WAN VXLAN 死活调不通,折腾 1 小时放弃。换路线:GPU 节点装独立的 k3s 单节点小集群,主集群通过公网 NodePort + SSH tunnel 调,vLLM 暴露 OpenAI-compatible /v1 端点跑 Qwen2.5-3B-Instruct。
整篇按 4 个阶段走:
- Stage 0 —— 复盘跨 WAN 加主集群为什么不行
- Stage A —— k3s 单节点装机,踩 3 个真坑(iptables 改路把 SSH 断了 / TLS cert NotBefore 错乱 / CoreDNS 11 分钟 NotReady)
- Stage B —— NVIDIA driver + container toolkit + Device Plugin DaemonSet,CDI mode 不兼容降级 envvar mode
- Stage C —— vLLM 起 Qwen2.5-3B,验证中文 / 代码 / 数学 / 并发四路推理
GPU 节点:单卡 A800-SXM4-40GB(HGX 阉割版,CN 市场专供),公网 ***.109.239.32,CN supplier NAT 默认只放 SSH 一个端口。
Stage 0:跨 WAN 加主集群为什么不通
第一次尝试 kubeadm join k8s-api:16443 ... 这一步 ✅ 成功,节点出现在 kubectl get nodes 里,但 cilium-agent CrashLoopBackOff,整节点 NotReady。3 个原因叠加:
- Cilium 假设节点在同子网。IPCache / 节点发现都按局域网设计。跨 WAN 时 InternalIP 是公网 IP,但 NAT 后真实 src IP 是 carrier IP,Cilium 看 EndpointSlice 跟实际握手 IP 对不上。
- VXLAN over WAN 要 NAT 保持双向 UDP 8472 映射。CN supplier NAT 会话超时 60-120s,Pod 沉默两分钟回包就 drop。WireGuard 51871 同样。
- 业务上没 ROI。scheduler 把无 GPU 需求的 Pod 也调到跨 WAN 节点 → 控制面流量穿 WAN → etcd watcher timeout → 整集群抖。
合理姿势是 Cilium ClusterMesh 或 多集群联邦,但 ClusterMesh 也要双向 UDP,supplier NAT 仍然挡。最终决定:独立 k3s + 主集群通过 NodePort + SSH tunnel 调 vLLM HTTP,4 层 TCP 比 L2/L3 跨 WAN 容易 N 个量级。
教训:GPU 节点不要直接 join 远端 K8s 主集群。要么独立集群 + 业务层 HTTP 调用,要么 ClusterMesh + 双向公网 UDP(私有 IDC 几乎不可行)。
Stage A:k3s 单节点装机
k3s 是 Rancher 出品的精简 K8s:单二进制 ~70MB,内置 containerd / flannel / CoreDNS / traefik / local-path-provisioner / metrics-server,curl | sh 起整套。单 GPU 节点跑推理服务最合适 —— kubeadm 重得没必要,docker compose 又少了 K8s 调度语义。
A.1 装机
# 关 ufw / firewalld(k3s 自己改 iptables)
systemctl disable --now ufw 2>/dev/null || true
swapoff -a && sed -i '/swap/s/^/# /' /etc/fstab
# k3s install(关键:在 tmux 里跑,见下文坑)
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh \
| INSTALL_K3S_MIRROR=cn \
INSTALL_K3S_EXEC="server --disable=traefik --disable=servicelb" \
sh -
# 验证
systemctl status k3s
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# gpu-1 Ready control-plane,master 30s v1.35.5+k3s1
--disable=traefik / --disable=servicelb 是让 k3s 别启自带 ingress 和 ServiceLB,单节点用 NodePort 就够。k3s 用 flannel VXLAN,单节点没有跨节点 Pod 通信,flannel 在 lo 上走就完了。
A.2 真坑 #1:curl | sh 改 iptables 把 SSH 断了
第一次跑命令直接在 SSH 主会话里跑到一半,SSH 卡死 30 分钟才恢复。原因:
- k3s install 启动 k3s server,往
iptables大规模插KUBE-*链 - 改链中途 inbound SSH 包匹配规则的瞬间错位,sshd 拒收
- supplier NAT 看 TCP 长时间无包,把 NAT 表项清了
- iptables sync 完 + supplier NAT 重建一遍连接,5-30 分钟随机
规则:任何大规模改 iptables 的操作(k3s install / kubeadm init / cilium install),必须在 tmux / screen 里跑,不要 hold 在 SSH 主会话里。
A.3 真坑 #2:TLS cert NotBefore 写到未来 8 小时
装完 kubectl get nodes 报:
tls: certificate is not yet valid: current time 2026-05-26T17:44Z
is before 2026-05-26T16:38:13Z
NotBefore 比当前时间晚 1 小时。节点装机时时区是 UTC+8,但 chrony 没同步,系统时间被 systemd 当 UTC 处理写进 cert,cert 的 NotBefore 比真实时间提前 8 小时。等 chrony 同步后,cert 反而看起来在未来。
systemctl restart k3s 没用,cert 已经写盘。删 tls/ 目录也不够 —— k3s 的 bootstrap data 还存在 sqlite db/state.db,重启会用 sqlite 里的旧数据 regen 同样错误的 cert。唯一办法是完全 wipe:
systemctl stop k3s
rm -rf /var/lib/rancher/k3s/server # 整个 server state 全删
timedatectl set-timezone Asia/Shanghai
chronyc -a makestep # 硬跳同步,让时间对
systemctl start k3s # 重 bootstrap,新 cert
教训:装 k3s / kubeadm 之前先把时区和 NTP 搞好。单机也不能省。
A.4 真坑 #3:CoreDNS 11 分钟 NotReady
k3s 起好后 CoreDNS 一直 0/1,日志:
[INFO] plugin/ready: Still waiting on: "kubernetes"
CoreDNS 的 kubernetes plugin 通过 service kubernetes:443 (10.43.0.1) 连 apiserver listwatch。这条路要先经 kube-proxy 的 iptables DNAT 到 127.0.0.1:6443 —— k3s 启动初期这条规则还没 sync 进 iptables,CoreDNS readiness probe 一直 503。
正常 1-2 分钟自愈,这次 11 分钟还没好(怀疑跟 A.3 时间错乱叠加)。短平快绕过:业务 Pod 不用 cluster DNS,用公网 DNS:
spec:
template:
spec:
dnsPolicy: None
dnsConfig:
nameservers: [223.5.5.5, 8.8.8.8]
vLLM Pod 只需要解析 hf-mirror.com 拉模型,不需要 cluster.local 服务发现,这种 hack 完全 OK。生产应该挖根因(Day 1 Stage G 那条 9 跳解析链)。
Stage B:NVIDIA driver + container toolkit + Device Plugin
GPU 在 K8s 能被调度需要 3 层就位:
- kernel 层 NVIDIA driver ——
nvidia-smi在宿主机能跑 - runtime 层 nvidia-container-toolkit —— containerd 起容器时注入
/dev/nvidia*和 driver 库 - K8s 层 NVIDIA Device Plugin DaemonSet —— kubelet 知道节点有几张卡、能调度
nvidia.com/gpu: 1
B.1 driver + toolkit
供应商通常预装了 driver:
nvidia-smi # Driver Version: 535.xxx CUDA Version: 12.2 / A800-SXM4-40GB
装 nvidia-container-toolkit:
distribution=$(. /etc/os-release; echo $ID$VERSION_ID)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
| gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list \
| sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
> /etc/apt/sources.list.d/nvidia-container-toolkit.list
apt-get update && apt-get install -y nvidia-container-toolkit
k3s 的 containerd config 在 /var/lib/rancher/k3s/agent/etc/containerd/config.toml(不是常规位置)。装完 toolkit 后 systemctl restart k3s,k3s 检测到 nvidia-container-runtime 在 PATH 会自动注入 nvidia runtime:
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
runtime_type = "io.containerd.runc.v2"
[plugins."...nvidia.options"]
BinaryName = "/usr/bin/nvidia-container-runtime"
声明 RuntimeClass,让 Pod 显式选这个 runtime:
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata: {name: nvidia}
handler: nvidia
B.2 Device Plugin DaemonSet
Device Plugin 跑 nvidia-smi 把 GPU 数量 + UUID 上报给 kubelet,kubelet 把 nvidia.com/gpu: 1 写进 Node Allocatable:
kubectl create ns nvidia-device-plugin
kubectl apply -n nvidia-device-plugin -f \
https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.17.0/deployments/static/nvidia-device-plugin.yml
B.3 真坑:v0.17.0 默认 CDI mode 跟 k3s 内嵌 containerd 不兼容
Pod CrashLoop,日志:
identifier is not a valid UUID or index: "/var/run/nvidia-container-devices"
v0.17.0 默认走 CDI mode(Container Device Interface,2024 后的标准)。CDI 要求 containerd 启 CDI 支持 + toolkit 写 CDI spec (/etc/cdi/nvidia.yaml)。k3s 内嵌 containerd 装时没启 CDI,spec 也没生成,CDI mode 直接崩。
升级 k3s + 启 containerd CDI 是大工程,单节点没必要。降级到 envvar mode(用 NVIDIA_VISIBLE_DEVICES 环境变量传 GPU UUID):
spec:
template:
spec:
containers:
- name: nvidia-device-plugin-ctr
env:
- {name: DEVICE_LIST_STRATEGY, value: envvar}
- {name: PASS_DEVICE_SPECS, value: "false"}
- {name: DEVICE_ID_STRATEGY, value: uuid}
重启后 Pod Running,节点 Allocatable 出现 nvidia.com/gpu: "1"。跑个 nvidia-smi Pod 验证容器里能看到 A800:
kubectl run nvidia-smi --rm -it --restart=Never \
--image=nvidia/cuda:12.2.0-base-ubuntu22.04 \
--overrides='{"spec":{"runtimeClassName":"nvidia","containers":[{"name":"nvidia-smi","image":"nvidia/cuda:12.2.0-base-ubuntu22.04","command":["nvidia-smi"],"resources":{"limits":{"nvidia.com/gpu":1}}}]}}'
到这里 K8s 调度 GPU 链路全通。
Stage C:vLLM 跑 Qwen2.5-3B-Instruct
vLLM 是 UC Berkeley 出的 LLM 推理框架,核心是 PagedAttention(KV cache 按虚拟内存分页管理),相比 HF transformers 吞吐高 5-10×。vllm/vllm-openai image 直接暴露 OpenAI 兼容 HTTP 端点 —— 业务把 OpenAI client base_url 换一下就能调,零迁移成本。
C.1 Deployment
apiVersion: v1
kind: Namespace
metadata: {name: vllm}
---
apiVersion: apps/v1
kind: Deployment
metadata: {name: vllm-qwen, namespace: vllm}
spec:
replicas: 1
strategy:
type: Recreate # GPU 不能 share,RollingUpdate 会让新旧 Pod 抢卡
selector:
matchLabels: {app: vllm-qwen}
template:
metadata:
labels: {app: vllm-qwen}
spec:
runtimeClassName: nvidia # 关键:用 nvidia container runtime
dnsPolicy: None # 绕过 A.4 那个 CoreDNS 11min NotReady
dnsConfig:
nameservers: ["223.5.5.5", "8.8.8.8"]
containers:
- name: vllm
image: docker.m.daocloud.io/vllm/vllm-openai:v0.6.5
args: ["--model", "Qwen/Qwen2.5-3B-Instruct",
"--host", "0.0.0.0", "--port", "8000",
"--gpu-memory-utilization", "0.85",
"--max-model-len", "4096",
"--served-model-name", "qwen2.5-3b"]
env:
- {name: HF_ENDPOINT, value: "https://hf-mirror.com"}
ports: [{containerPort: 8000}]
resources:
limits: {nvidia.com/gpu: 1} # 触发 Device Plugin 调度
volumeMounts:
- {name: hf-cache, mountPath: /root/.cache/huggingface}
- {name: dshm, mountPath: /dev/shm}
readinessProbe:
httpGet: {path: /health, port: 8000}
initialDelaySeconds: 60
periodSeconds: 10
failureThreshold: 200 # 冷启动 ~13min,容忍 33min
volumes:
- {name: hf-cache, hostPath: {path: /opt/hf-cache, type: DirectoryOrCreate}}
- {name: dshm, emptyDir: {medium: Memory, sizeLimit: 8Gi}}
---
apiVersion: v1
kind: Service
metadata: {name: vllm-qwen, namespace: vllm}
spec:
type: NodePort
selector: {app: vllm-qwen}
ports: [{port: 8000, targetPort: 8000, nodePort: 30800}]
几个关键配置:
strategy: Recreate:默认RollingUpdate先起新 Pod 再杀旧,单 GPU 节点新 Pod 会卡Pending(GPU 被旧 Pod 占着)。单卡必须 Recreate。gpu-memory-utilization=0.85:vLLM 启动按比例预分配显存做 KV cache pool。剩 15% 给 CUDA workspace + 临时张量。0.95 容易长上下文 OOM。max-model-len=4096:vLLM 算 KV cache 上限的依据。Qwen2.5-3B 原生 32k,但开 32k KV cache pool 占用线性上升,4k 够多数场景。hf-cachehostPath:模型 6GB+,Pod 重建不重下。dshm:vLLM worker process 通过/dev/shm共享 tensor(PyTorch IPC 默认用 shm)。容器默认/dev/shm只 64MB,跑模型秒爆,挂emptyDir.medium=Memory给 8GB。readinessProbe.failureThreshold=200×periodSeconds=10= 33 分钟容忍,避免 K8s 在模型加载期把 Pod kill 重启。
C.2 启动耗时(cold start)
Pod schedule + Image pull (vllm-openai:v0.6.5, 10GB) → 7 min
模型下载 (Qwen2.5-3B from hf-mirror, 6GB) → 5 min
vLLM 加载 GPU + 编译 CUDA kernel → 1 min
────────────────────────────────────────────────
total cold start ~13 min
第二次启动 image cache + 模型在 /opt/hf-cache,冷启动降到 ~1 分钟(只剩 CUDA kernel 编译)。
C.3 推理验证
curl http://localhost:30800/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "qwen2.5-3b",
"messages": [{"role": "user", "content": "用一句话介绍 Kubernetes"}],
"max_tokens": 100}'
# {"choices":[{"message":{"content":"Kubernetes 是一个开源的容器编排系统..."}}],
# "usage":{"prompt_tokens":34,"completion_tokens":22,"total_tokens":56}}
跑了 4 类测试:
| 场景 | 输入 | 输出 |
|---|---|---|
| 中文问答 | 「用一句话介绍 Kubernetes」 | 1 行准确定义,56 tokens |
| 代码生成 | 「用 Go 写 HTTP server 返回 hello world」 | 完整 Go 代码 + 注释,300 tokens 上限打满 |
| 数学推理 | 「125 × 36 = ? 给步骤」 | 125 × 30 + 125 × 6 = 3750 + 750 = 4500,分步 + LaTeX |
| 并发吞吐 | for i in 1..5; do curl ... &; done | 5 并发,总 430 tokens,5 秒内返回 |
并发 5 时 GPU util 从 36% 飙到 85%,显存稳定 33.7G / 40.9G。这就是 PagedAttention 的吞吐优势 —— 多 request KV cache 在同一 pool 分页复用,不互相阻塞。
C.4 GPU 资源占用
| 项 | 数值 |
|---|---|
| GPU / 总显存 | A800-SXM4-40GB / 40960 MiB |
| vLLM 实测占用 (target 0.85) | 33.7 GB |
| 模型 weight (FP16) | 6.2 GB |
| KV cache pool + activation | ~28 GB(动态) |
| GPU util 单请求 / 5 并发 | 36% / 85% |
A800-40G 跑 3B 模型「卡有富裕」—— 这卡装 14B Q8 或 70B Q4 量化是更合理算力配比。3B 是为快速验证链路。
C.5 主集群跨 WAN 调用
GPU 节点 NodePort 30800 在公网,但 supplier NAT 默认只开 SSH。两种姿势:
# A. supplier 后台手动开端口(最稳,生产姿势)
# B. SSH 隧道(不依赖 supplier)
ssh -L 30800:localhost:30800 -N gpu-1 &
curl http://localhost:30800/v1/chat/completions ... # 主集群任意机器
生产用 cloudflare tunnel / frp / wireguard 比裸 SSH tunnel 稳。Day 9 切到这个。
最终架构
主集群 (5 节点, 10.0.24.0/24) — Cilium / Prom / Loki / Longhorn / ArgoCD
↓ 公网 HTTP (NodePort + SSH tunnel / supplier NAT)
GPU 节点 (k3s 单节点, 公网 ***.109.239.32)
├─ k3s v1.35 (containerd / flannel,无 traefik/servicelb)
├─ nvidia-driver 535 + nvidia-container-toolkit + RuntimeClass nvidia
├─ NVIDIA Device Plugin DaemonSet (envvar mode)
└─ vLLM Deployment + NodePort 30800
└─ Qwen2.5-3B-Instruct on A800-SXM4-40GB
/v1/chat/completions /v1/completions /v1/models /health
典型坑速查
| 现象 | 根因 | 修复 |
|---|---|---|
curl install.sh | sh 跑到一半 SSH 断 30 分钟 | k3s install 大规模改 iptables 导致 inbound 中断 + supplier NAT 表项过期 | tmux / screen 里跑,不要在 SSH 主会话 hold |
tls: certificate is not yet valid, current time is before NotBefore | 装机时时区错乱导致 cert 写未来时间 | rm -rf /var/lib/rancher/k3s/server 全 wipe 再 restart |
CoreDNS 长期 0/1,日志 Plugins not ready: kubernetes | service routing iptables 还没 sync,CoreDNS 连不上 apiserver | 业务 Pod 用 dnsPolicy: None + 公网 DNS 绕过 |
Device Plugin 报 identifier is not a valid UUID: /var/run/nvidia-container-devices | v0.17.0 默认 CDI mode,但 k3s 内嵌 containerd 没启 CDI | DaemonSet env 加 DEVICE_LIST_STRATEGY=envvar 降级 |
跨 WAN 节点 NodePort Connection refused | supplier NAT 默认只开 SSH 端口 | 后台开端口 / SSH tunnel / cloudflare tunnel |
vLLM 启动时 RuntimeError: shm size too small | 容器默认 /dev/shm 只 64MB,PyTorch IPC 用 shm 传 tensor 爆 | 挂 emptyDir.medium=Memory, sizeLimit=8Gi 到 /dev/shm |
单 GPU 节点 RollingUpdate 卡在新 Pod Pending | 旧 Pod 占着 GPU,新 Pod 申请 nvidia.com/gpu: 1 调不上 | Deployment strategy.type=Recreate |
面试常见题
Q1:K8s Device Plugin 是怎么工作的?
K8s 原生 resource 只懂 CPU / memory / ephemeral-storage,对 GPU / FPGA / RDMA 这类异构硬件无知。Device Plugin 是扩展机制:第三方用 DaemonSet 把异构硬件接进调度器。
工作流(kubelet ↔ Device Plugin gRPC,socket 在 /var/lib/kubelet/device-plugins/):
- Register —— Device Plugin 启动通过
kubelet.sock把自己注册给 kubelet,声明resourceName=nvidia.com/gpu - ListAndWatch —— kubelet 持续拉 GPU 列表(UUID + healthy),写进 Node Allocatable
- Schedule —— Pod 写
limits.nvidia.com/gpu: 1,scheduler 找到有可用 GPU 的节点 - Allocate —— kubelet 起 Pod 前调 Device Plugin 的
Allocate(UUID),返回应注入的 env (NVIDIA_VISIBLE_DEVICES=GPU-xxx) + 设备 mount - Runtime 注入 —— containerd + nvidia-container-toolkit 看到 env / mount 把 driver 库和设备文件注入容器
为什么需要:异构硬件不像 CPU 可分时复用、不像 memory 可弹性切片,需要 「whole-device assign」语义,Device Plugin 就是这个 API。
深问:CDI vs envvar 区别? envvar 是老接口,Device Plugin 直接给 runtime 传环境变量;CDI 是 2024+ 标准,Device Plugin 写 spec (/etc/cdi/*.yaml) 由 runtime 读,decouple 更好但要 containerd / runtime 同时支持。
Q2:vLLM 的 PagedAttention 解决了什么?
传统框架(HF transformers、TGI v0)的 KV cache 是 连续显存分配:一个 request 进来按 [batch, num_heads, max_seq_len, head_dim] 分配整块。问题:
- 不同 request
seq_len差异大,按max_seq_len分配浪费严重(100 tokens 的 request 占 4096 tokens 空间) - 显存碎片化,大并发时即使总空闲够也分配不出连续大块
PagedAttention 类比 OS 虚拟内存:KV cache 切成固定 block (e.g. 16 tokens/block),用 block table 维护 request → block list 的映射。新 request 按需分配 block,不要求连续。结果:
- 显存利用率 4×,实测吞吐 5-10×
- prefix sharing —— 同 system message / few-shot 前缀的 block 物理共享
- dynamic batching —— 不同长度 request 同时跑,运行中可加入新 request
同期对标:SGLang (Berkeley)、TensorRT-LLM (NVIDIA)、TGI (HF),核心思路一致。
Q3:OpenAI-compatible API 端点的工程意义?
/v1/chat/completions 是业界 LLM HTTP 调用的事实标准。意义:
- 客户端零迁移 —— 业务用
openai-python,把base_url从api.openai.com换成 vLLM endpoint,代码一行不动 - 生态复用 —— LangChain / LlamaIndex / Continue.dev / AutoGen 全支持 OpenAI 接口,自有模型自动接入
- 运维标准化 —— rate limit / API key / streaming / function calling 有公开规范,不用从零设计
- A/B 测试 —— 从 GPT-4 切到 Qwen / DeepSeek 只换 base_url + key,prompt 不动
反过来私有 API 协议(gRPC / 私有 JSON)要写 adapter,LangChain 要 custom integration。除非性能极端要求(高频低延迟用 Triton + gRPC),OpenAI-compatible 是默认。
Q4:为什么用 k3s 不用 kubeadm?什么时候反过来?
k3s 适合本场景 4 个原因:
- 单二进制 70MB,装机 1 分钟。kubeadm 要 10+ 二进制 + image,单节点 overhead 太重
- 内嵌 containerd / flannel / CoreDNS,不用 Day 1 那套 containerd config + Cilium install + CoreDNS 编排
- 单节点开箱即用,
controlPlaneEndpoint/ etcd HA / per-node HAProxy 这些 kubeadm HA 必须的东西 k3s 自动跳过 - sqlite 替代 etcd,单节点不需要 raft 多数派,sqlite 写盘开销小内存减半
反过来用 kubeadm:多节点 HA / 生产 100+ 节点 / 跨云联邦 / 企业 CIS 合规。经验:单节点推理 / 边缘 / IoT / 开发用 k3s,生产数据面用 kubeadm。
Q5:主集群跨 WAN 调 GPU 节点上 vLLM 的正确姿势?
按运维复杂度递增:
- NodePort + 公网 —— 最简单,要 supplier NAT 开端口,裸 HTTP 没鉴权没 TLS,生产不行
- SSH tunnel —— demo / 调试 OK,会话掉就断
- cloudflare tunnel / frp / ngrok —— GPU 节点向公网建出站连接,不要 inbound 端口,自带 TLS
- WireGuard / Tailscale —— 主集群和 GPU 节点建 L3 隧道,应用层看就是同子网。tailscale 自动 NAT 穿透
- Service Mesh 多集群 (Istio / Linkerd) —— ServiceEntry 跨集群指向 vLLM,mTLS + 重试 / 熔断
- Cilium ClusterMesh —— K8s 层多集群,GPU 集群 Service 在主集群可见。要双向公网 UDP,私有 IDC 常不可行
工程实践 3 (cloudflare tunnel) + 业务侧 OpenAI client 性价比最高:免维护、自带 TLS、不依赖 supplier 端口配置。Day 9 切到这个。
下一步
Day 8 主线收尾,GPU 节点能从主集群跨 WAN 调 vLLM OpenAI 端点。Day 9 候选:
- SSH tunnel → cloudflare tunnel,加 API key 鉴权 + TLS
- vLLM 上 14B 模型 —— A800-40G 跑 Qwen2.5-14B 或 Qwen2.5-Coder-7B 把卡吃满
- Triton Inference Server 对比 —— NVIDIA 官方推理服务器,多模型 + gRPC,跟 vLLM 横向对照
- 业务接入 demo —— 主集群 ArgoCD 部署 chat UI,调 GPU 集群 vLLM,跑通跨集群 LLM 调用链路