Golang编程语言知识介绍


  • 首页

  • todo

  • 思考

  • life

  • food

  • OS

  • lua

  • redis

  • Golang

  • C

  • TCP/IP

  • ebpf

  • p4

  • OpenVPN

  • IPSec

  • L2TP

  • DNS

  • distributed

  • web

  • OpenWRT

  • 运维

  • Git

  • 鸟哥的私房菜

  • IT杂谈

  • 投资

  • About Me

  • 友情链接

  • FTP

  • 搜索
close

基于 eBPF 的网络监测实践

时间: 2023-08-24   |   分类: ebpf     |   阅读: 2463 字 ~5分钟

深入浅出 eBPF

首页 eBPF 技术报告 关于

本文地址:https://www.ebpf.top/post/ebpf_network_exporter

公有云网络问题,是一个老生常谈的事情了。今天主要简单说明下,在内网环境下去分析 & 定位这个问题。也并不是一开始就需要 eBPF 加持的,可以从简单的基础工具到监控配合去相关比较快速的定位问题,解决问题。下面会从发现问题到一步步配合使用 Or 开发一些简单的东西去解决问题的大致思路。

常见的网络相关问题

  • 业务负载不均衡:业务系统会越发的复杂,不可能单个服务承接所有流量,一定会逐步迭代为一组服务承接流量,那我们是可以先用一些负载均衡组件去解决这个问题(不推荐一上来就喊,我要自研,图啥呢?),可以使用 HaProxy、Nginx 配合公有云的弹性能力去解决问题。伴随着业务的发展慢慢的当全站使用 K8s 的时候,还会遇到 Kube-Proxy 存在负载不均衡的问题,这个时候就可以应入 Envoy 到最终上线 Istio 去解决问题,做到内部服务的负载均衡(当然服务的健康检查 & 优雅停止需要考虑下)。配合边缘公有云 LB 做入流量控制,到 Kong 这种非常自动化的网关来解决业务整个边缘路由的分流能力。至此 xLB + Kong/Openresty/Apisix/Envoy + Istio(Evnoy) 基本上已经到了天花板了。(应该还有更优的)
  • 网络异常抖动:日常会发生抖动的情况大致有几种情况吧。服务间异常、交换机过载、存储系统异常、业务系统 Bug,如上问题都需要专业的同学去帮助解决,并不能也不是每次都是网工的锅是吧。
  • 网络延时增大:抛开业务系统问题,Slow Query、Big Key、Hot Key、消费生产(消息队列)延时等问题的话。那只能朝着网络层面去找问题了。(你想,交换机里面也有 CPU)举一个例子,家里 Wifi 为什么没啥人用也会卡,有一个东西叫信道,就好比虽然有很多信道但是呢流量都在一个信道里面跑(1个人干活,一群人看)能不卡吗?(应该能解的,这个就不懂了,不懂不捣乱)

如何定位

Istio

  • Istio 监控指标里面其实已经可以发现服务链路之间的调用的情况了,就是会发现为什么有些快有些慢呢,在排除 xDS(服务发现、路由下发等,Envoy API 与 Pilot 交互) 的问题后(其实没几家公司可以触发 Istio 的 Bug 的),依然慢的莫名其妙。

Ping

  • 这个是一个好东西哦,简单看看绝对富裕了。闲来无事基于 ping_exporter 加了一个 RESTful API,这样想 Ping 哪里 Ping 哪里,如果需要 K8s 里面互相 Ping 的话,其实只要依靠 K8s API 做自发现 Node 互 Ping 就能一目了然的发现很多问题了(原来网络真的很糟糕)

