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 13:LLM Operator + 多集群联邦 + Ambient Mesh + RAG

4 个面试高频主题一日过:从 Day 3 SimpleApp 演进出真正的 LLM Operator,对比 Karmada 与 Cilium ClusterMesh 两种多集群方案,把 Istio Ambient 跟 sidecar / Cilium SM 三方拉齐,最后把 Day 11 的 chat-ui 扩成带向量检索的 RAG。

按「主题 + 命令 + 为什么 + 真坑」四段叙事走,每个主题都给一个可以直接 demo 的最小切片,剩下的是工业增强方向。


1. LLM Operator:从 SimpleApp 到 LLMServiceCRD

Day 3 写过一个 SimpleApp Operator,只管理一个 Deployment。生产里 LLM 推理服务远不只是 Deployment —— 它要 GPU、要 ConfigMap 传 model 名、要 Service 暴露 OpenAI 兼容 endpoint、要 HPA 基于 vllm:num_requests_waiting 扩缩容。把这些做成一个 LLMService CRD,是 PaaS for AI 团队的标准抽象。

1.1 CRD 设计

声明式 API 的第一步是把「用户该填什么」想清楚。LLM 推理的核心参数 5 个:model name、quantization、max-model-len、GPU mem util、replicas。

apiVersion: apps.bootcamp.local/v1
kind: LLMService
metadata: {name: qwen-3b}
spec:
  model: Qwen/Qwen2.5-3B-Instruct
  quantization: ""                  # "" | awq | gptq | fp8
  maxModelLen: 2048
  gpuMemoryUtilization: "0.85"
  replicas: 1
  hfEndpoint: https://hf-mirror.com
  autoscaling:
    minReplicas: 1
    maxReplicas: 3
    waitingThreshold: 5             # vllm:num_requests_waiting > 5 触发扩
status:
  phase: Serving                    # Pending | Loading | Serving | Scaling | Failed
  endpoint: http://qwen-3b.default.svc.cluster.local:8000/v1
  readyReplicas: 1

types.go 里用 kubebuilder marker 把校验做完,不要等到 reconcile 里再判:

type LLMServiceSpec struct {
    // +kubebuilder:validation:MinLength=3
    Model string `json:"model"`

    // +kubebuilder:validation:Enum=;awq;gptq;fp8
    Quantization string `json:"quantization,omitempty"`

    // +kubebuilder:default=2048
    // +kubebuilder:validation:Minimum=512
    MaxModelLen int32 `json:"maxModelLen,omitempty"`

    // +kubebuilder:default="0.85"
    GPUMemoryUtilization string `json:"gpuMemoryUtilization,omitempty"`

    // +kubebuilder:default=1
    Replicas int32 `json:"replicas,omitempty"`

    // +kubebuilder:default="docker.m.daocloud.io/vllm/vllm-openai:v0.6.5"
    Image string `json:"image,omitempty"`

    Autoscaling *AutoscalingSpec `json:"autoscaling,omitempty"`
}

加 printcolumn 让 kubectl get llmservices 一眼看到状态:

// +kubebuilder:printcolumn:name=Model,type=string,JSONPath=`.spec.model`
// +kubebuilder:printcolumn:name=Replicas,type=integer,JSONPath=`.spec.replicas`
// +kubebuilder:printcolumn:name=Ready,type=integer,JSONPath=`.status.readyReplicas`
// +kubebuilder:printcolumn:name=Phase,type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name=Endpoint,type=string,JSONPath=`.status.endpoint`
type LLMService struct { ... }

1.2 Reconcile 流程

Reconciler 做四件事,每一步幂等,每一步加 owner reference:

LLMService 创建
    ↓
LLMServiceReconciler.Reconcile
  ├─ 加 finalizer(防 K8s 直接 GC,HF cache 要清理)
  ├─ reconcileDeployment(vLLM + GPU + HF mirror env)
  ├─ reconcileService(ClusterIP :8000)
  ├─ reconcileHPA(External Metric: vllm_num_requests_waiting)
  └─ updateStatus(Endpoint / Phase / ReadyReplicas 回写)

Deployment 模板里几个非琐碎选择:

  • Strategy: Recreate —— GPU 不能 share,RollingUpdate 时新旧 Pod 都要 GPU 直接卡住,必须先停旧再起新
  • RuntimeClassName: nvidia —— 跟 nvidia-device-plugin 装的 runtime 对接
  • HF_ENDPOINT env 走 hf-mirror 国内加速 —— 不然冷启拉模型卡死
  • hf-cache hostPath + dshm emptyDir(memory, 4Gi) —— 前者跨 Pod 复用模型缓存,后者给 vLLM 张量并行用共享内存
  • nvidia.com/gpu: 1 resources limit —— scheduler 看这个调度

