ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

四块树莓派CM4搭建K3s集群:从刷机到Kafka踩坑全记录

四块树莓派CM4搭建K3s集群:从刷机到Kafka踩坑全记录 四块树莓派CM4插在同一块主板上塞进一个标准ITX机箱组成一个功耗不到50瓦的Kubernetes集群这就是Turing Pi 2给我的第一印象。自从树莓派Compute Module 4发布之后我一直想做一台能放在桌面上随意折腾的集群机器Turing Pi 2恰好是解决这个需求最优雅的方案。这篇文章我会把从选型、刷机、组网到踩坑的完整过程捋一遍包括我在集群里部署Kafka时遇到的那个臭名昭著的报错“client has run out of available brokers to talk to (is your cluster reachable?)”希望帮准备入坑的朋友省掉几下午的排查时间。1. Turing Pi 2到底是什么一块能装七台树莓派的ITX主板1.1 硬件规格与产品定位Turing Pi 2是Turing Machines推出的一款Mini-ITX规格的单板计算机底板核心卖点是支持7个树莓派Compute Module 4模块。你可以把它理解为一块专门为CM4设计的ATX主板——只不过CPU、内存、存储全都集成在这种口香糖大小的模块上插上就能用拔下来就能换。这块板子的关键规格包括标准Mini-ITX尺寸170mm x 170mm能直接装进常规ITX机箱7个CM4插槽兼容eMMC版本和Lite版本2个2.5GbE以太网口板载交换机芯片把它们连成一个内网2个M.2 NVMe插槽可以给集群加持久化存储1个USB-C口用于刷机和串口调试支持12V DC电源和标准ATX电源两种供电方式。为什么要用这种“高密度”的设计因为传统的树莓派集群方案是把N块树莓派通过交换机叠在一起电源线、网线、HDMI线串成一团体积大、连线多、管理混乱。Turing Pi 2把所有CM4集中在一块板子上机箱内部只剩电源线和两根网线清爽很多。板载交换机直接把7个节点的网络打通不用额外接交换机这是它跟“几台树莓派加一个小交换机”最本质的区别。1.2 为什么选择四块CM4而不是七块标题叫“Four Raspberry Pi CM4s”很多人会问既然板子支持7块为什么不一次性插满我在搭这套集群的时候认真算过这笔账。首先是成本。CM4目前的行情并不便宜8GB内存加32GB eMMC的版本价格接近一块中端开发板插满7块光模块成本就很高。四块CM4的预算压力小很多而且对于跑Kubernetes学习、测试微服务、练习DevOps工具链来说一个master加三个worker的拓扑已经完全够用。K8s生产环境一般也是三到五个节点起步四节点集群能覆盖绝大多数实验场景。其次是散热和功耗。CM4满载时的功耗大约在5到8瓦四块模块满载也就是30瓦出头加上主板和周边设备整机功耗可以控制在50瓦以内。一块12V 5A的适配器就能轻松供上。如果插满七块散热压力会成倍增加ITX机箱内的气流组织要做更多功课否则很容易触发CPU降频。四块是一个兼顾功能、成本和散热的黄金配置。最后是从运维角度考虑。四块CM4在一张板子上系统日志、监控面板、节点状态一目了然排错时不用在“物理机A坏了还是交换机端口没插好”之间反复横跳。等以后真有扩展需求再插三块CM4进去把NAT、持久化存储、监控栈单独拆节点无缝升级。2. 装机前的选型与准备CM4版本、电源方案和主动散热2.1 CM4模块怎么选eMMC容量和内存是关键CM4的SKU很多选型错误会直接影响集群体验。我在第一套方案里差点买了2GB内存的Lite版本后来复盘时发现这会是个大坑。内存容量。Kubernetes的kubelet、containerd、kube-proxy这些核心组件运行起来空闲状态内存占用接近1GB。跑一个简单的Spring Boot应用加上Java的堆内存2GB的机器立刻捉襟见肘。Kafka这类消息中间件更是内存大户4GB内存跑单节点Kafka都比较紧张。我最终选择的是8GB内存版本四个节点加起来32GB跑K8s之外的中间件集群也有余量。如果预算有限至少选4GB2GB只适合做纯网络实验或者轻量容器测试。eMMC容量。Lite版没有板载存储需要外接TF卡或NVMe反而增加了流程复杂度。我选的32GB eMMC版本烧录完Ubuntu Server 22.04之后系统占用大概5GB剩下20多GB可以放容器镜像、日志和测试数据。16GB版本也能用但跑Kafka、时序数据库这类写大量数据的应用会很快吃满磁盘日志轮转和镜像占用的腾挪空间非常小。无线模块。集群节点之间走的是板载有线网络节点访问外网也是通过以太网上行口WiFi完全用不上。所以我买的是不带WiFi/蓝牙的版本既能省掉一点点功耗也少了一个无线干扰的变量。2.2 电源方案适配器功率怎么计算Turing Pi 2有两种供电方式DC口的12V输入和标准ATX 24pin接口。我用的DC供电配官方推荐规格的12V 5A适配器理论最大功率60瓦。为什么5A够用我们可以简单估算。四块CM4满载功耗约32瓦M.2 NVMe硬盘读写时大约5瓦主板自身的交换芯片、风扇、USB设备合计约5瓦总负载在40瓦左右。12V 5A适配器的60瓦功率留出了50%的冗余足够覆盖瞬时尖峰。如果插满七块CM4或者挂多块NVMe我建议直接上12V 8A以上的适配器或者改用ATX电源供电功率余量会大很多。实际使用中我也验证过四节点集群跑一个持续2小时的压力测试电源适配器温热但不烫手电压稳定在12V附近没有出现重启或节点掉线。这里有一个经验——务必选择正品电源适配器杂牌电源在瞬时负载升高时电压跌落明显CM4对供电质量很敏感容易诱发莫名其妙的死机。2.3 散热方案被动散热片加机箱风扇是底线CM4模块和普通树莓派4B的最大区别之一是没有主动散热结构必须靠外加散热片或风扇。Turing Pi 2上四块CM4之间的距离很近如果只用小散热片而不做机箱风道热量会在主板内部累积。我在装机时给每块CM4配了铝合金散热片尺寸大概25x25x10mm用导热垫贴紧处理器表面。机箱后部装了一个12cm静音风扇向外部排风。实测待机时CM4温度在45度左右满载时稳定在70度上下没有触发80度降频阈值。如果机箱是闷罐式的或者风扇安装方向反了温度会显著上升跑几分钟负载就会降频到1.5GHz以下。可以用vcgencmd measure_temp实时查看每个节点的温度watch -n 2 vcgencmd measure_temp3. 系统烧录与首次启动把Ubuntu塞进四块eMMC3.1 进入刷机模式Turing Pi 2给每个CM4烧录系统的方式和树莓派原生的rpiboot工具链一致。先把主板断电确认所有CM4模块已经插牢然后把板上的DIP开关拨到刷机位置。接着用USB-C线连接电脑和主板的USB-C口再给主板上电。此时CM4会进入USB Mass Storage模式电脑上会看到四个U盘设备分别对应四块CM4的eMMC。这里有一个非常容易踩的坑USB-C线必须支持数据传输。我一开始随手拿了一根手机充电线电脑完全识别不到设备折腾了半小时才意识到是线的问题。建议直接使用包装里附带的USB-C线或者用你确信能传数据的线。在电脑端需要安装rpiboot工具。从树莓派官方仓库拉取编译git clone --depth1 https://github.com/raspberrypi/usbboot cd usbboot sudo apt install libusb-1.0-0-dev make sudo ./rpiboot执行之后rpiboot会在主机端把树莓派引导固件加载到CM4上随后eMMC设备就会出现在/dev/sd*列表里。可以用lsblk确认四个设备的名称和容量。3.2 烧录Ubuntu Server镜像我选择的是Ubuntu Server 22.04 LTS选它的理由是K8s、Docker、K3s以及绝大多数云原生工具链对Ubuntu的兼容性最好很多中间件的安装文档默认就是Ubuntu系统。烧录工具用Raspberry Pi Imager或者dd命令都行但针对四块eMMC同时烧录我推荐先用Imager把系统盘准备好再逐个写入。Imager在烧录时支持预配置SSH、无线网络和用户名密码。不过由于集群网络环境和我规划的静态IP不同我这里选择先烧一个干净系统再进入系统配置静态网络。写入命令很简单例如sudo dd ifubuntu-22.04.4-preinstalled-server-arm64raspi.img of/dev/sda bs4M statusprogress重复执行四次分别写入四个eMMC设备。需要注意的是写入目标一定要确认清楚比如/dev/sda、/dev/sdb、/dev/sdc、/dev/sdd千万别写错否则电脑上的硬盘数据就没了。写上后会有几秒明显的写入速度下降属正常现象。烧录完成之后断电把DIP开关拨回到正常启动模式接上网线和电源系统会开始首次启动并自动扩展根分区。3.3 首次启动后的基础配置四块CM4首启后从路由器后台能看到它们找DHCP分配的临时IP。我习惯用nmap扫一下局域网nmap -sP 192.168.1.0/24然后逐个SSH登录。这里由于四块板子默认主机名不同需要先记住哪块是master哪块是worker。最简单的办法是给每块CM4贴标签按插槽位置命名板子上的插槽编号可以对应到具体设备。Turing Pi 2四块CM4的USB Mass Storage顺序基本上是确定的烧录时我是按插槽顺序依次写入所以启动后也能区分。SSH登录之后第一步是修改主机名和配置静态IP。主机名在/etc/hostname和/etc/hosts里改sudo hostnamectl set-hostname k8s-master-1静态IP用Netplan配置。编辑/etc/netplan/50-cloud-init.yamlnetwork: version: 2 ethernets: eth0: dhcp4: false addresses: - 192.168.50.101/24 routes: - to: default via: 192.168.50.1 nameservers: addresses: - 192.168.50.1 - 223.5.5.5192.168.50.101给master其余三块节点分别用.102、.103、.104。192.168.50.1是我的路由器地址DNS根据需要填写。应用配置sudo netplan apply4. 组网与K3s集群搭建四块CM4跑起来4.1 网络拓扑和主机名解析Turing Pi 2板载交换机把所有CM4节点连接成一个二层网络两个2.5G以太网口可以理解为交换机的上行口。我实际连接方式是一个网口接家里路由器LAN口另一个网口暂时空着未来可以接第二路网络或做链路聚合。节点之间的内网通信非常快2.5Gbps的带宽在传输容器镜像、日志聚合时优势明显。为了在节点之间互相用主机名访问我在每台机器的/etc/hosts里写入了四行解析192.168.50.101 k8s-master-1 192.168.50.102 k8s-node-1 192.168.50.103 k8s-node-2 192.168.50.104 k8s-node-3这个配置看似基础但很多人会忽略。后续跑Kafka或者其他分布式服务时如果服务间互相用主机名通信而/etc/hosts没有解析就会出现无法连接的问题。后面我会细讲那个“client has run out of available brokers”的报错本质上就跟这类问题密切相关。4.2 SSH免密登录四台机器都要频繁登录每次输密码很烦。我在master上生成密钥并分发到三个workerssh-keygen -t rsa -b 4096 ssh-copy-id k8s-node-1 ssh-copy-id k8s-node-2 ssh-copy-id k8s-node-3分发完成后从master SSH到任意节点都不需要密码后续执行批量命令可以配合pssh或者简单的for循环。比如批量查看温度for host in k8s-node-1 k8s-node-2 k8s-node-3; do echo $host ssh $host vcgencmd measure_temp done4.3 K3s安装步骤Kubernetes全量版对树莓派这种资源有限的机器来说太沉重K3s是专为边缘和资源受限场景设计的轻量K8s发行版本质上是把K8s的核心组件打包成单个二进制加上默认的containerd运行时和轻量级kube-proxy内存占用比完整版低一半以上。在CM4集群上跑K3s是主流选择。master上执行curl -sfL https://get.k3s.io | K3S_KUBECONFIG_MODE644 sh -安装完成后会输出一个K3S_TOKEN同时kubectl配置文件在/etc/rancher/k3s/k3s.yaml。worker节点加入集群需要用这个tokencurl -sfL https://get.k3s.io | K3S_URLhttps://192.168.50.101:6443 K3S_TOKENtoken sh -在三台worker上依次执行然后在master上验证kubectl get nodes看到四个节点加上Ready状态说明集群已经搭建成功。整个过程不到十分钟。K3s默认会部署几个系统组件包括coredns、traefik ingress controller等可以用kubectl get pods -A查看。4.4 在集群里跑一个单节点Kafka验证网络集群建好之后我第一个想部署的是Kafka因为消息队列是分布式系统的核心组件也最容易暴露出网络配置问题。我直接在其中一个节点上用Docker运行单节点Kafka顺便验证集群内部的网络连通性。这里用一个简化版KRaft模式单节点Kafka服务docker run -d --name kafka \ --restart unless-stopped \ -p 9092:9092 \ -e KAFKA_CFG_NODE_ID1 \ -e KAFKA_CFG_PROCESS_ROLESbroker,controller \ -e KAFKA_CFG_LISTENERSPLAINTEXT://:9092,CONTROLLER://:9093 \ -e KAFKA_CFG_ADVERTISED_LISTENERSPLAINTEXT://192.168.50.102:9092 \ -e KAFKA_CFG_CONTROLLER_LISTENER_NAMESCONTROLLER \ -e KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAPCONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT \ -e KAFKA_CFG_CONTROLLER_QUORUM_VOTERS1192.168.50.102:9093 \ bitnami/kafka:latest这里有个关键点KAFKA_CFG_ADVERTISED_LISTENERS我特意填了节点实际IP192.168.50.102而不是容器ID或主机名。如果你填kafka-node:9092而客户端所在机器没有这个主机名的解析就会遇到经典的broker连接超时。我在第一次部署时就踩了这个坑第一次填的是容器名kafka结果宿主机上的客户端根本解析不了直接导致“client has run out of available brokers”报错。这个错误信息在Kafka 3.x版本里非常常见下文排查章节会专门展开。5. 踩坑实录那些让人头皮发麻的问题5.1 “client has run out of available brokers to talk to”深度排查这个报错是Kafka客户端在连接broker时最经典的故障之一完整信息是client has run out of available brokers to talk to (is your cluster reachable?)通俗解释Kafka客户端通过一个初始bootstrap-server列表拿到broker地址然后根据这些地址去建连。如果所有地址都无法建立TCP连接客户端就会抛出这个错误。它背后通常有四个原因网络不通。节点IP变了或者服务没监听。排查第一步是确认broker进程还活着监听端口正常ss -lntp | grep 9092如果监听正常再从客户端机器用nc测试端口nc -vz 192.168.50.102 9092这个命令验证的是二层网络和端口可达性排除防火墙和底层网络问题。如果nc显示连接成功问题一定出在上层的broker配置。advertised.listeners配置错误。这是最隐蔽也最常见的问题。broker启动时会把advertised.listeners告诉客户端客户端用它来建立后续连接。如果你在容器里跑Kafka把advertised.listeners填成了容器IP或容器名而客户端在宿主机或者另一个节点上它就拿到一个无法路由的地址。结果是bootstrap-server连上了但真正的数据连接连不上。我在4.4节就是踩了这个坑填了主机名kafka宿主机没有这个解析客户端一直报“out of available brokers”。解决办法很简单要么在/etc/hosts里加一行192.168.50.102 kafka要么直接把advertised.listeners改成实际IP。DNS解析失败。如果你在多个节点上部署Kafka并且broker之间、broker与客户端之间都通过主机名互相访问那么每个节点的/etc/hosts必须一致。少了一个节点解析该节点的broker就不可达。我曾遇到master能连node1但node2、node3连不上最后发现是node2、node3上的hosts文件没有同步。防火墙拦截。Ubuntu默认不开防火墙但如果你之前开过ufw需要放行Kafka端口sudo ufw allow 9092/tcp在K3s集群里还要注意如果Kafka被编排成Pod并通过NodePort暴露客户端需要连接的是宿主机的NodePort端口而不是容器内的9092。这种情况下的advertised.listeners要写宿主机IP加NodePort端口。资源耗尽导致broker进程被OOM。CM4如果是2GB内存跑Kafka非常勉强kafka进程很容易被Linux OOM killer干掉。一旦进程被kill客户端所有连接都会中断然后持续报这个错。排查方法dmesg | grep -i out of memory journalctl -u docker --since today | grep -i killed如果确认是OOM就只能加内存、限制堆内存大小或者换更轻量的队列实现。我后来给Kafka进程设置了KAFKA_HEAP_OPTS-Xmx512m -Xms256m把堆内存限制在512MB4GB的节点才稳定运行。上面这个排查过程我整理成了速查表排查方向命令/检查点结论broker进程docker ps/systemctl status kafka进程是否存活端口监听ss -lntp | grep 9092是否监听在0.0.0.0或正确IP上网络连通性nc -vz 192.168.50.102 9092是否TCP可达advertised配置broker配置文件中的listeners项是否填了可解析的IP/主机名DNS解析/etc/hosts和nslookup主机名与IP是否一致防火墙ufw status/iptables -L是否放行9092端口内存资源free -h/ dmesggrep -i oom5.2 eMMC刷写失败与USB-C识别不到烧录阶段最容易遇到的问题是电脑识别不到CM4的eMMC。我复盘了多次刷机流程主要问题有三个。USB-C线只支持充电。这是最大的坑很多看起来一样的线是有区别的换了正品数据线之后一次通过。如果手头有多根线逐个试是最快的办法。rpiboot版本过旧。旧版本对CM4的ID代码识别不全会卡在“Waiting for BCM2711”这个状态。建议直接拉最新源码重新编译问题通常会消失。DIP开关位置不对。Turing Pi 2进入刷机模式需要把DIP开关拨到USB boot位置不同批次的主板DIP开关标识可能略有差异以官方文档为准。我第一次因为没仔细看文档DIP开关还留在正常启动模式系统也进入不了Mass Storage自然识别不到。刷机过程中如果出现写入超时大概率是线材质量导致USB传输不稳定换线重刷即可。eMMC芯片本身有擦写保护刷到一半失败不会变砖重新来过就好。5.3 温度与功耗的实测心得四块CM4在K3s集群长时间运行的温度情况我之前有完整记录。装好散热片但机箱风扇朝机箱里吹的情况下空闲温度约50度跑容器构建任务时飙到82度明显触发了降频。后来我把风扇方向改成向外抽风让冷空气从机箱前面板缝隙进入满载温度降到72度左右。功耗方面用一个功率计插座实测完全空闲整机约18瓦四节点K3s跑满负载整机约42瓦加上一块NVMe持续读写整机约48瓦。这个功耗水平意味着可以24小时开着一个月电费也就十几块钱比租云服务器便宜得多而且完全可控。我在实际使用中还会周期性看温度ssh k8s-master-1 vcgencmd measure_temp如果温度超过78度我会检查机箱灰尘和风扇转速提前干预。CM4长期高温运行不仅会降频还会加快散热硅脂老化影响模块寿命。6. 这套集群还能怎么玩从K3s到边缘计算集群搭好之后很多玩法会自然浮现。因为四个节点是独立的机器你可以把常见的云原生组件全部部署上去Prometheus监控全家桶、Grafana仪表盘、Nacos注册中心、Redis集群、MinIO对象存储甚至可以用KubeVirt在上面跑虚拟化实验。Turing Pi 2的M.2插槽可以插两块NVMe配合Rancher Longhorn实现K8s原生持久化存储数据不会因为节点重启就丢。我目前在这套集群上跑了一个包含前端、后端、数据库、消息队列的完整业务演示系统四个节点划分是master跑控制面加一个轻量网关node1跑后端服务和MySQLnode2跑Kafka和日志采集node3跑前端静态文件加监控。资源利用率大约在60%左右还有余量做CI流水线测试。如果后续想扩展Turing Pi 2还可以再插三块CM4把监控、日志、存储分别拆到独立节点配合主板的2.5G交换机整体体验会更好。写到这里我最想强调的还是那句话集群的价值不在于你有多少块板子而在于你能把这些板子用起来并且理解背后的原理。Turing Pi 2只是把物理层的基础打好了真正考验人的是网络、存储、编排这些软件层面的功夫。这次搭建过程中踩过的每一个坑尤其是Kafka那个“client has run out of available brokers”的报错都让我对分布式系统又多了一分敬畏。希望这篇记录能帮你在入坑的道路上少走几步弯路如果后续有更深的玩法我再回来更新。
返回列表