边缘集群部署 - 端到端验证与最大坑

边缘节点接入后做端到端验证;或遇到"边缘 pod 一直 Pending、cloudcore 日志刷 0 nodes need to sync"这类调度不下发问题时读这篇

02-部署安装 / 部署方式 / 边缘集群
端到端验证busybox边缘推理node-role.kubernetes.io/edgelabel硬编码EdgeNodesdownstream.goIsEdgeNodeEdgeNodeRoleValuecloudcorePending调度不下发LocationCache

边缘集群部署 - 端到端验证与最大坑(label 硬编码)

承接 05-边缘节点接入与项目组配置。本篇是云-边主线的 Phase G 端到端验证,以及跨多个 KubeEdge 版本都会踩的最大坑

来源:docs/部署/kubeedge部署边缘集群cube-studio.md 第 8~9 节。


八、Phase G:端到端验证

G.1 Web 公网

curl -sI http://47.114.127.214/frontend/
# HTTP/1.1 200 OK

G.2 NFS 双向

# 云端
echo "cloud-$(date +%s)" > /data/nfs/cloud-write.txt
# 边缘
cat /data/nfs/cloud-write.txt

G.3 云端训练 Pod

kubectl run cloud-training-test \
  --image=docker.m.daocloud.io/library/busybox:1.36.0 \
  --restart=Never \
  --overrides='{"spec":{"nodeSelector":{"train":"true"}}}' \
  --command -- sh -c "echo hello; sleep 30"
kubectl get pod cloud-training-test -o wide   # NODE=cube

G.4 边缘推理 Pod(快速用一键脚本)

cd install/kubernetes/kubeedge
bash 50-verify.sh edge-pc
# ✅ POD edge-verify Running on edge-pc

如果一直 Pending / ContainerCreating 不前进,按严重性逐项检查:

现象 对应坑
kubectl describe pod 只有 Scheduled 事件 本页第九节(label 非空)
edgecore log 有 failed to find plugin "loopback" CNI 插件缺失,见 05 E.6 / 07
边缘上 iptables-save | grep KUBE-SVC kube-proxy kubeconfig IP,见 05 E.7 / 07
从公网 istio 访问边缘服务 502 by-design,不是 bug,见 01 / 07

G.5 边缘推理服务访问

CubeStudio 为边缘 service 建 <svc>-externalexternalIPs=[边缘内网 IP],port=NodePort):

# 在和边缘同网段的机器上(192.168.3.0/24)
curl -sI http://192.168.3.22:<nodeport>/docs
# HTTP/1.1 200 OK

web UI 里显示的服务访问 URL 就是这个。公网用户访问不到。


九、【最大坑】KubeEdge edge 节点 label 硬编码

⭐ 四大坑之 1。跨 v1.20.0 / v1.22.1 / v1.23.0 均复现。

现象

  • 边缘节点 Ready,但新建的 Pod/Deployment 永远 Pendingkubectl describe 只有 Scheduled ... to edge-pc,无后续事件;
  • cloudcore 日志反复输出 downstream.go:128] there are 0 nodes need to sync config map, operation: MODIFIED
  • cloudcore 只会 dispatch 节点刚加入时被 DS controller 直接设 nodeName 的那几个 pod(kube-proxy / kube-flannel / node-exporter / edge-eclipse-mosquitto),之后一切静默。

根因(已定位到 KubeEdge 源码)

cloud/pkg/edgecontroller/controller/downstream.go

func (dc *DownstreamController) syncPod() {
    ...
    if !dc.lc.IsEdgeNode(pod.Spec.NodeName) {
        continue   // ← 所有 edge pod 在这里被静默丢弃
    }
    ...
}

LocationCache.EdgeNodes 是通过固定 label selector 填充的:

// common/constants/default.go
EdgeNodeRoleKey   = "node-role.kubernetes.io/edge"
EdgeNodeRoleValue = ""      // ← 必须空字符串!

keadm join 时会自动打上 node-role.kubernetes.io/edge=(空值)。但如果之后手动 kubectl label node <edge> node-role.kubernetes.io/edge=true 去"强化"这个角色标记,label 值被覆盖成 truelabels.SelectorFromSet({key:""}) 就匹配不到了,所有 edge pod 从此静默丢弃。这就是 cube-studio web 界面上在边缘项目组里看到 pod 一直 Pending 的全部原因。

⚠️ 待核实:上述 KubeEdge 源码路径(cloud/pkg/edgecontroller/controller/downstream.gocommon/constants/default.go)来自上游 KubeEdge 项目,不在本仓库 CubeStudio 代码里,无法在本仓库核对行号。结论(edge label 必须空值)已被作者跨三个版本验证,按现象使用即可。

修复

# 检查:值必须是空字符串
kubectl get node <edge> -o jsonpath='{.metadata.labels.node-role\.kubernetes\.io/edge}'

# 修复(若非空)
kubectl label node <edge> node-role.kubernetes.io/edge-
kubectl label node <edge> node-role.kubernetes.io/edge=

# 让 cloudcore 重建 LocationCache
kubectl rollout restart deployment cloudcore -n kubeedge

重启后 cloudcore debug 日志(--v=4)应立刻看到:

message_dispatcher.go:140] [DispatchDownstream] dispatch Message to edge:
  Resource:node/<edge>/<ns>/pod/<your-pod>  Operation: update / insert

边缘 crictl pods 也会看到新 sandbox,pod 从 Pending 变 ContainerCreating → Running。

对 CubeStudio 的特别提醒

CubeStudio 在项目组页面可能让你打 node-role.kubernetes.io/edge=true(看起来更像 K8s 风格)。不要这样打。要么不动(keadm join 默认就对),要么 = 空值。其他用于 CubeStudio 自己调度的标签(org=edgeserving=true 等)不受影响,可正常打。


更多次要的坑(CNI / kube-proxy 详解、镜像大小预算、NFS stale handle、DaemonSet CrashLoop 等)见 07-踩坑速查

最后更新 2026-07-04完整文档以官方仓库为准:GitHub Wiki