如何理解sys-fs-bpf

/sys/fs/bpf 是 Linux 内核中 BPF 文件系统 的挂载点。简单来说,它是 BPF 对象(如 Maps 和 Programs)在用户空间文件系统中的“家”。 持久化与命名空间化 在 BPF 文件系统出现之前,BPF 程序和 Map 的生命周期完全依赖于持有其文件描述符的进程。如果创建 BPF Map 的进程退出了,Map 也就随之销毁了。 /sys/fs/bpf 解决了这个问题,它提供了两个核心能力: 持久化: 当将一个 BPF Map 或 BPF 程序 pin 到这个文件系统中时,即使创建它的进程结束,只要内核还在,这个对象就会一直存在。其他进程可以随后通过读取该文件来获取对象的句柄,从而实现数据的共享和程序的复用。 命名空间隔离: 它遵循文件系统的权限模型和命名空间规则。不同的容器或用户可以在各自的目录下管理 BPF 对象,避免了冲突,也增加了安全性。 Pin 当用户空间程序加载一个 BPF 程序或创建一个 Map 时,内核会在内存中创建相应的数据结构,并返回一个文件描述符给用户空间。 默认情况下,当 fd 关闭(进程退出)时,内核引用计数归零,对象被销毁。 用户空间程序可以调用 bpf_obj_pin() 系统调用,将这个 fd 与 /sys/fs/bpf/ 下的一个路径绑定: 这会在该路径下创建一个文件(这就是你看到的那些文件)。 内核会增加该对象的引用计数。 只要该文件存在,对象就不会被内核销毁。 目录下的文件是什么? 在 /sys/fs/bpf 下执行 ls -l 时,看到的文件实际上是 内核 BPF 对象的句柄。 Map 文件: 代表一个 Key-Value 存储结构。多个 BPF 程序可以共享同一个 Map 文件来交换数据(例如:一个程序统计流量,另一个程序读取统计数据)。 Program 文件: 代表编译好的 BPF 字节码。可以被附加到 Hook 点(如 XDP, Tracepoint, TC 等)上执行。 它们不存储文本或二进制数据流,无法用 cat 读取 Map 里的内容(虽然可以用 bpftool 命令操作)。 ...

发布于2026-03-10  ·  Kerolt

Headless Server如何进行 Portal 认证

