WEBKT

告别盲盒:用 eBPF 解锁容器内部系统调用追踪术,让 Bug 无处遁形

57 0 0 0

容器内部,黑盒重重?eBPF 来破局!

为什么 eBPF 适合容器追踪?

实战:用 eBPF 追踪容器内部的系统调用

进阶:更强大的 eBPF 追踪工具

eBPF 的更多应用场景

总结

踩坑记录和注意事项

进一步学习资源

容器内部,黑盒重重?eBPF 来破局!

作为一名老码农,我深知容器技术带来的便利,但也常常被其“黑盒”特性所困扰。应用跑在容器里,一旦出现问题,就像隔着一层毛玻璃,难以看清内部的真实情况。特别是对于那些隐藏得很深的 Bug,更是让人头疼不已。

传统的排查方法,比如打日志、加监控,虽然有效,但往往需要修改代码、重新部署,效率低下,而且对于已经发生的问题,也难以追溯。有没有一种方法,可以在不修改代码的情况下,实时地观察容器内部的行为,就像给容器装上“透视眼”一样?

答案是肯定的!那就是 eBPF (extended Berkeley Packet Filter)。

eBPF 最初是为网络数据包过滤而设计的,但随着技术的发展,它已经成为一个强大的内核追踪和分析工具。它可以让我们在内核中安全地运行自定义的代码,从而实现对系统调用的追踪、性能分析、安全审计等功能。

为什么 eBPF 适合容器追踪?

  • 无侵入性:eBPF 程序运行在内核中,无需修改应用程序代码,对应用程序性能影响很小。
  • 高灵活性:可以使用 C 语言编写 eBPF 程序,然后编译成字节码,加载到内核中运行。这使得我们可以根据实际需求,定制追踪逻辑。
  • 实时性:eBPF 程序可以实时地收集数据,并进行分析,帮助我们快速地定位问题。
  • 安全性:eBPF 程序在加载到内核之前,会经过严格的验证,确保其安全性,防止恶意代码破坏系统。

实战:用 eBPF 追踪容器内部的系统调用

接下来,我将通过一个实际的例子,演示如何使用 eBPF 追踪容器内部的系统调用。

1. 准备工作

  • 安装 bcc 工具:bcc (BPF Compiler Collection) 是一个用于创建 eBPF 程序的工具包,它提供了一系列的 Python 脚本和 C 头文件,方便我们编写和调试 eBPF 程序。你可以使用以下命令安装 bcc:

    sudo apt-get update
    sudo apt-get install bpfcc-tools linux-headers-$(uname -r)
  • 安装 docker:如果你还没有安装 docker,可以使用以下命令安装:

    sudo apt-get update
    sudo apt-get install docker-ce docker-ce-cli containerd.io
  • 创建一个简单的容器:为了演示方便,我们创建一个简单的 Ubuntu 容器:

    docker run -it ubuntu bash
    

2. 编写 eBPF 程序

创建一个名为 syscall_tracer.py 的 Python 脚本,内容如下:

#!/usr/bin/env python
from bcc import BPF
import argparse
# 定义命令行参数
parser = argparse.ArgumentParser(description="Trace syscalls inside a container")
parser.add_argument("-p", "--pid", type=int, help="Container PID to trace")
args = parser.parse_args()
# 定义 eBPF 程序
program = """
#include <linux/sched.h>
struct data_t {
u32 pid;
u64 ts;
int syscall;
char comm[TASK_COMM_LEN];
};
BPF_PERF_OUTPUT(events);
int syscall__enter(struct pt_regs *ctx, long id) {
struct data_t data = {};
data.pid = bpf_get_current_pid_tgid();
data.ts = bpf_ktime_get_ns();
data.syscall = id;
bpf_get_current_comm(&data.comm, sizeof(data.comm));
events.perf_submit(ctx, &data, sizeof(data));
return 0;
}
"""
# 创建 BPF 实例
bpf = BPF(text=program)
# 附加 kprobe 到 syscall__enter
bpf.attach_kprobe(event="syscall__enter", fn_name="syscall__enter")
# 定义 perf_output 回调函数
def print_event(cpu, data, size):
event = bpf["events"].event(data)
print(f"{event.pid} {event.comm.decode()} {event.syscall} {event.ts}")
# 打印表头
print("%4s %-16s %8s %s" % ("PID", "COMM", "SYSCALL", "TS"))
# 循环读取 perf_output
bpf["events"].open_perf_buffer(print_event)
while True:
try:
bpf.perf_buffer_poll()
except KeyboardInterrupt:
exit()

