- Author(s): Lu Huanbing
- Last updated: 2021-01-07
- Discussion at:
针对分布式数据库在异构集群中的运行时进程表现,TiDB+ebpf进行分布式系统中,对于同一分区或者网络内的集群进行工作进程性能的实时分析,运行在容器环境中,让内核感知到TiDB容器的存在,进而使用TiDB的容器和内核和不同宿主机的内核进行交互获得在异构集群中的较优表现。
自己查
等待建议
从内核和分布式集群中容器的两个角度使用ebpf分析内核中TiDB的运行时情况,进而使用ML&DL工具针对不同环境下的TiDB的进程表现进行建模分析。
-
内核中有两个主要的内存分配器:
- slab分配器,针对固定大小的对象的通用内存分配器,支持分配请求的缓存和回收,Linux中称为slub分配器。
- 页分配器,十来分配内存页,使用伙伴算法找到相邻的空间内存,将它们一起分配,同时支持NUMA架构。
-
用于内核内存分配的API调用包括用于小块的kmalloc(), kzalloc(), kmem_cache_alloc(),用于大块区域的vmalloc(), vzalloc(), 以及分配内存的alloc_pages;
-
内核支持不同类型的锁,自旋锁,互斥锁,读写锁,因此会阻塞线程,这是性能问题的主要来源,获取互斥锁可以尝试从fastpath、midpath、slowpath;
-
在Linux中,设备驱动程序被分为两部分,上半部分可快速处理中断,并将工作调度到下半部分,在这过程中在禁止中断的模式以延迟新中断的传递,当运行时间过长时会导致延迟问题
-
BPF可以回答的问题
- 线程为什么离开CPU,以及离开多久;
- off-cpu的线程在等待什么事件;
- 谁在使用内核的slab分配器
- 内核是否正在移动页面来平衡NUMA;
- 正在发生什么工作队列事件,延迟是多少;
- 调用哪些内核函数,传入和返回的参数是什么,延迟是多少
-
跟踪内核函数时的挑战:
- 编译器内联了一些内核函数,使得内核函数对bpf不可见,这里给出两种解决方法:一是,跟踪未内联并完成同一任务的父函数或者子函数;二是使用kprobe指令偏移量进行跟踪;
- 跟踪某些内核函数是不安全的,因为其运行在特殊模式下,比如禁用中断,或者是跟踪框架的本身的一部分;
- 任何基于kprobe的工具都需要维护才能长期匹配内核的修改,大量BCC工具已经损坏,长期的解决方案是尽可能使用跟踪点。
- 容器性能分析的主要问题是,某个容器正在大量消耗资源并导致其他容器的资源紧张。由于这些容器进程都在一个内核上运行,和上述内核进程分析并无不同,区别在于,cgroups会对资源加额外的限制,会在硬限制之前被触发。
- bpf在容器上的进程跟踪工具可以回答以下问题:
- 每个容器的运行队列延迟是多少?
- 调度器正在同样的CPU上切换容器吗?
- 目前是否遇到了CPU或者硬盘的软限制?
内核中有针对cgroup的事件跟踪点,包括cgroup:cgroup_setup_root, cgroup:cgroup_attach_task,也可以附加到cgroup入口点和出口点上处理网络数据包。
遇到的问题和挑战:
- BPF跟踪需要Root权限,意味着BPF工具只能在宿主机上执行而不能在容器内部执行;
- 被Kubernetes和Docker等技术使用的容器ID是通过用户态软件;
- 跨多个容器主机运行BPF工具面临与多VM云部署相同的问题;
- Faas这样的环境不支持用户运行BPF工具。
无特权BPF解决思路之一:添加一个/dev/bpf/设备,当打开该设备,内核会给进程设置一个task_struct标志来允许访问BPF,同时该设备在exec时自动关闭。
- go1.12+ & Rust 1.47+
- Linux Kernel 4.8+
由于时间原因,这里只给出部分的分析策略:
- 创建一个可以触发相关事件的工作负载,最好知道确定的触发次数;
- 检查现有的对该事件的插桩的跟踪点或者工具;
- 该事件会被频繁调用,占用较多的CPU资源,则CPU剖析可以查看涉及的内核函数。如果不是频繁调用的事件,长时间的剖析可以捕捉足够多的学习样本;
- 针对来自内核函数的调用栈统计,了解代码路径;
- 通过子事件跟踪函数调用流;
- 检查函数参数;
- 测量函数延迟;
- 从主机上分析:
- 使用kubectl或者docker stats针对宿主机的资源使用;
- 使用perf进行统计跟踪;
- 从容器内分析:
- 按照PID命名空间总结CPU运行队列延迟;
- 对PID命名空间切换进行统计,容器共享一个CPU;
- 对blk cgroup 限制的块IO进行计数;
- 显示Overlay文件系统的读写延迟;
暂时没有