ARTICLE DETAIL

资讯详情

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

Cloudflare OS深度解析:边缘Linux系统定制与性能优化实践

Cloudflare OS深度解析:边缘Linux系统定制与性能优化实践 聊cloudflare-os这个标题的多半是在追 Cloudflare 边缘基础设施怎么做的人。这个说法严格讲不是 Cloudflare 官方给某个操作系统起的正式名字圈子里更多是拿来指代他们跑在全球边缘节点上的那套深度定制 Linux 环境。Cloudflare 没有把整套系统完整开源但通过工程博客和 GitHub 上的公开仓库陆陆续续透露了大量设计思路和落地细节。对我来说这玩意儿与其说是一个系统不如说是一套把规模、性能、安全三个词同时压到极致时的参考答案。这篇文章想做的就是把这套思路拆开梳理清楚设计逻辑再给出一套能在本地复现出类 CFOS效果的具体做法。适合对 CDN、边缘节点、Linux 定制化有兴趣的运维和平台工程师。1. 先搞清楚cloudflare-os 到底是什么1.1 它不是官方系统却比官方系统更值得研究Cloudflare 在全球有数百个数据中心每个节点上跑的是一套定制 Linux。这套系统没有以cloudflare-os的名义公开发布过圈内这么叫纯粹是因为它太有自己的风格了。他们曾经公开过自己在 CentOS 基础上的长期实践也公开过迁移到其他发行版家族的路线图同时维护着包含内核配置在内的公开仓库。把这些信息拼起来你能看到一个非常清晰的轮廓基于 Linux 内核、采用 RPM 体系、内部有完整的构建与签名管道、系统层做了大量精简和加固。这套东西最妙的地方在于它不是实验室产物而是在每秒处理数千万请求的规模下被反复锤炼过的。你在通用发行版上纠结这个内核参数要不要调这个系统服务能不能砍他们是被流量逼着必须做出取舍。所以研究 cloudflare-os 的思路本质上是在研究一套经过生产环境极限检验的 Linux 服务器基线。1.2 为什么一个边缘节点要自己做操作系统你可能想问直接用 Ubuntu、Debian、CentOS 不好吗答案是在超大规模集群里通用发行版有太多你不需要的东西。每多一个系统服务就多一份出问题的概率。一个只做转发和缓存的边缘节点不需要桌面环境、不需要打印机驱动、不需要一堆固件和硬件探测工具。这些多余组件不仅占磁盘、占内存更重要的是扩大了攻击面。Cloudflare 选择自研系统核心诉求有三个把镜像缩到最小、把行为控到最严、把性能压到最高。另一个重要原因是标准化。几千台服务器如果靠人手去配早晚跑偏。如果所有节点都从同一个构建管道产生、用同一个签名机制校验、用同一套参数启动那么系统漂移问题就不存在了。节点出问题重装镜像就行而不是花半天去排查某台机器被谁改了什么配置。这就是不可变基础设施的思想系统是一个整体镜像而不是一堆可变的文件集合。1.3 看它之前先对齐三个基础知识要理解后文的内容需要先确认我们对几个基础概念的理解是一致的。引导流程服务器通电后主板固件加载引导程序引导程序加载内核到内存内核挂载根文件系统然后执行 PID 1通常是 systemd。这个链条上的每一步都可以定制。initramfs内核启动初期需要的一个临时根文件系统里面包含挂载真正根文件系统所需的驱动和工具。定制系统时通常会把 initramfs 压到很小甚至把根文件系统做成 initramfs 的一部分。eBPF/XDP内核里的一种机制允许你在不修改内核源码的情况下在数据包处理路径上执行自定义程序。XDP 是网卡驱动层最早期的钩子专门用来做高性能包处理。Cloudflare 的 DDoS 防护大量依赖 XDP 和 eBPF比如在丢包、限速、特征过滤这些高频场景里它们比传统的 iptables 高效得多。这三个概念是后面讨论的基础。如果你还不太熟建议先把它们对应的基础知识过一遍再往下看效果会好很多。2. 核心设计思路拆解三大原则2.1 最小化少一个组件就少一类问题最小化这条原则执行到什么程度Cloudflare 的做法是系统里只保留运行边缘服务所必需的东西内核、基础库、运行时、配置工具其余一律不装。极端的例子是启动流程。常规发行版从 BIOS 到内核再挂载根文件系统中间经过 GRUB 等多个环节每个环节都可能出问题。Cloudflare 在很多代际的硬件上做过简化把引导阶段需要的东西尽量内聚减少对外部固件和探测逻辑的依赖。通用发行版启动时可能要跑几十个系统服务而定制系统可以把启动服务压缩到个位数。这个思路落到我们的复现里就是能用 busybox 的不用 coreutils 全套能一个二进制解决的不装一个包含大量 man 页面和文档的软件包。做镜像时用dnf install --setopttsflagsnodocs这类参数关闭文档安装再把/usr/share/doc、/usr/share/man直接清理掉。这些操作看起来不起眼但累计起来能把镜像体积砍掉三到五成。2.2 不可变与可复现系统是构建出来的不是配置出来的传统的服务器管理是装好系统然后 SSH 上去改配置。这种方式在几台机器上没问题在上千台节点上就是灾难这台改了内核参数、那台多装了软件、另一台配置文件被手误改坏最后每台机器都长成了独一无二的雪花。Cloudflare 的思路反过来整个系统是一个经过签名校验的镜像由构建管道自动产出节点启动时只从镜像拉取运行中不允许随意修改系统分区。修改配置的唯一方式是重新构建镜像、走完测试和签名流程再发布到节点。这样做的收益是巨大的所有节点行为一致问题可复现任何节点都可以随时销毁重建不用担心丢失状态安全基线统一不需要逐个机器检查。复现这套思路可以在构建阶段用rpmbuild或linuxkit这类工具把定制内容打进镜像再用ostree或类似的镜像管理工具做版本化。没有这么重的需求时也可以用 Packer 做好基础镜像 Ansible 做初始化把最终产物固化成模板禁止运行时改动。2.3 网络性能优先一切服务都为流量让路如果一个系统的主要职责是处理网络数据那设计时就必须围绕网络性能来做取舍。Cloudflare 的很多技术都是从这里长出来的。举个例子传统 Linux 收包路径是网卡收到数据 - 硬件中断 - 内核协议栈 - socket - 应用程序。这个路径上有大量拷贝和上下文切换。Cloudflare 用 XDP 把过滤器直接挂到网卡驱动层数据包到达时第一时间决定丢弃、转发还是上送。这种机制在 DDoS 攻击场景下尤其关键攻击流量可能在靠近硬件的层面就直接被丢掉根本不会进入内核协议栈和应用层。除了 XDP还有几件事是必然要做的CPU 调优把中断和网络处理核心隔离出来、内存调优加大网卡环形缓冲区、调整 TCP 缓冲区、拥塞控制算法选择不同算法在不同网络条件下差异很大。这些技术细节在第 4 节会给出具体的参数建议。3. 本地复现一个类 CFOS的边缘系统到这一步我们要动手了。目标不是复刻真正的 Cloudflare 系统——那既不现实也没必要——而是搭建一个最小化的 Linux 系统思路和 cloudflare-os 同源。我在下面的演示里用 AlmaLinux 作为基础RHEL 系、RPM 生态、长期支持如果你偏好其他发行版原理是通用的。3.1 基础镜像与引导先准备一台虚拟机或物理机磁盘给 10GB 就够。AlmaLinux 有提供 minimal 安装镜像装完后第一件事是确认当前系统里有多少服务在跑systemctl list-unit-files --typeservice --stateenabled你会看到大量默认开启的服务。接下来做最小化裁剪我实际保留的通常是这几个sshd或者干脆不装用串口/带外管理systemd-networkd或NetworkManagerchronyd或systemd-timesyncd时间同步边缘服务自身的守护进程其余服务比如abrtd、cups、avahi-daemon、firewalld如果你用 XDP 做过滤它反而是多余的全部systemctl disable掉。注意最小化不是无脑删。删掉selinux相关的策略包之前先确认你的应用在关闭状态下能正常运行。安全基线不能因为性能优化就完全放弃。裁剪之后可以用systemd-analyze blame查看各服务的启动耗时。我之前在一台机器上从 21 秒压到 9 秒主要就是靠砍服务和关掉不必要的硬件探测。3.2 定制内核性能和安全同时抓Cloudflare 对内核做了大量配置调整并且把配置公开在 GitHub 的cloudflare/linux仓库里。我们不能直接照搬因为那是针对他们硬件和业务场景调出来的但可以参考几个方向关闭不需要的驱动和文件系统缩小内核体积、减少攻击面开启 BPF 相关选项CONFIG_BPF、CONFIG_BPF_JIT、CONFIG_BPF_EVENTS这是 XDP/eBPF 运行的前提网络协议栈相关选项按业务需要调整比如 TCP 拥塞控制在编译期就编入需要的算法如果打算用 XDP要确认网卡驱动支持并开启CONFIG_XDP_SOCKETS。裁剪内核最直观的收益是启动速度模块少了内核加载和设备探测都更快。我见过一些嵌入式项目把启动压缩到 2 秒以内服务器端没必要那么极致但从通用内核换成裁剪内核启动能快三分之一是常态。内核编译过程比较长建议按以下步骤操作# 从 elrepo 或 kernel.org 拉取对应版本源码 # 以当前运行内核的配置为起点 make olddefconfig # 用 menuconfig 逐项调整 make menuconfig # 编译并安装 make -j$(nproc) bzImage modules make modules_install make install编译时注意-j参数不要超过 CPU 线程数的 1.5 倍否则容易因内存不足中断。我当时给虚拟机分配了 4 核 8GB编译一次内核大约 20 分钟还算可以接受。3.3 让系统的分层清晰可复现参考 Cloudflare 对外公开过的构建思路他们用的是分层构建基础系统 - 核心服务 - 业务应用我也建议把镜像构建分成三层每一层单独管理版本。L0 基础层最小化的 AlmaLinux 根文件系统包含内核、基础工具、时区、用户配置不开任何非必要服务。L1 网络层网络栈调优脚本、XDP 程序编译产物、网卡队列配置、防火墙规则。L2 应用层实际的边缘应用二进制和配置文件比如 Nginx、自研的 Go 服务、日志采集器等。每一层构建完都给镜像打上版本标签。发布时先 L0 再 L1 再 L2如果哪一层出了问题可以快速回滚到上一层的稳定镜像。构建产物建议规范命名cfos-l0-3.2.14-20250601.img、cfos-l1-1.4.0-20250601.img。这个习惯能救命的尤其是你同时维护着几十台机器、又分不清哪个镜像对应哪次变更的时候。4. 安全加固与性能调优的关键参数系统层做完真正的难点在运行时调优。这里给出一组我在类似边缘系统上实测过的关键参数以及它们背后的理由。4.1 网络栈调优在/etc/sysctl.d/99-network.conf里我通常会配这么几项net.core.rmem_default 1048576 net.core.rmem_max 16777216 net.core.wmem_default 1048576 net.core.wmem_max 16777216 net.ipv4.tcp_rmem 4096 1048576 16777216 net.ipv4.tcp_wmem 4096 1048576 16777216 net.ipv4.tcp_congestion_control bbr net.core.netdev_budget 600 net.core.netdev_max_backlog 65536这里解释几个关键的。rmem_max和wmem_max调大是为了让高并发连接有足够的内核缓冲。如果缓冲区太小高负载下会出现丢包应用层看到的就是时不时卡一下。netdev_budget控制每次软中断处理的数据包数量太小浪费 CPU太大可能饿死其他任务600 左右是我觉得比较均衡的取值。BBR 拥塞控制算法在跨地域长链路上效果显著尤其是丢包并不剧烈但延迟较高的场景比默认的 cubic 在吞吐上能高出不少。注意不是所有场景都适合 BBR。如果节点只做内网转发、链路极稳cubic 和 bbr 的差别不明显。调优之前先搞清楚你的流量特征。4.2 CPU 与中断隔离服务器上跑网络服务的 CPU最怕的是被各种系统任务打断。Cloudflare 这类公司普遍会做 CPU 隔离把一部分核心专门留给数据面其他核心跑控制面。操作分成两步。第一步在 GRUB 内核命令行里加参数isolcpus2,3 nohz_full2,3 rcu_nocbs2,3isolcpus把 2、3 号核心从通用调度器中移出用户态程序可以绑定到这两个核心上nohz_full关闭这两个核心上的周期性时钟中断降低抖动。第二步把网卡的中断IRQ绑定到别的核心上让数据面核心不被打断# 查看网卡队列和对应的 IRQ cat /proc/interrupts | grep eth0 # 把 IRQ 亲和性绑定到 0、1 号核心 echo 3 /proc/irq/$(irq_number)/smp_affinity这套组合做下来网络应用的 p99 延迟能明显下降。代价是 2、3 号核心不能再跑普通任务CPU 利用率会显得浪费但这种浪费在延迟敏感的 CDN 节点上是值得的。4.3 安全配置与签名机制安全层面有三个点必须做。强制访问控制。SELinux 或 AppArmor 至少保留一个。很多做性能的人第一反应是把 SELinux 关掉理由是麻烦、影响性能。实际上正确配置的 SELinux 对性能损耗很小尤其在只跑固定几个服务的系统上策略写好之后毫无感知。我见过的线上事故里因为关闭 SELinux 导致被提权的案例远比因为开启 SELinux 导致服务失败的案例多。系统签名与完整性校验。Cloudflare 的做法是所有镜像使用签名密钥签名引导时校验。我们用rpm --verify和aide这类工具做定期完整性检查也可以达到类似效果。关键点是校验是定时的、自动化的不是等出了问题才想起来。最小权限。服务全部用独立用户运行不跑在 root 下SSH 只允许密钥登录并禁用 root对外只暴露必要端口其余一律拒绝。这三条老生常谈但在边缘节点上尤其重要因为节点暴露在公网流量下任何多余的服务都是攻击入口。5. 实操踩坑记录四个经典问题复现这套系统我踩过的坑比预想的多。把印象最深的几个列出来都是常规文档不会写的内容。5.1 内核裁剪之后网卡驱动没了有次按cloudflare/linux的思路裁剪内核为了追求小体积把大量驱动编成模块结果启动后网卡不工作一查发现对应驱动模块没加载。原因是裁剪时把驱动相关的固件文件也删了模块加载失败。诊断过程进入紧急模式检查dmesg看到 firmware 加载失败。解决方法是把固件文件单独打包成 RPM 安装回去而不是整个驱动目录塞回去。教训是裁剪驱动的原则应该是把你不需要的明确排除而不是把所有不确定的都放进排除列表。5.2 systemd 服务启动顺序导致应用起不来边缘应用依赖网络就绪但 network 服务在应用之后才启动结果应用初始化网络失败进程反复重启。这个问题出现得比自己预期的多因为系统裁剪后服务依赖关系不如完整发行版清楚。解决方式是在 service 文件里显式声明依赖[Unit] Afternetwork-online.target Requiresnetwork-online.target [Service] Typeoneshot ExecStart/usr/local/bin/cf-edge init RemainAfterExityes同时确保systemd-networkd-wait-online.service是开启的否则network-online.target在极端情况下可能永远不会触发。5.3 XDP 加载不了提示内存不足XDP 程序加载时内核需要为 BPF map 分配连续内存。小内存机器上ulimit -l默认值可能限制了这个过程。解决方法是调大 memlock 限制以及在 sysctl 里配置vm.max_map_count 655360再配合/etc/security/limits.conf里给加载程序的用户设置memlock unlimited。如果还不行检查内核是否开启CONFIG_BPF_JIT没开的话 eBPF 程序只能走解释器路径性能和可加载性都大打折扣。5.4 镜像体积减下来了但启动反而变慢这是个反直觉的问题。按最小化原则清掉大量软件包后某次测试发现启动时间比完整系统还慢。排查后发现内核把大部分驱动编成了模块启动时需要逐一轮询硬件设备这个探测过程比直接编译进内核还要慢。解决方法是把高频设备的驱动直接编译进内核不常见的设备用模块。模块数量要少否则启动阶段模块加载本身也是开销。后面我把CONFIG_SCSI_MOD这类通用选项直接编译进内核启动恢复了预期的速度。这说明最小化的核心是按需保留不是尽量删减。最后分享一点体会如果让我把 cloudflare-os 的思路浓缩成一句话那就是系统不是装出来的是构建出来的不是因为闲才去精简而是因为规模大到不精简就活不了。对普通团队来说不需要一上来就搞这么重但其中的思想是可以渐进落地的。先从清理系统服务、固化内核参数、规范镜像版本开始把这些做成自动化再慢慢演进到不可变镜像和签名发布这是一个很自然的路径。另一个建议是做这些调整时每次只改一个变量。我见过太多人同时改了内核参数、升级了内核版本、换了网络驱动最后出问题根本没法定位。系统的可调试性往往比性能优化本身更重要。保持单一变更、保留审计记录、每次调整之后固化基线这套习惯带来的长期收益不亚于那些亮眼的性能数字。
返回列表