代码解释:

  • argparse 用于解析命令行参数,例如容器的 PID。
  • program 定义了 eBPF 程序,它包含一个 data_t 结构体,用于存储系统调用的信息,例如 PID、时间戳、系统调用号等。BPF_PERF_OUTPUT 定义了一个 perf_output,用于将数据从内核空间传递到用户空间。
  • syscall__enter 是一个 kprobe 函数,它会在每个系统调用进入内核时被调用。在这个函数中,我们获取系统调用的信息,并将其存储到 data_t 结构体中,然后通过 events.perf_submit 将数据发送到 perf_output。
  • bpf.attach_kprobe 将 kprobe 附加到 syscall__enter 事件上。
  • print_event 是 perf_output 的回调函数,它会在接收到数据时被调用。在这个函数中,我们将数据打印到终端。
  • bpf.perf_buffer_poll 循环读取 perf_output,并调用回调函数处理数据。

3. 运行 eBPF 程序

  • 获取容器的 PID:首先,需要获取要追踪的容器的 PID。可以使用以下命令获取:

    docker inspect <container_id> | grep Pid
    

    <container_id> 替换为你的容器 ID。

  • 运行 eBPF 程序:使用以下命令运行 eBPF 程序,并将容器的 PID 作为参数传递:

    sudo ./syscall_tracer.py -p <container_pid>
    

    <container_pid> 替换为你的容器 PID。

4. 观察结果

运行 eBPF 程序后,你将在终端看到类似以下的输出:

PID COMM SYSCALL TS
1234 bash 56 1678886400123456
1234 bash 59 1678886400234567
1234 bash 60 1678886400345678
...

每一行代表一个系统调用,包括 PID、进程名、系统调用号和时间戳。通过观察这些系统调用,我们可以了解应用程序的行为模式,并发现潜在的问题。

进阶:更强大的 eBPF 追踪工具

除了自己编写 eBPF 程序,还可以使用一些现成的 eBPF 追踪工具,例如:

  • bpftrace:bpftrace 是一种高级的 eBPF 追踪语言,它允许我们使用简单的语法,编写复杂的追踪程序。bpftrace 提供了大量的内置函数和变量,方便我们访问内核数据。
  • Falco:Falco 是一种云原生的运行时安全工具,它使用 eBPF 技术,监控容器和 Kubernetes 集群的行为,并检测异常活动。

eBPF 的更多应用场景

除了容器追踪,eBPF 还可以应用于以下场景:

  • 网络性能分析:使用 eBPF 监控网络数据包的流量、延迟、丢包率等指标,帮助我们优化网络性能。
  • 安全审计:使用 eBPF 监控系统调用、文件访问、网络连接等行为,检测恶意活动。
  • 性能调优:使用 eBPF 分析 CPU 使用率、内存分配、磁盘 I/O 等指标,帮助我们优化应用程序性能。

总结

eBPF 是一项强大的技术,它可以让我们深入地了解 Linux 内核的行为,并解决各种性能、安全和故障排除问题。在容器时代,eBPF 更是发挥着重要的作用,它可以帮助我们打破容器的“黑盒”,更好地管理和监控容器化应用程序。希望这篇文章能够帮助你入门 eBPF,并将其应用到实际工作中。

踩坑记录和注意事项

  • 内核版本兼容性:eBPF 的某些特性可能需要较新的内核版本才能支持。在编写 eBPF 程序之前,请确保你的内核版本满足要求。
  • 权限问题:运行 eBPF 程序需要 root 权限。请使用 sudo 命令运行程序。
  • 程序验证:eBPF 程序在加载到内核之前,会经过严格的验证。如果程序验证失败,可以尝试简化程序,或者查看内核日志,了解错误原因。
  • 性能影响:虽然 eBPF 对应用程序性能影响很小,但过度使用 eBPF 仍然可能导致性能下降。请谨慎使用 eBPF,并根据实际情况进行优化。
  • bcc 版本问题:不同版本的 bcc 工具可能存在兼容性问题。建议使用最新版本的 bcc 工具。

进一步学习资源

希望这些资源能够帮助你更深入地学习 eBPF!

容器透视眼 eBPF容器追踪系统调用

评论点评

打赏赞助
sponsor

感谢您的支持让我们更好的前行

分享

QRcode

https://www.webkt.com/article/9611