HPA 用 External Metric 而不是 CPU —— LLM 推理 CPU 永远拉不满,唯一靠谱的扩缩信号是 vLLM 自己 export 的 vllm:num_requests_waiting:

Metrics: []autoscalingv2.MetricSpec{{
    Type: autoscalingv2.ExternalMetricSourceType,
    External: &autoscalingv2.ExternalMetricSource{
        Metric: autoscalingv2.MetricIdentifier{
            Name: "vllm_num_requests_waiting",
            Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"deployment": llm.Name}},
        },
        Target: autoscalingv2.MetricTarget{Type: autoscalingv2.AverageValueMetricType, AverageValue: &avg},
    },
}}

1.3 验证

make generate && make manifests && make build   # bin/manager ~73 MB

kubectl apply -f config/crd/bases/apps.bootcamp.local_llmservices.yaml

cat <<EOF | kubectl apply -f -
apiVersion: apps.bootcamp.local/v1
kind: LLMService
metadata: {name: qwen-3b}
spec:
  model: Qwen/Qwen2.5-3B-Instruct
  replicas: 1
EOF

kubectl get llmservices
# NAME     MODEL                       QUANT  REPLICAS  READY  PHASE     ENDPOINT  AGE
# qwen-3b  Qwen/Qwen2.5-3B-Instruct           1                 Pending             3s

# defaults 自动填
kubectl get llmservice qwen-3b -o yaml | grep -E "image:|maxModelLen:|hfEndpoint:"
#   hfEndpoint: https://hf-mirror.com
#   image: docker.m.daocloud.io/vllm/vllm-openai:v0.6.5
#   maxModelLen: 2048

# enum 校验生效
kubectl apply -f bad.yaml  # quantization: invalid_value
# Error: Unsupported value "invalid_value": supported values "awq", "gptq", "fp8"

1.4 真坑:defaults / enum 一定要 marker 写在 spec 字段上

+kubebuilder:default= 必须紧贴字段,不能放注释段开头。错位的话 make manifests 生成的 CRD 里 schema 里没 default,跑起来 apiserver 不会自动填,反而 reconcile 报 model is empty。kubebuilder 不会 warning,只有跑起来才发现。

另一个常踩:Enum= 列表里空串要显式列出(Enum=;awq;gptq;fp8),否则 quantization: "" 会被拒。

1.5 工业演进路径

v1(本节)= CRD + Deployment + Service + HPA。往后三步:v2 加 Admission Webhook 做跨字段校验(GPU 资源够 / HF mirror 可达);v3 加 Conversion Webhook 走 v1beta1 → v1 schema 演进;v4 跟 Karmada 联动让 LLMService 自动调度到 GPU cluster。


2. 多集群联邦:Karmada vs Cilium ClusterMesh

单集群跑到 100 节点后就会撞上跨集群需求:跨可用区灾备、CPU 集群 + GPU 集群分工、prod / staging 共一套 control plane。两种主流方案的抽象层完全不同,选错会硬塞半成品。

2.1 Karmada:K8s 资源级联邦

Karmada 自己跑一套独立的 apiserver + etcd + controller + scheduler 当 federation control plane,member cluster 上跑 karmada-agent 反连过来。用户把资源提交到 karmada-apiserver,scheduler 决定下发到哪些 member。

核心 CRD 四个:

  • ResourceTemplate —— 就是普通 Deployment / Service,提交到 Karmada apiserver 而不是 member cluster
  • PropagationPolicy —— 这个资源该部署到哪些 member、按什么权重切 replicas
  • OverridePolicy —— 不同集群不同 override(image registry、replicas、env)
  • MultiClusterIngress —— 跨集群 ingress

部署 chat-ui 到 GPU cluster 的最小 yaml:

---
apiVersion: apps/v1
kind: Deployment
metadata: {name: chat-ui}
spec:
  replicas: 2
  # ...
