边缘集群部署 - 踩坑速查与版本命令
按严重程度排序的其它坑,以及常见坑附录、版本命令速查。配合 06-验证与最大坑-label硬编码 的四大坑一起看。
来源:
docs/部署/kubeedge部署边缘集群cube-studio.md第 10 节 + 附录 A/B。
一、其它踩过的坑(按严重程度)
10.1 CNI 插件只有 flannel,缺 loopback/bridge(⭐⭐⭐ 必中)
- 症状:pod 一直
ContainerCreating,kubectl describe看到Events: <none>但不报错;journalctl -u edgecore有plugin type="loopback" failed (delete): failed to find plugin "loopback" in path [/opt/cni/bin]。 - 原因:keadm join 只放了
flannel一个 CNI 插件,kubelet 建/拆 pod sandbox 必须要loopback。 - 修复:装
containernetworking/pluginsv1.4.0(见 05 E.6)。
10.2 kube-proxy kubeconfig 默认是"云端私网 IP"(⭐⭐⭐ 必中)
- 症状:边缘上
iptables-save | grep KUBE-SVC空无规则;service ClusterIP / external IP / NodePort 在边缘节点本机都不通;但直接 pod IP 能通。 - 排查:边缘 kube-proxy pod 日志
failed to list *v1.Service: Get "https://172.30.159.159:6443/...": dial tcp ...: i/o timeout。 - 原因:kubeadm 生成的
kube-system/kube-proxyconfigmap 里server:是 apiserver 监听的私网 IP,边缘经公网连不通。 - 修复:configmap 的 server 改公网 IP(见 05 E.7)。前提是
kubeadm init --apiserver-cert-extra-sans=<公网 IP>。
10.3 Cloud → Edge pod 单向不通(⭐⭐ by-design)
- 症状:云端 istio ingressgateway → Service → edge pod 路由走不通,返回 502。
- 排查:云端
ip route里看不到 edge 节点 pod CIDR(如10.244.1.0/24)的路由;edge flannel 只显示它主动连 cloud 的 VXLAN,cloud flannel 没有回建。 - 原因:边缘在 NAT 后面,cloud flannel 无法主动建 VXLAN 隧道到边缘。这是 KubeEdge+NAT 边缘的固有限制。
- 解决方式:
- 不解决:CubeStudio 已为此设计了单独的
<svc>-externalService(externalIPs=[<边缘内网 IP>]),边缘网内用户直接访问http://<edge-ip>:<nodeport>/即可; - 要公网访问的话,自己在边缘上加反向代理(nginx / frp)把流量暴出去。
- 不解决:CubeStudio 已为此设计了单独的
10.4 cloudcore parse message ... resource type not found(⭐ 忽略)
edgecore 上行 lease/status 消息格式 warning,与 pod-sync 无关,忽略即可。
10.5 cloudcore failed to list *v1beta1.DeviceStatus forbidden(⭐ 老环境需补)
旧版 keadm 生成的 ClusterRole 缺 devices.kubeedge.io 权限。修复:kubectl apply -f install/kubernetes/kubeedge/20-cloud-rbac.yaml(该文件 ClusterRole 名 cloudcore-extra,补齐相关 CRD 权限)。
10.6 docker image prune -af 会误删正在使用的镜像(⭐ 容易踩)
docker image prune -af 清所有"没有 running container 使用"的镜像。如果把 docker.service 停了再 prune,所有镜像都被认定 idle 一次性全删。
- 教训:想从 docker 迁移镜像到 containerd
k8s.io,要先docker save | ctr -n k8s.io images import -流式迁移,再停 docker。
10.7 镜像大小预算(⭐ 容易撑爆 /)
docker images 显示的是解压后大小(如 yolo26 25.4 GB),但拉镜像到 containerd 时经历 "download 压缩态(13.5 GB) + unpack 解压到 overlayfs(25 GB)" 两阶段,峰值磁盘需要约 压缩 + 解压(约 38 GB)。
- 排查:
/var/lib/containerd所在分区剩余空间要有单镜像大小 × 2 的余量,df -h /检查。 - 修复:如果 / 分区小,把 containerd root dir 搬到大盘后
systemctl restart containerd:# /etc/containerd/config.toml root = "/data/containerd" state = "/data/containerd-state"
10.8 edgecore.yaml 的 quic / websocket.enable 互斥
edgecore 启动验证会检查 websocket.enable 和 quic.enable 不能同时为 true。盲用 sed 's/false/true/g' 会让 edgecore 启动失败。修复:用 python 按 YAML 结构定向改(见 05 E.5 脚本)。
二、常见坑附录
A-1 containerd 2.x 的 mirror 不生效
默认启用 Transfer Service 做 pull,它有独立的 config_path。只配 CRI plugin 的 config_path 对 kubelet 拉镜像不够,必须也配 transfer plugin 的 config_path,或者设 use_local_image_pull = true 强制走 CRI plugin(见 03 A.2)。
A-2 Ubuntu 24.04 无 iptables-legacy / firewalld
init_node.sh 里 systemctl stop firewalld iptables ip6tables nftables 报 Unit not loaded 可忽略。
A-3 GitHub release 下载超时
国内直连 github.com/releases/download/* 几乎必 timeout。替代前缀:gh-proxy.com(本文到处用)、ghproxy.net、mirror.ghproxy.com。
A-4 NFS showmount -e timeout 但 mount -t nfs 成功
showmount 走 rpcbind 端口 111,mount NFSv4 只要 2049。安全组只开 2049 的话 showmount 会超时,直接 mount 没问题。
A-4b NFS 挂载存活但 ls /data/nfs 报 d?????????(stale handle)
老方案按"边缘出口公网 IP"做 NFS export 白名单,出口 IP 一变(家宽重拨/4G 切换),边缘 NFS 客户端会 hang 成 stale handle,所有依赖 /data/k8s/... 的 pod 报 failed to prepare subPath for volumeMount "kubeflow-user-workspace" 进入 CreateContainerConfigError。
现在 export 已改成 *(见 03 Phase B,注意必须用 * 不是 0.0.0.0/0),不会再因 IP 变化失效。若延续了老的 IP 白名单方案又遇到,临时修复:
# 云端
sed -i 's|^/data/nfs <旧IP>|/data/nfs <新IP>|' /etc/exports
exportfs -ra
# 边缘
umount -f -l /data/nfs
mount -t nfs -o vers=4,rsize=1048576,wsize=1048576,hard,timeo=600 \
<云端公网IP>:/data/nfs /data/nfs
# 云端:重启所有报错的 pod
kubectl get pods -A | grep CreateContainerConfigError | \
awk '{print "kubectl delete pod -n", $1, $2, "--grace-period=0 --force"}' | bash
A-5 start.sh 脚本 node 名按 IP grep 匹配不到
脚本假设传入的 $1 是内网 IP 且能在 kubectl get node -o wide 输出里匹配到。本场景传的是公网 IP(用于 SERVICE_EXTERNAL_IP),grep 匹配不到,fallback 成"取第一个节点":
node=$(kubectl get node -o name | head -n 1 | sed 's|node/||')
A-6 istio-ingressgateway 没 Service
install/kubernetes/istio/install-1.15.0.yaml 里只有 Deployment 没有 Service,要手动建 ClusterIP + externalIPs(见 04 C.5)。创建后重启 istiod + ingressgateway 让 envoy 拿到 CA + route。
A-7 cloudcore 升级/重装后 ns 卡 Terminating
kubectl delete ns kubeedge 可能因残留 pod finalizer 卡住,强制 finalize:
kubectl get ns kubeedge -o json | \
python3 -c 'import json,sys;d=json.load(sys.stdin);d["spec"]["finalizers"]=[];print(json.dumps(d))' | \
kubectl replace --raw /api/v1/namespaces/kubeedge/finalize -f -
A-8 edgecore.yaml 改 enable 别用全局 sed
edgeStream.enable、quic.enable、websocket.enable、metaServer.enable 各有取值约束(websocket.enable=true 和 quic.enable=true 互斥)。用 python 按 YAML 结构局部改,不要 sed -i 's/false/true/g'。
A-9 边缘上 kube-flannel-ds / node-exporter CrashLoopBackOff(无害)
kube-flannel:边缘 KubeEdge 自己实现网络(不通过 flannel),但 DaemonSettolerations: Exists强制把 pod 调度到 edge 节点,pod 起来发现没 cluster CIDR、没法 watch apiserver 就 crash;node-exporter:依赖 hostNetwork + 一些 kernel proc 节点,KubeEdge edge 节点 kubelet socket 路径不一致。
这两个对 CubeStudio 推理流程完全不影响,可忽略。要彻底干净,加 nodeAffinity 排除 edge 节点:
kubectl patch ds kube-flannel-ds -n kube-flannel \
-p '{"spec":{"template":{"spec":{"affinity":{"nodeAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchExpressions":[{"key":"node-role.kubernetes.io/edge","operator":"DoesNotExist"}]}]}}}}}}}'
kubectl patch ds node-exporter -n monitoring \
-p '{"spec":{"template":{"spec":{"affinity":{"nodeAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchExpressions":[{"key":"node-role.kubernetes.io/edge","operator":"DoesNotExist"}]}]}}}}}}}'
三、版本命令速查
# 云端
kubectl version --short # v1.28.15
containerd --version # 2.2.1
nerdctl --version # 1.7.6
keadm version # v1.23.0(注意上游打包显示可能为 v1.20.0,不影响)
# 边缘
containerd --version # 1.7.3
keadm version # v1.23.0
kubectl get node edge-pc # v1.32.x-kubeedge-v1.23.0
以上版本为本次部署实录的实际栈,具体以你自己的环境与源码为准。