在局域网中对于一台没有安装图形化界面的服务器(无头 Headless 服务器),如果需要使用校园网来进行上网连接,通常需要通过弹出网页登陆的形式验证,即 Portal 认证。最简单的方法就是通过==SSH Tunnel + 本地浏览器==来解决。 其原理为建立一个 Dynamic Port Forwarding。让本地浏览器通过 SSH 隧道,以目标服务器的身份去访问网络。 在你的本地笔记本终端运行: ssh -D 123456 user@ip 保持这个终端别关。打开笔记本的浏览器,设置 SOCKS5 代理: 地址:127.0.0.1 端口:123456 在浏览器输入任一网址(如 http://xxx.com),它会自动跳转到校园网认证页面。此时你输入的账号密码,在服务端看来就是 Mini 主机发出的请求。 登录成功后,关闭代理即可。 参考:Portal认证原理

发布于2026-03-06  ·  Kerolt

Kind与MiniKube配置代理

What 使用 kind 在本地运行集群时,拉取镜像总是有这样的报错: spec.containers{nginx}: Failed to pull image "nginx:alpine": failed to pull and unpack image "docker.io/library/nginx:alpine": failed to resolve reference "docker.io/library/nginx:alpine": failed to do request: Head "https://registry-1.docker.io/v2/library/nginx/manifests/alpine ": proxyconnect tcp: dial tcp 127.0.0.1:7890: connect: connection refused Why 问题的根源在于 网络隔离层级 搞错了。 在 kind 的架构中,K8s 的“节点”其实是运行在宿主机上的 Docker 容器。 宿主机的视角:127.0.0.1:7890 指向宿主机上运行的代理软件。 kind 节点的视角:当容器(Node)尝试访问 127.0.0.1:7890 时,它访问的是 容器内部的 Loopback 接口。 结果:容器内部根本没有监听 7890 端口的服务,所以报 connection refused。 How 对于 Kind 因此不能在配置里写 127.0.0.1,必须让 kind 节点访问宿主机的 IP。 在 Linux 宿主机上,Docker 默认网桥的网关通常是 172.17.0.1。 修改 /etc/systemd/system/docker.service.d/http-proxy.conf: ...

发布于2026-03-05  ·  Kerolt

Kind配置代理

What 使用 kind 在本地运行集群时,拉取镜像总是有这样的报错: spec.containers{nginx}: Failed to pull image "nginx:alpine": failed to pull and unpack image "docker.io/library/nginx:alpine": failed to resolve reference "docker.io/library/nginx:alpine": failed to do request: Head "https://registry-1.docker.io/v2/library/nginx/manifests/alpine ": proxyconnect tcp: dial tcp 127.0.0.1:7890: connect: connection refused Why 问题的根源在于 网络隔离层级 搞错了。 在 kind 的架构中,K8s 的“节点”其实是运行在宿主机上的 Docker 容器。 宿主机的视角:127.0.0.1:7890 指向宿主机上运行的代理软件。 kind 节点的视角:当容器(Node)尝试访问 127.0.0.1:7890 时,它访问的是 容器内部的 Loopback 接口。 结果:容器内部根本没有监听 7890 端口的服务,所以报 connection refused。 How 因此不能在配置里写 127.0.0.1,必须让 kind 节点访问宿主机的 IP。 在 Linux 宿主机上,Docker 默认网桥的网关通常是 172.17.0.1。 修改 /etc/systemd/system/docker.service.d/http-proxy.conf: [Service] Environment="HTTP_PROXY=http://172.17.0.1:7890" Environment="HTTPS_PROXY=http://172.17.0.1:7890" Environment="NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,172.0.0.0/8,192.168.0.0/16,.svc,.cluster.local" 代理软件(如 Clash)必须开启 “Allow LAN” (允许局域网连接),否则它会拒绝来自 172.17.x.x 这种非本地流量。 ...

发布于2026-03-05  ·  Kerolt

Service与Endpoint

在 Kubernetes 中,Service、Endpoint(或 EndpointSlice)和 kube-proxy 是协同实现服务发现与负载均衡的核心组件。它们的关系可以简单理解为:Service 定义了访问规则,Endpoint 维护了实际后端 Pod 的地址列表,而 kube-proxy 负责将规则落地到每个节点,实现流量转发。下面从概念到实例逐一说明。 1. 核心概念 Service 作用:为一组功能相同的 Pod 提供稳定的网络访问入口(IP 和 DNS),屏蔽后端 Pod 的动态变化(如扩缩容、故障迁移)。 类型:ClusterIP(集群内访问)、NodePort(通过节点端口访问)、LoadBalancer(云负载均衡器)等。 关键属性:标签选择器(selector)决定了哪些 Pod 属于该 Service。 Endpoint(及 EndpointSlice) 作用:记录 Service 背后实际 Pod 的 IP 地址和端口列表。每个 Service 通常对应一个同名的 Endpoint 对象(旧版)或一组 EndpointSlice(新版,用于大规模集群)。 自动维护:当 Service 的标签选择器匹配到 Pod 时,Kubernetes 控制平面会自动创建并更新 Endpoint/EndpointSlice,将匹配的 Pod IP:Port 加入列表;若 Pod 变化,Endpoint 也随之更新。 kube-proxy 作用:运行在每个工作节点上的代理组件,负责将访问 Service 的流量转发到后端的 Pod。 工作模式:常见的有 iptables(默认)、IPVS(高性能)、userspace(旧版)。kube-proxy 通过监听 API Server 中 Service 和 Endpoint 的变化,动态更新节点上的网络规则(如 iptables 规则),实现流量的负载均衡。 2. 三者的关系与协同流程 用户/客户端 -> Service(ClusterIP)-> kube-proxy 设置的规则 -> 后端 Pod(通过 Endpoint 获取地址) 控制平面: 用户创建 Service,指定 selector: app=web。 Kubernetes 自动创建同名的 Endpoint 对象,并持续监控所有带 app=web 标签的 Pod,将其 IP:Port 写入 Endpoint 列表。 数据平面(每个节点): kube-proxy 监听 Service 和 Endpoint 的变化。 有更新时,kube-proxy 在节点上配置相应的转发规则(例如 iptables 规则): 所有目标为 Service ClusterIP 的流量,随机或轮询转发到 Endpoint 列表中的某个 Pod IP。 当客户端通过 Service IP 访问时,请求到达节点,被节点上的 iptables 规则拦截,直接转发给某个后端 Pod。 流量路径示例: Pod A(客户端) -> Service 10.96.0.1:80 -> 节点 iptables 规则 -> Pod B(后端)10.244.1.2:8080 3. 举例说明 假设我们有一个简单的 Web 应用,由两个 Pod 组成,它们都带有标签 app=web,容器端口为 80。 ...

发布于2026-03-01  ·  Kerolt

RingBuffer与PerfBuffer

本篇文章由 AI 辅助完成 在 eBPF(extended Berkeley Packet Filter)生态中,Ring Buffer 和 Perf Buffer 都是用于将内核态采集的数据高效传递到用户态的机制。但从 Linux 5.8 开始引入的 Ring Buffer 被认为在多数场景下优于传统的 Perf Buffer,主要原因包括以下几点: 1. 内存模型与数据一致性(Memory Model & Data Consistency) Perf Buffer: 基于 per-CPU 的 ring buffer(每个 CPU 一个独立 buffer)。 用户态需轮询所有 CPU 的 buffer,增加了复杂度。 写入时若跨页(跨越单个 perf event buffer 的页面边界),会丢弃整个样本(sample loss)。 不保证写入原子性:如果事件大于 buffer 剩余空间,会被截断或丢弃。 Ring Buffer: 单一共享 buffer(可选 per-CPU 模式,但默认是全局的)。 支持 原子 reserve-commit 语义:先 bpf_ringbuf_reserve() 预留空间,填充后再 bpf_ringbuf_submit() 提交。这确保了即使事件较大,也不会因 buffer 空间不足而被静默丢弃。 若空间不足,reserve 直接失败,用户程序可感知并处理(如降级、计数等),避免 silent data loss。 💡 底层原理:Ring Buffer 使用 memory-mapped shared memory + consumer/producer index(类似 DPDK 或 Linux kernel 的 kfifo),通过 memory barrier 保证 SMP 下的一致性。 ...

发布于2026-02-25  ·  Kerolt

k8s中的Informer

本篇文章由 AI 辅助完成 Informer 就是一个带“自动更新”功能的本地缓存(Local Cache)。 直观理解:订阅模式 vs. 轮询模式 想象你要==监控集群里的 Pod 状态==: 笨办法(直接调用 Client): 你每隔 5 秒问一次 API Server:“现在的 Pod 列表是什么?” 后果:集群里有 1 万个 Pod,你每秒问一次,API Server 的 QPS 瞬间爆炸,你会被限流(Rate Limit)。 术语:这叫 Polling(轮询),效率低,延迟高。 聪明办法(使用 Informer): 你告诉 API Server:“我要订阅 Pod 的变化”。 过程: 先全量拿一次列表(List),存到你本地内存里。 建立长连接(Watch),只要有变化,API Server 主动推给你。 你更新本地内存,并触发你的回调函数(Callback)。 后果:大部分查询直接读本地内存,零网络开销。只有变化时才通信。 术语:这叫 Watch 机制 + Client-side Caching。 代码极简版 核心就三步:创建 -> 注册回调 -> 启动。 package main import ( "context" "time" corev1 "k8s.io/api/core/v1" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" "k8s.io/klog/v2" ) func main() { // 假设 clientset 已经初始化好 var clientset *kubernetes.Clientset // 1. 创建 Informer:监听所有 Namespace 的 Pod // 参数 30*time.Minute 是 Resync 周期,意思是即使没变化,每隔这么久也重新同步一次(防丢失) factory := informers.NewSharedInformerFactory(clientset, 30*time.Minute) podInformer := factory.Core().V1().Pods().Informer() // 2. 注册回调:告诉 Informer 有事了叫我 podInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { pod := obj.(*corev1.Pod) klog.Infof("捕获新增 Pod: %s", pod.Name) // 这里写你的业务逻辑,比如加入工作队列 }, UpdateFunc: func(oldObj, newObj interface{}) { // 只有真正变化了才处理,避免噪音 oldPod := oldObj.(*corev1.Pod) newPod := newObj.(*corev1.Pod) if oldPod.ResourceVersion == newPod.ResourceVersion { return } klog.Infof("捕获更新 Pod: %s", newPod.Name) }, DeleteFunc: func(obj interface{}) { klog.Infof("捕获删除 Pod") }, }) // 3. 启动 ctx, cancel := context.WithCancel(context.Background()) defer cancel() // 异步启动 Informer go factory.Start(ctx.Done()) // 等待缓存同步完成(这步很重要,否则查不到数据) factory.WaitForCacheSync(ctx.Done()) klog.Info("Informer 已就绪,正在监听...") // 阻塞主线程 <-ctx.Done() } 为什么要用 Informer? 如果你只是写个脚本一次性查个数据,用 clientset.CoreV1().Pods().Get() 没问题。 ...

发布于2026-02-22  ·  Kerolt

更适合eBPF程序宝宝体质的clangd配置

--- # 1. 扩大匹配范围,确保头文件也能享受 BPF 配置 If: PathMatch: [.*\.bpf\.c, .*/bpf/.*\.c, .*\.h] # 增加对 .h 的支持 CompileFlags: Add: - "-target" - "bpf" - "-x" - "c" - "-std=gnu11" - "-D__BPF_TRACING__" - "-D__TARGET_ARCH_x86" # 必须指定架构,否则 vmlinux.h 内部的部分条件编译会失效 - "-I." # 确保当前目录的头文件优先级最高 - "-I/usr/include/bpf" # 指向 libbpf 头文件路径 - "-Wno-unknown-attributes" # 忽略不认识的属性 - "-ferror-limit=0" # 即使错误再多也不要停止解析,这对补全很重要 Remove: - "-std=c++*" - "-fexceptions" Diagnostics: # 2. 针对 vmlinux.h 的重定义问题进行精准打击 Suppress: - "redefinition" # 抑制所有的符号重定义 - "typedef_redefinition" # 特别针对 typedef 的冲突 - "too_many_errors" # 彻底关掉那个烦人的警告 - "err_expected_identifer" # 针对 vmlinux 内部属性解析失败的补救 --- # 3. 针对特定的 vmlinux.h 文件彻底关闭检查 If: PathMatch: .*vmlinux\.h Diagnostics: Suppress: ["*"]

发布于2026-02-15  ·  Kerolt

使用nmcli连接Wi-Fi

现在个人的环境是 Fedora43,使用NetworkManager管理网络,因而基于 nmcli(NetworkManager Command Line Interface)来高效、可靠地配置 WiFi 第一步:确认 NetworkManager 已接管无线接口 首先确保你的无线网卡(如 wlan0)处于 NetworkManager 的管理之下: nmcli device status 输出应类似: DEVICE TYPE STATE CONNECTION wlan0 wifi disconnected -- eth0 ethernet connected Wired connection 1 🔍 注意:如果 STATE 显示为 unmanaged,说明该设备被排除在 NM 管理之外。检查 /etc/NetworkManager/NetworkManager.conf 是否有 unmanaged-devices 规则,或运行: sudo nmcli device set wlan0 managed yes 第二步:扫描并连接 WiFi(支持 WPA2/WPA3) (1)扫描可用网络(可选,用于确认 SSID 拼写) nmcli device wifi list (2)连接目标 WiFi(自动保存连接配置) sudo nmcli device wifi connect "Your_SSID" password "Your_Password" 成功时你会看到: Device 'wlan0' successfully activated with 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' 💡 底层机制: 此命令会: ...

发布于2026-02-12  ·  Kerolt

使用nmcli连接Wi-Fi

现在个人的环境是 Fedora43,使用NetworkManager管理网络,因而基于 nmcli(NetworkManager Command Line Interface)来高效、可靠地配置 WiFi 第一步:确认 NetworkManager 已接管无线接口 首先确保你的无线网卡(如 wlan0)处于 NetworkManager 的管理之下: nmcli device status 输出应类似: DEVICE TYPE STATE CONNECTION wlan0 wifi disconnected -- eth0 ethernet connected Wired connection 1 🔍 注意:如果 STATE 显示为 unmanaged,说明该设备被排除在 NM 管理之外。检查 /etc/NetworkManager/NetworkManager.conf 是否有 unmanaged-devices 规则,或运行: sudo nmcli device set wlan0 managed yes 第二步:扫描并连接 WiFi(支持 WPA2/WPA3) (1)扫描可用网络(可选,用于确认 SSID 拼写) nmcli device wifi list (2)连接目标 WiFi(自动保存连接配置) sudo nmcli device wifi connect "Your_SSID" password "Your_Password" 成功时你会看到: Device 'wlan0' successfully activated with 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' 💡 底层机制: 此命令会: ...

发布于2026-02-12  ·  Kerolt