---
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata: {name: chat-ui-prop}
spec:
  resourceSelectors:
  - apiVersion: apps/v1
    kind: Deployment
    name: chat-ui
  placement:
    clusterAffinity:
      clusterNames: [gpu-cluster]
    replicaScheduling:
      replicaSchedulingType: Divided
      replicaDivisionPreference: Weighted
      weightPreference:
        staticWeightList:
        - targetCluster: {clusterNames: [gpu-cluster]}
          weight: 2
        - targetCluster: {clusterNames: [cpu-cluster]}
          weight: 0

2.2 真坑:Karmada helm install 拉不到 cfssl

按官方 helm 装:

helm install karmada karmada-charts/karmada \
  --namespace karmada-system --create-namespace \
  --set installMode=host

pre-install Job 拉 docker.io/cfssl/cfssl:latest(用来给 karmada-apiserver 签证),跨 WAN 几分钟不动,最终 helm install timeout。再加上 Kyverno 卡 image: latest PolicyViolation,整个流程崩溃。

生产可行的修复路径(按成本从低到高):

  1. 预拉镜像:节点上 ctr -n k8s.io image pull cfssl + karmada-* 整套,再 helm install
  2. 镜像替换:--set apiServer.image=harbor.local/karmada/... 全部走内网 Harbor
  3. 延长 timeout:--timeout 30m,慢但能过
  4. Karmadactl bootstrap:karmadactl init 替代 helm,更可控

学习场景没内网 Harbor,留到 yaml 设计层面打通,装机部分在企业环境再做。

2.3 Cilium ClusterMesh:网络层联邦

Cilium 不做资源级编排,只在网络层把多个集群粘起来 —— 一个 Service 加个 annotation 就能跨集群路由。前提是两个集群都装 Cilium。

# 1. 两个集群分别 enable
cilium clustermesh enable --service-type NodePort --context main-cluster
cilium clustermesh enable --context gpu-cluster

# 2. 双向 connect(互换 CA + endpoint)
cilium clustermesh connect --context main-cluster --destination-context gpu-cluster

# 3. 给 Service 加全局 annotation
kubectl annotate service vllm-3b -n vllm \
  service.cilium.io/global=true \
  service.cilium.io/affinity=remote

# 4. main 集群里的 Pod 调 vllm-3b.vllm.svc → eBPF 自动跨集群路由到 GPU cluster

2.4 真坑:ClusterMesh 要求 PodCIDR 不重叠

两个集群的 Pod CIDR 必须不同。Day 1 的 K8s 用 10.244.0.0/16,新 K3s 默认是 10.42.0.0/16,刚好错开 OK。但很多团队两个集群都直接用 kubeadm init 默认 10.244.0.0/16,clustermesh enable 会直接报错拒绝 connect。

装第二个集群前先确认:

kubectl get cm -n kube-system kubeadm-config -o yaml | grep podSubnet
# 或者直接看任一个 node:
kubectl get node <n> -o jsonpath='{.spec.podCIDR}'

冲突的话只能重装第二个集群(改 --pod-network-cidr),路由表没法热改。

2.5 两个方案的根本差异

维度KarmadaCilium ClusterMesh
抽象层K8s 资源级网络层
service discoveryMultiClusterIngress / ServiceImportglobal service annotation
数据面普通 K8s service 调用(依赖 ingress / VPN 跨集群可达)eBPF 跨集群直连
学习曲线高(8 个 CRD)中(2 个 annotation)
CNI 绑定不绑必须 Cilium
集群差异容忍高(OverridePolicy)低(假设各集群同 cluster.local)

选型:

  • 只要跨集群 service discovery → Cilium ClusterMesh
  • 需要 placement 策略 + replicas 切分 + per-cluster override → Karmada
  • 多集群 GitOps 场景:Cilium ClusterMesh 做网络 + ArgoCD ApplicationSet 做编排,KISS

3. Istio Ambient vs Cilium Service Mesh

Day 4 装的是 Cilium SM,这一节把 Istio Ambient(2024 GA)和经典 sidecar mode 也拉进来,三方对比。

3.1 Sidecar mesh 的「原罪」

经典 Istio 1.0-1.20:每个 Pod 多塞一个 Envoy sidecar。

  • 资源开销:Envoy ~100MB 内存 × Pod 数。10K Pod 集群直接吃 1TB 内存
  • 启动顺序 race:应用容器起好了 sidecar 没好 → 流量 drop
  • 升级困难:Istio 升级要全集群 Pod restart
  • Pod 加入 mesh 要重启(注入 sidecar)

Linkerd 也是 sidecar 模式,相同问题。

3.2 Istio Ambient:拆分 sidecar

