
简介面向Kubeedge与Kubernetes集群集成部署的实操资源包专为初次接触Kubeedge的运维或开发人员设计。内容围绕Centos7.9系统、K8Sv1.22.17及Kubeedgev1.13.1的完整搭建流程展开并重点演示如何借助MetalLB负载均衡器对外暴露服务解决边缘节点与云端集群的联通及流量调度问题。压缩包共15个文件约474.64MB包含7份YAML配置清单、6个离线镜像tar包、1份Markdown部署文档和1个Kubeedge安装工具压缩包可对照文档完成从环境准备、集群初始化到插件部署的全过程。目前已有242人学习特别适合希望快速复现边缘计算环境、减少镜像下载与版本踩坑的读者。文档对准备CentOS系统、安装Kubeedge、配置MetalLB、测试实例及验证结果等环节均给出明确说明按步骤操作即可获得可运行的负载均衡型Kubeedge集群。1. 把 Centos7.9K8Sv1.22.17Kubeedgev1.13.1 这套组合讲透边缘节点访问不是玄学先解决 LoadBalancer这套资源解决的是边缘计算里最尴尬的一个问题KubeEdge 把云端和边缘端打通了节点也能注册上来但边缘端的服务怎么被外面访问到大多数教程讲到kubectl get nodes看到两个 Ready 就收工结果部署完业务发现 Service 的 EXTERNAL-IP 一直 Pending没人告诉你下一步怎么办。这份资源包把最后一块短板补上了——用 MetalLB 做 LoadBalancer让边缘服务拿到真实 IP。整套组合是 Centos7.9 系统、kubeadm 部署的 K8S v1.22.17、KubeEdge v1.13.1 边缘框架外加 MetalLB 负载均衡器镜像和 yaml 文件都打好了包。适合两类人一类是刚接触 KubeEdge、照着文档能把集群拉起来但卡在服务暴露上的新手另一类是在内网环境做边缘计算验证、没有云厂商负载均衡器可用的运维。你拿到的不是一份纯文档而是一套包含六个镜像 tarball、七个 yaml 文件的完整落地包。2. 环境准备与镜像导入先把版本边界和系统底子打好2.1 版本匹配关系为什么是 v1.22.17 和 v1.13.1 这对组合KubeEdge 对 Kubernetes 的版本兼容不是差不多就行官方支持的组合范围收敛得很明确。v1.13.1 对应的是 K8S 1.22 到 1.24 这个区间超出这个范围keadm 初始化或者 edgecore 启动阶段就会报证书或者协议不匹配的问题。用 v1.22.17 是 kubeadm 部署的这个版本线里比较稳的补丁版本既能避开 1.22.0 早期版本的 etcd 稳定性问题又留在 KubeEdge v1.13.1 的兼容区间内。Centos7.9 这一层要注意内核版本uname -r至少是 3.10.0-1160 系列KubeEdge 的 edgecore 对内核没有特别激进的要求但 Docker 和 Kubernetes 的组件依赖 iptables 和 ipvs 模块这些在 7.9 默认内核里都已经具备。另一点是系统架构资源包里的 keadm-v1.13.1-linux-amd64.tar.gz 是 amd64 版本如果你用 arm64 的机器跑整个流程走不通。2.2 系统初始化swap 关闭与内核模块加载不论云端节点还是边缘节点K8S 集群的底座要先立住。以下命令在两台节点上都要执行# 关闭 swapkubelet 默认要求 swap 必须关闭否则启动直接失败 swapoff -a sed -i / swap / s/^/#/ /etc/fstab # 加载 Kubernetes 依赖的内核模块 cat EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter # 配置 ipvs 所需的系统参数 cat EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables 1 net.bridge.bridge-nf-call-ip6tables 1 net.ipv4.ip_forward 1 EOF sudo sysctl --systemswapoff -a只是临时关闭重启后失效所以要用sed -i把/etc/fstab里的 swap 行注释掉这是 K8S 安装里最常见的遗漏点。内核模块里br_netfilter必须存在否则流量经过 bridge 时 iptables 规则不生效Service 的转发链路是断的。net.ipv4.ip_forward这个参数在边缘节点尤其重要因为 edgecore 的流量转发依赖它。注意一点如果机器以前装过 Docker 或者 K8S 组件先清理干净kubeadm reset加rm -rf /etc/kubernetes是常规操作否则初始化时会遇到端口占用和证书路径冲突的问题浪费半小时排查不如一开始就清场。2.3 镜像批量导入六个 tar 包分别落在哪些节点资源包里的镜像 tarball 直接对应部署流程的各个阶段导入之前先看清各自的用途镜像包用途目标节点image.tar基础镜像集合pause 等 K8S 系统镜像云端节点kubeedge.tarKubeEdge 相关组件镜像云端节点coreedge.tarcloudcore 运行所需的镜像云端节点metallb_image.tarMetalLB 控制器和 speaker 镜像云端节点metrics-server.tar指标采集组件镜像云端节点nginx.tar测试业务镜像云端节点或边缘节点均可# 云端节点上批量导入所有镜像 cd /root/kubeedge-images for i in *.tar; do docker load -i $i; done # 导入完成后检查镜像列表确认关键镜像已经存在 docker images | grep -E kubeedge|metallb|nginx|metricsdocker load的循环逻辑很直接但有一个隐性问题镜像 tarball 里保存的是原始仓库地址和 tag如果你的环境需要推送到私有仓库必须先docker tag再push。资源包默认场景是离线环境直接 load 到 Docker 里用不需要额外 push。docker images | grep这一步建议不要跳过我遇到过 tar 包拷贝过程中损坏的情况docker load不报错但镜像列表里找不到后续 pod 拉镜像时全是 ImagePullBackOff。云端节点和边缘节点的镜像需求不同。云端节点需要 cloudcore 相关镜像边缘节点其实只需要 edgecore 的二进制和配置不需要跑容器镜像。所以严格来说这份资源包里的镜像 tarball 主要面向云端节点边缘侧只需要keadm join生成的 edgecore 服务即可。3. kubeadm 建集群 keadm 部署 KubeEdge从初始化到边缘节点注册3.1 kubeadm 初始化云端节点Pod 网段和 Service 网段不能含糊K8S 集群初始化是整套流程的地基kubeadm init的参数直接决定后续 Calico 和 MetalLB 的配置方式# 云端节点 IP 假设是 192.168.10.10按实际环境替换 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/12 \ --image-repository registry.cn-hangzhou.aliyuncs.com/google_containers mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config--pod-network-cidr 10.244.0.0/16是给 Calico 用的网段--service-cidr 10.96.0.0/12决定 Service 的虚拟 IP 范围MetalLB 分配的负载均衡 IP 不能落在这个 CIDR 里否则会跟 Service 网段冲突。--image-repository指向国内镜像源因为默认的 k8s.gcr.io 在多数网络环境里拉不动。如果你是完全离线环境直接用前面导入的 image.tar 里的镜像即可去掉这个参数让 kubeadm 从本地 Docker 拉取。初始化完成后部署 Calico 网络插件。资源包里的 calico.yaml 是配套版本直接应用kubectl apply -f calico.yamlCalico 的 pod 启动后检查kubectl get pods -n kube-systemcalico-node 进入 Running 状态才算网络就绪。如果卡在 Init 阶段看日志里是否报bird启动失败通常是 pod 网段和节点 IP 冲突改 calico.yaml 里的 IP 池配置即可。3.2 keadm init 部署 cloudcoreadvertise-address 决定边缘端访问入口KubeEdge 的云端组件是 cloudcore用 keadm 工具初始化# 解压 keadm 工具 tar -zxvf keadm-v1.13.1-linux-amd64.tar.gz cd keadm-v1.13.1-linux-amd64 cp keadm /usr/local/bin/ # 初始化 cloudcore指定云端节点 IP 作为边缘端连接入口 keadm init \ --advertise-address 192.168.10.10 \ --kubeedge-version v1.13.1 \ --set cloudcore.modules.cloudHub.https.enablefalse--advertise-address是边缘节点能访问到的云端地址必须填写云端节点的内网 IP 或者域名不能写成 127.0.0.1。边缘节点通过这个地址连接 cloudcore 的 WebSocket 端口。--kubeedge-version指定版本keadm 会从官方源拉取对应组件离线环境下需要提前把 KubeEdge 的镜像和二进制放到指定路径。--set cloudcore.modules.cloudHub.https.enablefalse是让 cloudhub 用 HTTP 模式内网环境不需要 TLS 加密边缘端 join 时也不用配证书。初始化后验证 cloudcore 运行状态systemctl status cloudcore kubectl get pods -n kubeedgecloudcore 正常启动后kubectl get pods -n kubeedge应该看到 cloudcore 和边缘控制器相关的 pod 处于 Running。然后获取边缘节点加入时需要的 tokenkeadm gettoken3.3 keadm join 边缘节点token 和 cloudcore-ipport 是唯二入口边缘节点上只需要安装 Docker 和 KubeEdge 的 edgecore不需要跑 k8s 组件。这里有一个常见的理解偏差边缘节点不是 K8S 集群的 node不能用kubeadm join而是用keadm join把自己的 edgecore 注册到 cloudcore# 在边缘节点执行192.168.10.20 是边缘节点自身 IP keadm join \ --cloudcore-ipport192.168.10.10:10000 \ --tokenxxxxx \ --kubeedge-versionv1.13.1 \ --cgroupdriversystemd--cloudcore-ipport的端口 10000 是 cloudhub 默认监听端口必须和 cloudcore 配置一致。--token用上一步keadm gettoken的输出。--cgroupdriversystemd这个参数要特别注意Docker 的 cgroup 驱动如果也是 systemd这里就匹配如果 Docker 用的是 cgroupfsedgecore 启动时会报 cgroup 驱动不匹配解决办法是统一改为 systemd。验证边缘节点是否注册成功回到云端节点执行kubectl get nodes看到边缘节点的状态是 Ready并且角色显示为空或者边缘标记说明 KubeEdge 的部署链路已经通了。如果一直是 NotReady先看边缘节点的 edgecore 日志journalctl -u edgecore -f4. MetalLB 负载均衡器L2 模式和 IP 池配置是关键4.1 为什么边缘环境需要自建的 LoadBalancerK8S 的 Service 类型里LoadBalancer 描述的是由云厂商负载均衡器提供外部 IP。在裸机或者边缘机房环境没有云厂商的 LB 组件这个类型的 Service 就会一直 Pending。MetalLB 就是来解决这个问题的它用标准路由协议让裸机集群里的 LoadBalancer 类型服务能拿到外部 IP。这份资源包里搭载 MetalLB用意就是把边缘端业务从 NodePort 的端口限制里解放出来。MetalLB 有两种模式BGP 和 L2。边缘计算场景基本选 L2 模式原因有两个一是 L2 模式不需要交换机支持 BGP 协议普通二层网络就能跑二是它直接用 ARP 应答对外宣告 IP配置简单适合小规模集群。BGP 模式需要网络设备配合做路由发布在边缘侧很多时候没有这个条件。资源包里的 l2-forward.yaml 就是为 L2 模式准备的后面会细说。4.2 部署 MetalLB 组件两个 yaml 文件的分工MetalLB 部署分两步先装 CRD 和控制器再建 IP 池# 第一步部署 MetalLB 控制器、speaker 和 CRD kubectl apply -f metallb-native.yaml # 确认 MetalLB 组件运行状态 kubectl get pods -n metallb-system部署后能看到 metallb-controller 和 metallb-speaker 两类 pod。controller 负责 IP 分配speaker 负责对外宣告。如果在边缘节点上也跑了自己的业务 podspeaker 的 pod 数量会跟随节点数变化。当前版本的 MetalLB 使用 CRD 管理 IP 池所以第二步是创建 IPAddressPool。资源包里的 first-ipaddresspool.yaml 就是干这个的kubectl apply -f first-ipaddresspool.yaml看一下这个 yaml 内容apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: first-ipaddresspool namespace: metallb-system spec: addresses: - 192.168.10.200-192.168.10.210 autoAssign: truespec.addresses定义了 MetalLB 可以分配的 IP 范围这里要注意三个约束不能在 K8S 的 service-cidr 内不能和节点 IP 冲突必须和云节点在同一二层网络内否则 ARP 通告跨不了网段。autoAssign: true表示只要创建 LoadBalancer 类型的 ServiceMetalLB 就自动从池里分配 IP。如果你希望某些服务不自动分配可以设为 false 并通过 annotation 手动指定。4.3 L2 转发配置二层通告的最后一个拼图资源包里有一个独立的 l2-forward.yaml这个文件解决的是 L2 模式下跨节点转发的问题。MetalLB 的 L2 模式默认由 Speaker 响应 ARP 请求但如果边缘节点和云端节点的二层网络之间存在隔离或者业务 pod 与负载均衡 IP 不在同一节点上流量就会断。kubectl apply -f l2-forward.yaml这里我按常见的 L2 网络模型解释edgecore 节点的数据面流量通常走 flannel 或 calico 的 VXLAN而 MetalLB 的 L2 通告是宿主机的二层网络。l2-forward 配置一套规则让宿主机接收到的访问流量正确转发到对应的 pod 网络命名空间里。如果你的环境是 single-node 测试流量直达同一个节点这个配置可能不生效也不影响多节点环境下没它一定会遇到 IP 能 ping 通但业务端口不通的问题。4.4 负载均衡测试nginx 服务拿到 EXTERNAL-IP 才算成功环境部署完用资源包里的 nginx 镜像和 nginx-loadbalancer.yaml 做端到端验证# 部署 nginx 测试实例 kubectl apply -f nginx.yaml # 用 LoadBalancer 类型暴露服务 kubectl apply -f nginx-loadbalancer.yaml然后查看服务状态kubectl get svc nginx-lb正常的输出里 EXTERNAL-IP 栏会显示一个 192.168.10.200 到 .210 之间的 IP如果一直是 Pending说明 MetalLB 没有正常工作。拿到了 IP 之后用 curl 验证业务访问curl http://192.168.10.200能返回 nginx 的欢迎页说明从外部访问到边缘业务 pod的链路已经完全打通。这一步是整个部署流程的高光时刻前面所有配置——K8S 集群、KubeEdge 注册、MetalLB 部署——都是为这一下通的。5. 避坑记录五个常见问题与排查路径5.1 边缘节点 NotReadyedgecore 日志报 cgroup 驱动不匹配现象keadm join执行成功但云端kubectl get nodes看到边缘节点一直是 NotReadyjournalctl -u edgecore日志里出现failed to create new Cgroup manager或者 kubelet 启动报错的输出。原因Docker 的 cgroup 驱动是 cgroupfs而 edgecore 启动时指定了 systemd两者的驱动不一致kubelet 无法初始化。解决在边缘节点上执行docker info | grep Cgroup Driver查看 Docker 的驱动类型确认后重新执行keadm join把--cgroupdriver参数改成和 Docker 一致然后systemctl restart edgecore。两个驱动其实都能用关键是统一。5.2 MetalLB 控制器正常但 EXTERNAL-IP 一直 Pending现象metallb-system命名空间下的 pod 都是 Running但创建的 LoadBalancer 类型服务 EXTERNAL-IP 显示pending超过五分钟。原因最常见的是 IPAddressPool 没有生效。检查kubectl get ipaddresspool如果资源不存在说明 first-ipaddresspool.yaml 没有正确应用。另一个可能是 IP 池范围和节点不在同一网段MetalLB 的 controller 在分配前会校验可用性。解决先确认 IPAddressPool 存在且状态正常再看定义的范围是否正确。测试环境下我习惯把地址范围放宽到 192.168.10.200-192.168.10.250避开 DHCP 分配段即可。5.3 负载均衡 IP 能 ping 通但业务端口不通现象curl http://192.168.10.200超时但 ping 这个 IP 是通的说明二层 ARP 已经应答成功数据链路是通的问题出在上层转发。原因流量到达宿主机后没有被转发到对应的 pod 网络命名空间。这就是 l2-forward 配置缺失或应用顺序不对的表现。MetalLB 的 L2 模式本身只解决 ARP 应答不负责把流量送进 pod 网络这一步要靠配套的转发规则完成。解决确认 l2-forward.yaml 已经kubectl apply并且 service 的externalTrafficPolicy: Cluster没有被显式改成 Local。如果改成了 Local流量只会被转发到 pod 所在的节点其他节点上的访问就会失败。边缘计算场景下保持默认的 Cluster 策略让 kube-proxy 做跨节点转发。5.4 docker load 镜像后 pod 拉取仍然报 ImagePullBackOff现象docker load -i kubeedge.tar执行成功但 cloudcore 相关 pod 一直处于 ImagePullBackOff 状态事件里显示Failed to pull image kubeedge/cloudcore:v1.13.1或者类似的错误。原因镜像虽然加载到本地了但 pod 定义里引用的镜像仓库地址和本地镜像的 tag 对不上。例如本地镜像叫kubeedge/cloudcore:v1.13.1但 deployment 里写的是docker.io/kubeedge/cloudcore:v1.13.1。K8S 在 imagePullPolicy 为 Always 时会去远程拉拉不到就用现有的。解决这一步我用了个土办法先看kubectl describe pod cloudcore-pod里的镜像地址然后docker tag把本地镜像打成一样的名字再把 deployment 的imagePullPolicy改成IfNotPresent并滚动重启。另外 nginx.tar 导入后也要检查 tag资源包里这几个 tar 包的 tag 命名比较规范离线环境下先docker images确认一遍最稳妥。5.5 keadm 初始化报 token 无效但实际上节还没起来现象keadm gettoken返回一个 tokenkeadm join时报 token 无效或者 join 后边缘节点没有注册上来。原因两种情况。一是 cloudcore 的 cloudhub 没有监听 10000 端口token 对应的服务没有在线。二是防火墙或者 SELinux 拦截了 WebSocket 连接边缘节点连不上 cloudhub触发了 token 校验超时。解决先在云端节点检查ss -lntp | grep 10000确认端口在监听。然后分别在两个节点上执行getenforce查看 SELinux 状态如果是 Enforcing 就临时允许或干脆关闭。防火墙方面systemctl status firewalld如果开着放行 10000 端口边缘节点还要放行 8080 端口给 KubeEdge 的 edgehub 使用。这个环节比较费时间我一般直接写进初始化脚本里避免每次重装都踩一遍。6. 收尾验证把 metrics-server 和负载均衡链路串起来跑一遍6.1 部署 metrics-server边缘节点的指标也能被采集集群搭建完成后kubectl top nodes往往报错metrics not available这是缺少 metrics-server 的表现。资源包里的 metrics-server.tar 和 components.yaml 就是解决这个问题的# 导入 metrics-server 镜像 docker load -i metrics-server.tar # 部署 metrics-server 组件 kubectl apply -f components.yamlcomponents.yaml 里有一个关键参数要改--kubelet-preferred-address-typesInternalIP默认值是InternalIP,ExternalIP,Hostname在边缘节点场景下如果 kubelet 的 Hostname 和节点 IP 无法互相解析会直接采集失败。改成只走 InternalIP 能跳过域名解析这一步。6.2 一套自检命令流部署完成不等于真的通了我习惯按以下顺序做一遍完整自检每一步的输出都盯着看十秒再往下走# 1. 节点和 pod 状态 kubectl get nodes -o wide kubectl get pods -A | grep -E calico|metallb|cloudcore # 2. 指标采集验证 kubectl top nodes # 3. 服务类型和外部 IP 验证 kubectl get svc nginx-lb # 4. 边缘节点上的 edgecore 服务状态 ssh root192.168.10.20 systemctl status edgecore前三步在云端节点执行最后一步登录边缘节点验证。kubectl top nodes能看到 CPU 和内存数据说明 metrics-server 和 KubeEdge 的指标链路没有断。6.3 一个实用技巧把 IP 池的预留段和业务隔离如果这套集群要长期跑业务不要图省事把整个网段丢给 MetalLB。first-ipaddresspool.yaml里我只写了一个窄范围 192.168.10.200-210原因有两个一是避免 DHCP 冲突二是 LoadBalancer 的 IP 本来是稀缺资源窄范围能让你清楚看到哪些 IP 被占用了。业务扩容时再去改 yaml 里的addresses字段kubectl apply滚动生效不需要重启 MetalLB。kubectl get ipaddresspool first-ipaddresspool -o yaml能看到每个 IP 的分配状态排查服务绑定关系时非常有用。从那以后我每次搭完边缘集群都强制走一遍上面那四条自检命令服务暴露链路不通的话问题基本都出在 IP 池范围和防火墙这两个地方。希望这份部署记录能帮你少花几个晚上的排查时间把更多精力留在业务本身。本文还有配套的精品资源点击获取