AI Infra 训练营
总览
  • 总览
  • 完整安装
  • 核心 K8s
  • Cilium 网络
  • Longhorn 存储
  • 监控日志
  • CI / GitOps
  • 安全准入
  • CI/CD 实战(MySQL+Go+Vue)
  • HPA/Ingress/Hubble 实战
  • 面试速查 + 真实踩坑
  • Day 0 · 新手接管 Runbook
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
命令手册
HiHuo 主站
GitHub
总览
  • 总览
  • 完整安装
  • 核心 K8s
  • Cilium 网络
  • Longhorn 存储
  • 监控日志
  • CI / GitOps
  • 安全准入
  • CI/CD 实战(MySQL+Go+Vue)
  • HPA/Ingress/Hubble 实战
  • 面试速查 + 真实踩坑
  • Day 0 · 新手接管 Runbook
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
命令手册
HiHuo 主站
GitHub
  • Day 0 · 环境与硬件

    • Day 0 新手现场接管 Runbook:先看懂,再动手
    • Day 0:5 节点裸 Ubuntu → K8s 装机基线
  • Week 1:K8s 内核 + 周边基础设施

    • Day 1:3 CP HA 集群 + CNI 选型 + DNS 调优
    • Day 2:控制面 deep dive + etcd 内核 + chaos drill
    • Day 3:CRD + Operator (kubebuilder 从 0 写)
    • Day 4:Longhorn 存储 + Cilium 二探(Hubble / NetworkPolicy / L7)
    • Day 5:PVC 在线扩容 + K8s 安全基线(RBAC / PSA / Secret 加密 / Kyverno)
    • Day 6:调度策略 + Prometheus / Loki 观测栈
    • Day 7:Harbor 私有镜像 + ArgoCD GitOps + Cilium WireGuard
  • Week 2:制品 + GitOps + AI Infra + 综合

    • Day 8:k3s 单节点 + NVIDIA Device Plugin + vLLM 跑 Qwen2.5-3B
    • Day 8(attempt 1):跨 WAN GPU 加入主集群(失败复盘)
    • Day 8:AlertManager 真接入 + PrometheusRule 实战
    • Day 8:集群内 CI 闭环 — Gitea + Jenkins + Kaniko
    • Day 9:Triton 多框架推理 + DCGM 跨集群可观测 + vLLM 实测
    • Day 10:MIG 硬切片 + AWQ 量化 + HPA Custom Metrics
    • Day 11:AI 业务端到端 —— chainlit + GitOps + 跨 WAN vLLM
    • Day 12:灾难恢复 + 生产事故注入
    • Day 13:LLM Operator + 多集群联邦 + Ambient Mesh + RAG
    • Day 14:CKA / CKS 真题演练 + 14 天技术栈横向汇总

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 个原因叠加:

  1. Cilium 假设节点在同子网。IPCache / 节点发现都按局域网设计。跨 WAN 时 InternalIP 是公网 IP,但 NAT 后真实 src IP 是 carrier IP,Cilium 看 EndpointSlice 跟实际握手 IP 对不上。
  2. VXLAN over WAN 要 NAT 保持双向 UDP 8472 映射。CN supplier NAT 会话超时 60-120s,Pod 沉默两分钟回包就 drop。WireGuard 51871 同样。
  3. 业务上没 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 层就位:

  1. kernel 层 NVIDIA driver —— nvidia-smi 在宿主机能跑
  2. runtime 层 nvidia-container-toolkit —— containerd 起容器时注入 /dev/nvidia* 和 driver 库
  3. 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-cache hostPath:模型 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 ... &; done5 并发,总 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: kubernetesservice routing iptables 还没 sync,CoreDNS 连不上 apiserver业务 Pod 用 dnsPolicy: None + 公网 DNS 绕过
Device Plugin 报 identifier is not a valid UUID: /var/run/nvidia-container-devicesv0.17.0 默认 CDI mode,但 k3s 内嵌 containerd 没启 CDIDaemonSet env 加 DEVICE_LIST_STRATEGY=envvar 降级
跨 WAN 节点 NodePort Connection refusedsupplier 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/):

  1. Register —— Device Plugin 启动通过 kubelet.sock 把自己注册给 kubelet,声明 resourceName=nvidia.com/gpu
  2. ListAndWatch —— kubelet 持续拉 GPU 列表(UUID + healthy),写进 Node Allocatable
  3. Schedule —— Pod 写 limits.nvidia.com/gpu: 1,scheduler 找到有可用 GPU 的节点
  4. Allocate —— kubelet 起 Pod 前调 Device Plugin 的 Allocate(UUID),返回应注入的 env (NVIDIA_VISIBLE_DEVICES=GPU-xxx) + 设备 mount
  5. 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 调用的事实标准。意义:

  1. 客户端零迁移 —— 业务用 openai-python,把 base_url 从 api.openai.com 换成 vLLM endpoint,代码一行不动
  2. 生态复用 —— LangChain / LlamaIndex / Continue.dev / AutoGen 全支持 OpenAI 接口,自有模型自动接入
  3. 运维标准化 —— rate limit / API key / streaming / function calling 有公开规范,不用从零设计
  4. 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 个原因:

  1. 单二进制 70MB,装机 1 分钟。kubeadm 要 10+ 二进制 + image,单节点 overhead 太重
  2. 内嵌 containerd / flannel / CoreDNS,不用 Day 1 那套 containerd config + Cilium install + CoreDNS 编排
  3. 单节点开箱即用,controlPlaneEndpoint / etcd HA / per-node HAProxy 这些 kubeadm HA 必须的东西 k3s 自动跳过
  4. sqlite 替代 etcd,单节点不需要 raft 多数派,sqlite 写盘开销小内存减半

反过来用 kubeadm:多节点 HA / 生产 100+ 节点 / 跨云联邦 / 企业 CIS 合规。经验:单节点推理 / 边缘 / IoT / 开发用 k3s,生产数据面用 kubeadm。

Q5:主集群跨 WAN 调 GPU 节点上 vLLM 的正确姿势?

按运维复杂度递增:

  1. NodePort + 公网 —— 最简单,要 supplier NAT 开端口,裸 HTTP 没鉴权没 TLS,生产不行
  2. SSH tunnel —— demo / 调试 OK,会话掉就断
  3. cloudflare tunnel / frp / ngrok —— GPU 节点向公网建出站连接,不要 inbound 端口,自带 TLS
  4. WireGuard / Tailscale —— 主集群和 GPU 节点建 L3 隧道,应用层看就是同子网。tailscale 自动 NAT 穿透
  5. Service Mesh 多集群 (Istio / Linkerd) —— ServiceEntry 跨集群指向 vLLM,mTLS + 重试 / 熔断
  6. 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 调用链路
在 GitHub 上编辑此页
Next
Day 8(attempt 1):跨 WAN GPU 加入主集群(失败复盘)