把 sidecar 拆成两层 DaemonSet:

  • ztunnel(每节点一个)—— L4 proxy,接管 Pod 出入流量、自动 mTLS、HBONE 隧道传 L4
  • waypoint(per-namespace 可选)—— L7 流量管理(retry / route / 鉴权),只在需要 L7 能力的 namespace 启

L4 always-on,L7 opt-in。ztunnel 用 Rust 写,比 Envoy 内存低一个数量级。代价:L7 流量多一跳(ztunnel → waypoint → Pod),且 2024 才 GA 生态不熟。

3.3 Cilium Service Mesh

Day 4 已经装好:cilium-agent 把 eBPF program 装进内核 hook,Pod 流量在 kernel level 拦截,L7 才 redirect 到 cilium-envoy DaemonSet。完全无 sidecar,L4 零用户态切换。

mTLS 不是 Pod identity 级(要靠 SPIRE 单独配),但 Day 7 启的节点级 WireGuard 加密对大部分场景够用。

3.4 三方对比

维度Istio sidecarIstio AmbientCilium SM
L4 数据面Envoy per Podztunnel DaemonSeteBPF in kernel
L7 数据面Envoy per Podwaypoint per nscilium-envoy DS
mTLSPod 级 identityPod 级 identity节点级 WireGuard
L7 routingVirtualService / HTTPRouteHTTPRouteCiliumNetworkPolicy(弱)
资源开销高(sidecar × N)中(DS × node)极低(eBPF)
Pod 启动顺序race无 sidecar 无 race无 race
加入 mesh必须重启 Pod不需要不需要
CNI 绑定不绑不绑必须 Cilium
生态成熟5+ 年2024 GA4+ 年

3.5 选型决策

已有 Cilium CNI?
├─ 是 → Cilium SM(零成本,Day 4 已 demo)
└─ 否 ↓

需要 Pod 级 mTLS + SPIFFE identity?
├─ 是,新部署 → Istio Ambient
├─ 是,已有 Istio sidecar → 渐进迁移到 Ambient
└─ 否(节点级 WireGuard 够) → 考虑装 Cilium 重做

Pod 数 > 1000?
├─ 是 → Ambient 或 Cilium SM(sidecar 内存吃太多)
└─ 否 → sidecar 也可(生态最熟)

生产新集群直接 Cilium 全栈(CNI + SM + LB + ClusterMesh),简单 + 资源效率。本笔记 Day 4 装 L7 NetworkPolicy(GET 允许 / POST 拒)+ Hubble 流量观察 + Day 7 WireGuard 透明加密已经完整 demo。


4. RAG 业务扩展:ChromaDB + Embedding + vLLM

Day 11 chat-ui 直接打 vLLM,模型答的全靠预训练知识。生产 RAG 流程是 retrieve → augment → generate:先用 embedding 把问题转向量,去向量库捞 top-k 相关文档,把文档拼进 prompt 再丢 LLM。这样答案可以引用私有知识,且能标注 source。

4.1 RAG 链路

5 步:用户问题 → embedding 模型(MiniLM 117MB CPU 跑)转 384 维向量 → ChromaDB 检索 top-3 相似文档 → 把文档拼进 prompt(Context: [doc1][doc2][doc3]\n Question:...\n Answer:)→ vLLM 流式生成带 source 标注的答案。

4.2 Chainlit 加 RAG

在 Day 11 chat-ui 基础上:

"""RAG-augmented chainlit chat。
向量库: ChromaDB(持久化到 PVC)
Embedding: sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2(117MB CPU)
"""
import os, chainlit as cl
from openai import AsyncOpenAI
import chromadb
from chromadb.utils import embedding_functions

# 1. 初始化向量库 + embedding model
embed_fn = embedding_functions.SentenceTransformerEmbeddingFunction(
    model_name="paraphrase-multilingual-MiniLM-L12-v2",
    device="cpu",
)
chroma_client = chromadb.PersistentClient(path="/data/chroma")
collection = chroma_client.get_or_create_collection(
    name="bootcamp_docs",
    embedding_function=embed_fn,
)

# 2. 启动时索引 sample docs(生产从 Wiki / Gitea 抓)
SAMPLE_DOCS = [
    {"id": "k8s-pod-1",       "text": "Pod 是 K8s 最小调度单元,包含 1+ 容器共享 network namespace 和 volume。"},
    {"id": "k8s-container-1", "text": "Container 是 Pod 内的运行单位,有自己 cgroup 和 PID namespace。"},
    {"id": "k8s-deploy-1",    "text": "Deployment 通过 ReplicaSet 管理 Pod 副本,支持滚动升级。"},
]
collection.upsert(
    ids=[d["id"] for d in SAMPLE_DOCS],
    documents=[d["text"] for d in SAMPLE_DOCS],
)

