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_ENDPOINTenv 走 hf-mirror 国内加速 —— 不然冷启拉模型卡死hf-cachehostPath +dshmemptyDir(memory, 4Gi) —— 前者跨 Pod 复用模型缓存,后者给 vLLM 张量并行用共享内存nvidia.com/gpu: 1resources 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,整个流程崩溃。
生产可行的修复路径(按成本从低到高):
- 预拉镜像:节点上
ctr -n k8s.io image pullcfssl + karmada-* 整套,再 helm install - 镜像替换:
--set apiServer.image=harbor.local/karmada/...全部走内网 Harbor - 延长 timeout:
--timeout 30m,慢但能过 - 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 两个方案的根本差异
| 维度 | Karmada | Cilium ClusterMesh |
|---|---|---|
| 抽象层 | K8s 资源级 | 网络层 |
| service discovery | MultiClusterIngress / ServiceImport | global 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 sidecar | Istio Ambient | Cilium SM |
|---|---|---|---|
| L4 数据面 | Envoy per Pod | ztunnel DaemonSet | eBPF in kernel |
| L7 数据面 | Envoy per Pod | waypoint per ns | cilium-envoy DS |
| mTLS | Pod 级 identity | Pod 级 identity | 节点级 WireGuard |
| L7 routing | VirtualService / HTTPRoute | HTTPRoute | CiliumNetworkPolicy(弱) |
| 资源开销 | 高(sidecar × N) | 中(DS × node) | 极低(eBPF) |
| Pod 启动顺序 | race | 无 sidecar 无 race | 无 race |
| 加入 mesh | 必须重启 Pod | 不需要 | 不需要 |
| CNI 绑定 | 不绑 | 不绑 | 必须 Cilium |
| 生态成熟 | 5+ 年 | 2024 GA | 4+ 年 |
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 /root/.local /root/.local
COPY /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 的演进路径是怎样的?
四阶段:
- CRD + Reconciler —— types.go 定义 spec/status,controller 用 controller-runtime 写 Reconcile,幂等 + 加 owner reference。能
kubectl apply就 reconcile,删 CR 就级联 GC - Validation marker ——
+kubebuilder:default=/Enum=/Minimum=把校验写进 CRD schema,apiserver 层挡住非法值,不要等 reconcile 才判 - Admission Webhook —— validation marker 表达不了的(跨字段一致性、外部依赖检查)走 ValidatingWebhook
- Conversion Webhook —— spec 演进到 v1beta1 → v1,需要 conversion webhook 让老 CR 自动转新版
深问:「reconcile 怎么保证幂等?」—— 所有子资源(Deployment / Service / HPA)都用 CreateOrUpdate 模式,加 owner reference,状态变更只更新 status subresource。
Q2:K8s 多集群联邦有哪些方案?怎么选?
三类:
- Karmada / KubeFed —— K8s 资源级,提交 ResourceTemplate + PropagationPolicy 到 federation control plane,调度器决定部署到哪些 member。适合需要 placement 策略 + per-cluster override 的复杂场景
- Cluster API —— 元层面,管「怎么创建 K8s 集群」而不是「怎么往多集群部署应用」。生命周期工具,不是联邦
- 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 工程链路完整跑通要哪些组件?
四段:
- Embedding:文本转向量。小模型(MiniLM 117MB)CPU 跑,大模型(bge-large-zh)GPU。多语言场景用
paraphrase-multilingual-* - Vector DB:ChromaDB(嵌入式,小规模)/ Qdrant / Milvus / Weaviate。要 PVC 持久化
- Retrieve:query embed → vector DB top-k 检索。生产加 hybrid(BM25 + dense)+ re-ranker
- 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=trueannotation,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。