简介:面向Kubeedge初学者与边缘计算实践者的部署资源包,基于CentOS 7.9、K8s v1.22.17(kubeadm)与Kubeedge v1.13.1搭建链路,整合MetalLB负载均衡器,解决边缘节点接入、Service LoadBalancer暴露及集群验证等常见问题。包内共15个文件,YAML部署清单覆盖Calico网络、MetalLB配置与测试应用,Docker镜像tar包提供Kubeedge相关组件及测试所需镜像,另有Markdown部署说明与keadm离线安装工具,整体约474.64MB,便于离线环境使用;压缩包内目录划分清晰,可快速定位所需配置或镜像。已有242人学习,适合想按完整部署链路复现环境、减少版本匹配踩坑的入门用户。随包文档从系统准备、K8s集群部署、Kubeedge安装、MetalLB配置,到测试实例与结果验证依次展开,并备齐所需镜像和配置清单,能够降低自行搜集组件与排查兼容问题的时间成本,按步骤操作即可搭建一套可用的Kubeedge边缘计算环境。
1. 这套组合的定位:给边缘节点一个真正可用的云端入口
做云边协同的人,大多被同一个问题卡过:云端 K8s 集群搭好了,Kubeedge 的 CloudCore 也装上去了,但边缘节点拿着 IP 和 token 怎么都连不上。你绕开 LoadBalancer,改用 NodePort 或者直接把进程跑在宿主机上,能连上是能连上,可生产环境一次重启、一次迁移就把你打回原形。CentOS 7.9 加 K8s v1.22.17 加 Kubeedge v1.13.1 这条组合线,看起来版本老,实际是边缘现场最能落地的搭配:系统资源占用低,内核习惯接近传统运维,而 v1.13.1 的 Kubeedge 正好还支持用 LoadBalancer 把 CloudCore 的 WebSocket 通道做成稳定入口。适合正在搭边缘网关、想把设备数据汇聚到云端的集群运维和开发人员。这篇文章会照着这条线,把环境、组件、负载均衡器配置和排错讲完。
2. CentOS 7.9 上的 K8s v1.22.17:先让集群稳定,再谈边缘
2.1 系统初始化参数:内核和网络是 CentOS 7.9 集群的命门
CentOS 7.9 用 3.10 内核,平时跑传统服务没什么感觉,一旦上 K8s,iptables、cgroup 和网络插件会接连给你颜色看。我一般会在三台机器或单机 All-in-One 上做最小化初始化,以下参数是跑 K8s v1.22.17 之前必须处理的。
# 关闭 swap,K8s 强制要求 swapoff -a sed -i 's/.*swap.*/#&/' /etc/fstab # 加载内核模块 cat > /etc/modules-load.d/k8s.conf <<EOF overlay br_netfilter EOF modprobe overlay modprobe br_netfilter # 网络转发与 iptables 桥接流量 cat > /etc/sysctl.d/k8s.conf <<EOF net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF sysctl --system这段逻辑不复杂:swapoff -a让 kubelet 的资源计算不再被交换分区干扰;br_netfilter是保证 K8s 的 Service 和 Pod 网络能把 bridge 上的数据包送到 iptables 规则链里。CentOS 7.9 默认net.bridge.bridge-nf-call-iptables是关闭的,如果你不打开,后面 LoadBalancer 转发流量时会丢包,查找问题能找出一身汗。建议顺手把机器时间同步打开,因为 Certificates 的签发和校验对时间敏感,边缘节点和云端时间差超过 5 分钟,证书大概率直接不可用。
2.2 用 kubeadm 锁定版本:为什么是 v1.22.17 而不是 v1.23
K8s v1.22.17 是 1.22 系列的最后一个补丁版本,和 CentOS 7.9 的兼容性是把一些列升级包按保守组合压出来的。更高版本的 kubelet 对 cgroup v2 和 glibc 有更激进的默认值,在 CentOS 7 上经常出现 Pod 启动后立刻被 OOMKilled,或者 kubelet 报unsupported systemd version。锁版本的正确办法是安装时指定完整版本号。
# 配置阿里云镜像源(vault 源在 CentOS 7.9 上也存在) cat > /etc/yum.repos.d/kubernetes.repo <<EOF [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled=1 gpgcheck=0 EOF yum install -y kubeadm-1.22.17-0 kubelet-1.22.17-0 kubectl-1.22.17-0 systemctl enable --now kubelet这里把gpgcheck=0是为了避开公网 GPG 密钥在离线环境失效的问题。安装后先不急着 init,建议先拉取镜像再初始化,避免初始化时因为镜像拉取超时导致集群半成品。
kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.22.17 kubeadm init \ --kubernetes-version v1.22.17 \ --apiserver-advertise-address=192.168.10.10 \ --pod-network-cidr=10.244.0.0/16 \ --service-cidr=10.96.0.0/16常见做法是先用一个内网地址做 apiserver 的 advertise address,之后部署 CNI 插件,这里我推荐 Flannel v0.19 或 Calico v3.22,二者在 1.22 上都有成熟参数。先确认集群 Ready,再继续往下走,否则 Kubeedge 的故障排查会叠着 K8s 自身的网络故障,很难定位。
2.3 CentOS 7.9 的存储和镜像仓库:几个先决参数
CentOS 7.9 默认/分区常常只有 50GB,K8s 组件加 Kubeedge 镜像很容易撑满。如果磁盘不够,在初始化前把根分区扩容掉,避免中途kubeadm init因为磁盘写满失败。另一个容易被忽略的点是容器运行时。Kubeedge v1.13.1 的 EdgeCore 在边缘侧需要访问 containerd 或 Docker,建议统一用 containerd,并配置SystemdCgroup,与 kubelet 的 cgroup driver 保持一致。
3. Kubeedge v1.13.1 部署:CloudCore 和 EdgeCore 的配对关系
3.1 下载 keadm 与 CloudCore 的证书策略
Kubeedge v1.13.1 的部署工具叫keadm,它负责在云端初始化 CloudCore,在边缘侧注册 EdgeCore。下载之后先把keadm放到/usr/local/bin并给执行权限。初始化前要决定两个参数:CloudCore 的advertise-address和加密端口。这里的 advertise address 不是随便填,EdgeCore 会把--cloudcore-ipport指向这个地址,如果填错,边缘侧握手阶段直接 fail。
# 在云端机器上执行 keadm init \ --advertise-address=192.168.10.10 \ --kubeedge-version=v1.13.1 \ --cloudcore-image=kubeedge/cloudcore:v1.13.1 \ --set cloudCore.modules.cloudHub.enable=true这段命令会生成 CloudCore 的 DaemonSet 或 Deployment 资源,并自动签发证书。注意advertise-address这个 IP 的作用是让 CloudCore 把证书里的 IP SAN 和地址一起公布出去。如果你后面要换成 LoadBalancer 的外部 IP,这里就存在一个隐患:证书里的 SAN 不包含 LB IP。所以更稳的做法是先不填最终 IP,等 LoadBalancer 地址确定后,再重新生成证书,或者直接给 CloudCore 签发带多个 IP 的证书。别嫌麻烦,证书问题在边缘侧几乎无法排查,日志只显示failed to websocket connection。
3.2 CloudCore 负载均衡化的第一步:确认端口与模块
CloudCore 里承担边缘连接的是 CloudHub 模块,默认监听 10000 端口做 WebSocket,10002 端口做 QUIC。如果边缘侧网络设备只会放行 TCP,那就只暴露 10000。初始化完成后,用kubectl get svc -n kubeedge查看 CloudCore 服务是否存在。
kubectl get svc -n kubeedge -o wide kubectl logs -n kubeedge -l kubeedge=cloudcore --tail=50Kubeedge v1.13.1 在初始化时通常不会自动创建 LoadBalancer 类型的 Service,默认可能是 ClusterIP。你需要手动把 Service 类型改成 LoadBalancer,背后的原理是让 K8s 通过云控制器或 MetalLB 这类组件分配一个额外 IP,并把 CloudHub 的 10000 端口映射上去。很多教程在这里直接跳过,导致边缘节点只能连到 ClusterIP,边缘侧一旦不在集群内网,就永远连不上。这一步,是整套方案踩坑率最高的地方。
3.3 获取 token 与边缘侧加入命令
CloudCore 初始化完成后,需要在云端生成一个 token,边缘节点加入时使用。Kubeedge 的 token 机制是一次性的,默认有效期 24 小时,生产环境建议配置持久 token,或者把 token 放在边缘侧的配置文件中。
keadm gettoken返回的 token 是加密字符串。边缘节点执行加入命令,这里的关键参数是--cloudcore-ipport,它必须是 LoadBalancer 暴露出的 IP 和端口,不能填 CloudCore 所在机器的内网 IP。
keadm join \ --cloudcore-ipport=192.168.20.100:10000 \ --token=<你的token> \ --kubeedge-version=v1.13.1 \ --cgroupdriver=systemd \ --remote-runtime-endpoint=unix:///var/run/containerd/containerd.sock加入完成后,在云端执行kubectl get nodes,应该能看到边缘节点以edge-worker的角色出现。如果节点状态是 NotReady,多半是 CloudCore 侧证书和地址不匹配,或边缘侧无法解析 LoadBalancer 的 IP,后面避坑章节专门讲。
4. 用 LoadBalancer 暴露 CloudCore:MetalLB 服务与参数调整
4.1 裸机环境为什么选 MetalLB
Kubeedge 的 CloudCore 是部署在云端的 Pod,边缘节点通过公网或专线访问它。云端集群如果是自建机房的裸机环境,没有云厂商的 LB 组件,就需要一个能在二层或三层网络上宣告虚拟 IP 的工具。MetalLB 是使用最广的方案,它有两种模式:BGP 和 L2。CentOS 7.9 + K8s v1.22.17 下面,我推荐 L2 模式,因为 BGP 还需要额外对接交换机,L2 模式只要确保 VIP 所在网段与边缘节点可达即可。
安装 MetalLB 的常见做法是:
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.10/config/manifests/metallb-native.yaml注意:K8s v1.22.17 的 API 版本和 MetalLB v0.13.x 的 CRD 是匹配的。更高版本的 MetalLB 可能需要 K8s v1.23 以上的 feature gate,反而翻车。安装完成后要创建配置清单。
4.2 MetalLB 的 L2 配置与 strictARP
apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: cloudcore-pool namespace: metallb-system spec: addresses: - 192.168.20.200-192.168.20.220apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: cloudcore-l2 namespace: metallb-system spec: ipAddressPools: - cloudcore-poolIPAddressPool 定义了 LoadBalancer 可分配的 IP 范围。这里有一个关键前提:这个网段必须和边缘节点路由可达,而且不能让 DHCP 分配池和 MetalLB 的池重叠,否则两个设备抢同一个 IP,负载均衡器就成了玄学。L2Advertisement 告诉 MetalLB 以 ARP 应答的方式宣告 VIP。还要修改 kube-proxy 的 strictARP 参数,因为 MetalLB L2 模式依赖 kube-proxy 不去竞争 VIP 的 ARP 响应。
kubectl get configmap -n kube-system kube-proxy -o yaml > kube-proxy-cm.yaml sed -i 's/strictARP: false/strictARP: true/' kube-proxy-cm.yaml kubectl apply -f kube-proxy-cm.yaml kubectl rollout restart daemonset kube-proxy -n kube-system这里把strictARP打开,否则 MetalLB 收到 ARP 请求时,kube-proxy 也会尝试应答,VIP 的流量到不了 CloudCore。
4.3 CloudCore Service 改为 LoadBalancer:配置 externalTrafficPolicy 与端口
CloudCore 默认 Service 大概是 ClusterIP,手动改掉它。
apiVersion: v1 kind: Service metadata: name: cloudcore namespace: kubeedge annotations: metallb.universe.tf/allow-shared-ip: cloudcore spec: type: LoadBalancer loadBalancerIP: 192.168.20.200 selector: kubeedge: cloudcore ports: - port: 10000 targetPort: 10000 protocol: TCP name: cloudhub - port: 10002 targetPort: 10002 protocol: TCP name: cloudhub-quic externalTrafficPolicy: Local上面把 Service 的loadBalancerIP固定为地址池里的192.168.20.200。固定 IP 的意义是让 CloudCore 重启、重建后地址不变,边缘节点的配置也不用跟着改。externalTrafficPolicy: Local则保证了 CloudCore 的 Pod 在哪台机器,流量就到哪台机器,不经过 SNAT。这样做的好处是边缘节点看到的连接源 IP 就是真实 IP,便于后续做设备维度的访问控制。代价是如果 CloudCore 的 Pod 被调度走了,VIP 会临时不可用,所以实际部署时可以给 CloudCore 加一个 NodeSelector,或者多副本跑两个实例。
创建后验证:
kubectl get svc -n kubeedge cloudcore -o wide如果EXTERNAL-IP显示192.168.20.200,说明 MetalLB 已经把这个 IP 接管了。接着在边缘节点上 ping 和 telnet 一下,确认端口通。
ping -c 3 192.168.20.200 nc -vz 192.168.20.200 10000只要网络可达,EdgeCore 的加入命令里就可以放心把这个 IP 填进去。
5. CentOS 7.9 上的排错与避坑:版本、内核和网络模式
5.1 EdgeCore 连接 CloudCore 一直 connection refused
现象:边缘节点执行keadm join后,一直提示failed to connect,CloudCore 日志里没有任何报错,边缘节点日志里只有connection refused。
原因:最常见的是 CloudCore Service 的端口没有正确暴露。用kubectl get svc -n kubeedge cloudcore看 EXTERNAL-IP,如果是<none>说明 LoadBalancer 分配失败。另外,如果用了 NodePort 临时替代,边缘侧和云端必须保证端口一致,而且不能有中间防火墙拦截。
解决:先确认 MetalLB 的 IPAddressPool 是否还有空闲地址,然后看 CloudCore 的 Pod 日志里cloudhub模块是否真的监听在 10000。执行kubectl logs -n kubeedge -l kubeedge=cloudcore,如果看到failed to load certificate,就要连带检查证书 SAN 中是否包含 VIP 地址。
5.2 MetalLB 分配了 IP 但 ARP 不通
现象:EXTERNAL-IP有值,但边缘节点ping不通,nc也不通,在云端机器上却能通。
原因:L2 模式要求 MetalLB 的 Pod 和 CloudCore 的 Service 流量都在同一二层网络内。CentOS 7.9 默认防火墙 open 了端口,但没有允许 ARP 协议。如果边缘节点和 VIP 在同一网段,但 ARP 请求被 firewalld 丢弃,也会导致 ping 不通。
解决:在云端节点打开 ARP 相关规则,或者直接关闭 firewalld。
systemctl disable --now firewalld systemctl restart network生产环境如果必须保留防火墙,就只放行 CloudCore 端口和 ARP 协议。这个坑最隐蔽,因为从云端本机连 VIP 时流量走 lo,不经过 ARP,所以常被误判为 MetalLB 配置正常。
5.3 Kubeedge v1.13.1 在 K8s v1.22.17 上缺少 CRD 和 RBAC
现象:keadm init执行完后,CloudCore 的 Pod 一直 CrashLoopBackOff,日志报failed to list configmaps或failed to create leader election lock。
原因:Kubeedge v1.13.1 安装包里的 CRD 和 RBAC 通过keadm init创建时,不一定覆盖了 K8s v1.22 的所有资源权限。特别是cloudcore需要访问configmaps、leases、nodes,如果 RBAC 没有授权,就会一直因为权限失败退出。
解决:从 Kubeedge 发布包中的build目录里手动应用crds和rbac,然后再重启 CloudCore。
kubectl apply -f build/crds/ kubectl apply -f build/rbac/ kubectl rollout restart deployment -n kubeedge cloudcore遇到这种情况,别急着调网络参数,先看日志里有没有Forbidden字样,Kubeedge 的权限问题比网络问题好处理,但容易误导排查方向。
5.4 edgecore 加入后节点一直 NotReady
现象:边缘节点已经加入,kubectl get nodes能看到边缘节点,但状态始终NotReady,在边缘节点查看 EdgeCore 日志,报failed to sync pod status。
原因:边缘节点与云端之间的 WebSocket 连接虽然通了,但 EdgeCore 上报 Pod 状态时依赖 Kubelet 的运行时,如果边缘节点的容器运行时 socket 配置不对,CloudCore 无法拿到 Pod 状态。CentOS 7.9 上如果把 Docker 和 containerd 混装,--remote-runtime-endpoint填错了就会出现这种问题。
解决:明确边缘节点用 containerd 还是 Docker,二者选一。使用 containerd 时,必须确保containerd.sock存在,且 EdgeCore 的配置里remote-runtime-endpoint与 socket 路径一致。加入命令加上--remote-runtime-endpoint参数后,重启 edgecore 服务。
5.5 token 过期与证书轮换
现象:边缘节点第一次加入成功后,后来因为网络断开重连,发现要重新加入,旧的 token 失效。
原因:Kubeedge 默认生成一次性 token,过期后 EdgeCore 的证书无法续期,手里没有永久 token 就只能重新生成。
解决:在 CloudCore 的配置中把 token 的有效期改长,或者使用--token参数配合证书轮换。一般在初始化时设置--set cloudCore.modules.cloudHub.authTokenExpirationSeconds=31536000,让 token 一年有效。这一步是后来才加的,我在第一次搭的时候就吃过一次亏,边缘设备装好后没法再到现场重新输入 token,提前把 token 写死到配置文件里才能安心。
6. 验证边缘节点与云端负载:从节点 Ready 到 Pod 下发
最后,把整套链路验证一遍。云端执行kubectl get nodes和常用 K8s 命令确认边缘节点 Ready:
kubectl get nodes -o wide kubectl get pods -n kubeedge -o wide kubectl logs -n kubeedge -l kubeedge=cloudcore --tail=30在边缘节点上查看 edgecore 服务的活跃状态,然后部署一个测试 Pod,指定调度到边缘节点:
kubectl run edge-test --image=busybox --overrides='{"spec":{"nodeSelector":{"node-role.kubernetes.io/edge-worker":""}}}' -- sleep 3600 kubectl get pods -o wide | grep edge-test如果 Pod 能 Running,说明从 CloudCore 到 EdgeCore 的 Pod 下发链路是通的。实际生产里我还会加一条额外验证:从边缘节点反方向访问 CloudCore 暴露的 10000 端口,确认 WebSocket 连接重启后不会断。这个可以通过反复重启 edgecore 来模拟,只要边缘节点能在云端看到正常心跳,就可以收工。
进阶做法是把 CloudCore 的 LoadBalancer Service 再做一次固定 IP 绑定。如果没有条件部署 MetalLB,也可以绕开 LoadBalancer,直接在 Service 上写externalIPs,把宿主机 IP 暴露出去。这个方案少了一层 LB,但少了一堆 ARP 和 strictARP 的坑:
spec: type: ClusterIP externalIPs: - 192.168.20.100 ports: - port: 10000 targetPort: 10000externalIPs和 LoadBalancer 的区别在于它没有健康检查,IP 不可达时也不会自动漂移。如果你只有单台 CloudCore,这个简化方案反而更稳定。但要做多副本和故障转移,老老实实用 LoadBalancer。我希望你第一次搭就把 LoadBalancer 这一层吃透;否则后期边缘节点多了,每一次重连问题都会回到这个入口上。这套组合是我踩过很多坑后愿意留下的稳定路线,希望帮到你。
本文还有配套的精品资源,点击获取