# 3. vLLM client
client = AsyncOpenAI(base_url=os.getenv("VLLM_URL"), api_key="not-needed")

# 4. on_message: retrieve → augment → generate
@cl.on_message
async def on_message(msg: cl.Message):
    results = collection.query(query_texts=[msg.content], n_results=3)
    docs = results["documents"][0]
    sources = results["ids"][0]

    context = "\n\n".join(f"[{i+1}] {d}" for i, d in enumerate(docs))
    augmented = f"""请基于以下背景知识回答:

{context}

问题:{msg.content}

要求:答复末尾标注用到的源 [1] [2] [3]。"""

    response = cl.Message(content="")
    stream = await client.chat.completions.create(
        model=os.getenv("MODEL", "qwen2.5-3b"),
        messages=[{"role": "user", "content": augmented}],
        stream=True, max_tokens=512,
    )
    async for chunk in stream:
        if chunk.choices[0].delta.content:
            await response.stream_token(chunk.choices[0].delta.content)

    # 把 source 显示在侧栏
    response.elements = [
        cl.Text(name=f"Source {i+1}", content=f"id={src}\n{doc}", display="side")
        for i, (src, doc) in enumerate(zip(sources, docs))
    ]
    await response.update()

4.3 Dockerfile:预下载 embedding model

multi-stage build,builder 里 python -c "SentenceTransformer(...)" 把 model 拉到 ~/.cache/torch/sentence_transformers/,第二阶段直接 COPY --from=builder:

FROM python:3.11-slim AS builder
WORKDIR /app
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt
RUN python -c "from sentence_transformers import SentenceTransformer; \
  SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')"

FROM python:3.11-slim
COPY --from=builder /root/.local /root/.local
COPY --from=builder /root/.cache/torch /root/.cache/torch
ENV PATH=/root/.local/bin:$PATH
COPY app.py /app/app.py
WORKDIR /app
CMD ["chainlit", "run", "app.py", "--host", "0.0.0.0", "--port", "8000", "--headless"]

requirements.txt 加 chromadb==0.5.20 / sentence-transformers==3.3.1 / torch==2.5.1+cpu --index-url https://download.pytorch.org/whl/cpu。

4.4 真坑:embedding model 不预下载,冷启拉 HF 卡 5 分钟

不在 builder 里预下载的话,Pod 第一次启动会去 HF Hub 拉 117MB —— 国内裸连 HF 几乎必 timeout,readinessProbe fail,Pod 反复 restart。预下载放 builder stage 还顺带让 model 进 image layer,CI cache 命中后零下载时间。

4.5 K8s 加 PVC 持久向量库

ChromaDB 本地数据 /data/chroma 必须挂 PVC,否则 Pod 重启全丢:

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata: {name: chroma-data, namespace: chat}
spec:
  accessModes: [ReadWriteOnce]
  storageClassName: longhorn      # Day 4 装的
  resources: {requests: {storage: 5Gi}}
---
apiVersion: apps/v1
kind: Deployment
metadata: {name: rag-chat-ui, namespace: chat}
spec:
  template:
    spec:
      containers:
      - name: chat-ui
        image: 10.0.24.28:30002/bootcamp/rag-chat-ui:latest
        volumeMounts:
        - {name: chroma, mountPath: /data/chroma}
      volumes:
      - name: chroma
        persistentVolumeClaim: {claimName: chroma-data}

4.6 工业增强方向

最小切片之外,生产 RAG 五个增强方向:chunking strategy(recursive / semantic 切块防语义断)、hybrid retrieval(BM25 sparse + embedding dense + RRF 融合)、re-ranker(bge-reranker-large 二阶段精排)、HyDE(用 LLM 先生成假答案再 embed,对模糊问题召回更准)、Ragas 评估(faithfulness / context_precision / answer_relevancy 量化)。

把 RAG 也 Operator 化是自然下一步 —— 扩 RAGService CRD 指向已有的 LLMService,加 embedding model + vector store + 文档源(Gitea repo 自动同步),跟 Day 13.1 同款声明式。


面试常见题

Q1:写一个 Operator 的演进路径是怎样的?