eBPF

  • eBPF(extended Berkeley Packet Filter) 是一种可以在 Linux 内核中运行用户编写的程序,而不需要修改内核代码或加载内核模块的技术。简单说,eBPF 让 Linux 内核变得可编程化了。eBPF 程序是一个事件驱动模型。Linux 内核提供了各种 hook point,比如 system calls, function entry/exit, kernel tracepoints, network events 等。eBPF 程序通过实现想要关注的 hook point 的 callback,并把这些 callback 注册到相应的 hook point 来完成“内核编程”。Linux bcc/BPF Tracing Tools
  • bcc 是一个基于 eBPF 的工具包,内部包装了 LLVM。你可以用 C 语言 + Python、Lua 等高级语言编写 eBPF 程序,同时还提供了不少有用的工具。BCC是一个用于跟踪内核和操作程序的工具集,其软件包中包含了一些有用的工具和例子,它扩展了BPF(Berkeley Packet Filters),通常被称为eBPF , 在Linux3.15中首次引入,但大多数BCC的功能需要Libux4.1及以上版本。(可以话时间研究下下的,基础功能 & BPF 代码里面多有)
  • bpftrace 是一门基于 eBPF 的高级追踪语言,可以方便快捷地编写 Linux 追踪程序。
  • 工作原理:eBPF 通过 C 语言自由扩展(这些扩展通过 LLVM 转换为 BPF 字节码后,加载到内核中执行。从图中你可以看到,eBPF 的执行需要三步:从用户跟踪程序生成 BPF 字节码;加载到内核中运行;向用户空间输出结果。BPF for Tracing
  • 实际上,在 eBPF 执行过程中,编译、加载还有 maps 等操作,对所有的跟踪程序来说都是通用的。把这些过程通过 Python 抽象起来,也就诞生了 BCC(BPF Compiler Collection)。
  • BCC 把 eBPF 中的各种事件源(比如 kprobe、uprobe、tracepoint 等)和数据操作(称为 Maps),也都转换成了 Python 接口(也支持 lua)。这样,使用 BCC 进行动态追踪时,编写简单的脚本就可以了。
  • 不过要注意,因为需要跟内核中的数据结构交互,真正核心的事件处理逻辑,还是需要我们用 C 语言来编写。

cloudflare/ebpf_exporter

  • Prometheus exporter for custom eBPF metrics. 非常棒的一个项目,在与 K8s 结合的时候,以 DaemonSet 的形式运行在集群中,可借鉴 kube-ebpf-exporter。

  • 依据 Bcc Tools tcpconnlat,配合 ebpf_exporter 的改写如下(ebpf_exporter 例子中没有网络延时相关的)

  •   programs:
        # See:
        # * https://github.com/iovisor/bcc/blob/master/tools/tcpconnect.py
        # * https://github.com/iovisor/bcc/blob/master/tools/tcpconnlat_example.txt
        - name: tcpconnlat
          metrics:
            counters:
              - name: tcpconnlat_ipv4
                help: tcp4 connection latency(ms)
                table: ipv4_events
                labels:
                  - name: pid
                    size: 4
                    decoders:
                      - name: uint
                  - name: sst_addr
                    size: 4
                    decoders:
                      - name: inet_ip
                  - name: dst_addr
                    size: 4
                    decoders:
                      - name: inet_ip
                  - name: lst_port
                    size: 2
                    decoders:
                      - name: uint
                  - name: dst_port
                    size: 2
                    decoders:
                      - name: uint
                  - name: proc_name
                    size: 16
                    decoders:
                      - name: string
          kprobes:
            tcp_rcv_state_process: trace_tcp_rcv_state_process
            tcp_v4_connect: trace_connect
          code: |
            #include <uapi/linux/ptrace.h>
            #include <net/sock.h>
            #include <net/tcp_states.h>
            #include <bcc/proto.h>
    
            struct info_t {
                u64 ts;
                u32 pid;
                char task[TASK_COMM_LEN];
            };
            BPF_HASH(start, struct sock *, struct info_t);
    
            // separate data structs for ipv4
            struct ipv4_data_t {
                u32 pid;
                u32 saddr;
                u32 daddr;
                u16 lport;
                u16 dport;
                char task[TASK_COMM_LEN];
            };
            BPF_HASH(ipv4_events, struct ipv4_data_t);
    
            int trace_connect(struct pt_regs *ctx, struct sock *sk)
            {
                u32 pid = bpf_get_current_pid_tgid();
                struct info_t info = {.pid = pid};
                info.ts = bpf_ktime_get_ns();
                bpf_get_current_comm(&info.task, sizeof(info.task));
                start.update(&sk, &info);
                return 0;
            };
    
            // See tcp_v4_do_rcv(). So TCP_ESTBALISHED and TCP_LISTEN
            // are fast path and processed elsewhere, and leftovers are processed by
            // tcp_rcv_state_process(). We can trace this for handshake completion.
            // This should all be switched to static tracepoints when available.
            int trace_tcp_rcv_state_process(struct pt_regs *ctx, struct sock *skp)
            {
                // will be in TCP_SYN_SENT for handshake
                if (skp->__sk_common.skc_state != TCP_SYN_SENT)
                    return 0;
                // check start and calculate delta
                struct info_t *infop = start.lookup(&skp);
                if (infop == 0) {
                    return 0;   // missed entry or filtered
                }
                u64 ts = infop->ts;
                u64 now = bpf_ktime_get_ns();
                u64 delta_us = (now - ts) / 1000ul;
                u64 delta_ms = delta_us / 1000;
            #ifdef MIN_LATENCY
                if ( delta_us < DURATION_US ) {
                    return 0; // connect latency is below latency filter minimum
                }
            #endif
    
                // pull in details
                u16 family = 0, lport = 0, dport = 0;
                family = skp->__sk_common.skc_family;
                lport = skp->__sk_common.skc_num;
                dport = skp->__sk_common.skc_dport;
    
                // emit to appropriate data path
                if (family == AF_INET) {
                    struct ipv4_data_t data4 = {.pid = infop->pid};
                    data4.saddr = skp->__sk_common.skc_rcv_saddr;
                    data4.daddr = skp->__sk_common.skc_daddr;
                    data4.lport = lport;
                    data4.dport = ntohs(dport);
                    __builtin_memcpy(&data4.task, infop->task, sizeof(data4.task));
                    ipv4_events.increment(data4, delta_ms);
                }
                start.delete(&skp);
                return 0;
            }      
    

基于上面一轮干下来,基本上哪里慢,为什么慢,怎么慢也就差不多定位了。剩下么,该干嘛干嘛。记得调用链还是非常有用的哦,配合网络监测 + 调用链,问题自然而然的就暴露啦(当然调用链接入姿势是另外一个问题啦)

最后推荐几本书吧,BPF之巅:洞悉Linux系统和应用性能 & Linux内核观测技术BPF。

原文地址:基于 eBPF 的网络检测实践

  • 原文作者:孙文杰
  • **原文链接:**https://www.ebpf.top/post/ebpf_network_exporter/
  • **版权声明:**本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
#ebpf#
【BPF入门系列-11】EBFP 和 GO 之初探编程
【BPF入门系列-11】使用 eBPF 技术跟踪 Netfilter 数据流过滤结果
shankusu2017@gmail.com

shankusu2017@gmail.com

日志
分类
标签
GitHub
© 2009 - 2025
粤ICP备2021068940号-1 粤公网安备44011302003059
0%