四阶段:

  1. CRD + Reconciler —— types.go 定义 spec/status,controller 用 controller-runtime 写 Reconcile,幂等 + 加 owner reference。能 kubectl apply 就 reconcile,删 CR 就级联 GC
  2. Validation marker —— +kubebuilder:default= / Enum= / Minimum= 把校验写进 CRD schema,apiserver 层挡住非法值,不要等 reconcile 才判
  3. Admission Webhook —— validation marker 表达不了的(跨字段一致性、外部依赖检查)走 ValidatingWebhook
  4. Conversion Webhook —— spec 演进到 v1beta1 → v1,需要 conversion webhook 让老 CR 自动转新版

深问:「reconcile 怎么保证幂等?」—— 所有子资源(Deployment / Service / HPA)都用 CreateOrUpdate 模式,加 owner reference,状态变更只更新 status subresource。

Q2:K8s 多集群联邦有哪些方案?怎么选?

三类:

  1. Karmada / KubeFed —— K8s 资源级,提交 ResourceTemplate + PropagationPolicy 到 federation control plane,调度器决定部署到哪些 member。适合需要 placement 策略 + per-cluster override 的复杂场景
  2. Cluster API —— 元层面,管「怎么创建 K8s 集群」而不是「怎么往多集群部署应用」。生命周期工具,不是联邦
  3. Cilium ClusterMesh —— 网络层,Service 加个 annotation 就跨集群路由。学习成本最低,但 CNI 必须 Cilium,且要求 PodCIDR 不重叠

实战推荐:生产环境 Cilium ClusterMesh 做网络 + ArgoCD ApplicationSet 做多集群 GitOps,避开 Karmada 8 个 CRD 的学习曲线。

Q3:Istio Ambient mesh 跟 sidecar mesh 有什么本质区别?

三层差异:

  • 数据面拆分:sidecar 把 Envoy 塞每个 Pod;Ambient 拆成 ztunnel(DaemonSet,L4 + mTLS)+ waypoint(per-ns,L7)
  • 资源开销:sidecar 是 Envoy × Pod 数,Ambient 是 ztunnel × Node 数。10K Pod 集群差 1TB 内存
  • 加入 mesh:sidecar 必须重启 Pod 注入 Envoy;Ambient 透明,namespace label 一打就生效

代价:Ambient L7 流量多一跳 hop(ztunnel → waypoint → Pod),生态成熟度差 sidecar 几年。

Q4:RAG 工程链路完整跑通要哪些组件?

四段:

  1. Embedding:文本转向量。小模型(MiniLM 117MB)CPU 跑,大模型(bge-large-zh)GPU。多语言场景用 paraphrase-multilingual-*
  2. Vector DB:ChromaDB(嵌入式,小规模)/ Qdrant / Milvus / Weaviate。要 PVC 持久化
  3. Retrieve:query embed → vector DB top-k 检索。生产加 hybrid(BM25 + dense)+ re-ranker
  4. Augment + Generate:top-k docs 拼进 prompt → LLM 生成 → 标注 source

常踩坑:embedding model 不在 Dockerfile 里预下载,runtime 拉 HF Hub 超时;chunking 切块切断语义;不带 source 标注用户无法判断答案可信度。

Q5:跨集群 service discovery 怎么实现?

两种思路:

  • K8s 资源层:Karmada ServiceImport / MultiClusterIngress,往 federation 提交 Service,scheduler 决定哪些集群暴露,client 通过统一 ingress 入口访问。控制面复杂
  • 网络层:Cilium ClusterMesh,Service 加 service.cilium.io/global=true annotation,eBPF 直接把跨集群 endpoint 写进每节点 datapath,client 调本地 Service IP 自动路由到远端 cluster

数据面区别:第一种走 ingress(多一跳 + TLS 终止),第二种直接 Pod-to-Pod 跨集群 VXLAN。后者 latency 低但要求两集群 PodCIDR 不重叠 + 都装 Cilium。


下一步

Day 13 把 4 个面试高频主题串成了一条「声明式 + 多集群 + mesh + 数据增强」叙事。Day 14 收尾:把这 14 天的内容做成可复盘的知识图谱,挑 3-5 个最有代表性的 incident 写成完整故事,准备技术面 deep dive。

在 GitHub 上编辑此页
Prev
Day 12:灾难恢复 + 生产事故注入
Next
Day 14:CKA / CKS 真题演练 + 14 天技术栈横向汇总