This commit is contained in:
yunwei37
2023-05-06 19:59:08 +00:00
parent e10d1a77f1
commit 981d1333f6
22 changed files with 663 additions and 744 deletions

View File

@@ -1,2 +1,4 @@
.vscode
package.json
tcpconnlat
.output

141
13-tcpconnlat/Makefile Normal file
View File

@@ -0,0 +1,141 @@
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
OUTPUT := .output
CLANG ?= clang
LIBBPF_SRC := $(abspath ../../libbpf/src)
BPFTOOL_SRC := $(abspath ../../bpftool/src)
LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a)
BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool)
BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool
LIBBLAZESYM_SRC := $(abspath ../../blazesym/)
LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym.a)
LIBBLAZESYM_HEADER := $(abspath $(OUTPUT)/blazesym.h)
ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \
| sed 's/arm.*/arm/' \
| sed 's/aarch64/arm64/' \
| sed 's/ppc64le/powerpc/' \
| sed 's/mips.*/mips/' \
| sed 's/riscv64/riscv/' \
| sed 's/loongarch64/loongarch/')
VMLINUX := ../../vmlinux/$(ARCH)/vmlinux.h
# Use our own libbpf API headers and Linux UAPI headers distributed with
# libbpf to avoid dependency on system-wide headers, which could be missing or
# outdated
INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX))
CFLAGS := -g -Wall
ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS)
APPS = tcpconnlat # minimal minimal_legacy uprobe kprobe fentry usdt sockfilter tc ksyscall
CARGO ?= $(shell which cargo)
ifeq ($(strip $(CARGO)),)
BZS_APPS :=
else
BZS_APPS := # profile
APPS += $(BZS_APPS)
# Required by libblazesym
ALL_LDFLAGS += -lrt -ldl -lpthread -lm
endif
# Get Clang's default includes on this system. We'll explicitly add these dirs
# to the includes list when compiling with `-target bpf` because otherwise some
# architecture-specific dirs will be "missing" on some architectures/distros -
# headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h,
# sys/cdefs.h etc. might be missing.
#
# Use '-idirafter': Don't interfere with include mechanics except where the
# build would have failed anyways.
CLANG_BPF_SYS_INCLUDES ?= $(shell $(CLANG) -v -E - </dev/null 2>&1 \
| sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }')
ifeq ($(V),1)
Q =
msg =
else
Q = @
msg = @printf ' %-8s %s%s\n' \
"$(1)" \
"$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \
"$(if $(3), $(3))";
MAKEFLAGS += --no-print-directory
endif
define allow-override
$(if $(or $(findstring environment,$(origin $(1))),\
$(findstring command line,$(origin $(1)))),,\
$(eval $(1) = $(2)))
endef
$(call allow-override,CC,$(CROSS_COMPILE)cc)
$(call allow-override,LD,$(CROSS_COMPILE)ld)
.PHONY: all
all: $(APPS)
.PHONY: clean
clean:
$(call msg,CLEAN)
$(Q)rm -rf $(OUTPUT) $(APPS)
$(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT):
$(call msg,MKDIR,$@)
$(Q)mkdir -p $@
# Build libbpf
$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf
$(call msg,LIB,$@)
$(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \
OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \
INCLUDEDIR= LIBDIR= UAPIDIR= \
install
# Build bpftool
$(BPFTOOL): | $(BPFTOOL_OUTPUT)
$(call msg,BPFTOOL,$@)
$(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap
$(LIBBLAZESYM_SRC)/target/release/libblazesym.a::
$(Q)cd $(LIBBLAZESYM_SRC) && $(CARGO) build --features=cheader,dont-generate-test-files --release
$(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT)
$(call msg,LIB, $@)
$(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym.a $@
$(LIBBLAZESYM_HEADER): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT)
$(call msg,LIB,$@)
$(Q)cp $(LIBBLAZESYM_SRC)/target/release/blazesym.h $@
# Build BPF code
$(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL)
$(call msg,BPF,$@)
$(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \
$(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \
-c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@)
$(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@)
# Generate BPF skeletons
$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL)
$(call msg,GEN-SKEL,$@)
$(Q)$(BPFTOOL) gen skeleton $< > $@
# Build user-space code
$(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h
$(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT)
$(call msg,CC,$@)
$(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@
$(patsubst %,$(OUTPUT)/%.o,$(BZS_APPS)): $(LIBBLAZESYM_HEADER)
$(BZS_APPS): $(LIBBLAZESYM_OBJ)
# Build application binary
$(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT)
$(call msg,BINARY,$@)
$(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@
# delete failed targets
.DELETE_ON_ERROR:
# keep intermediate (.skel.h, .bpf.o, etc) targets
.SECONDARY:

View File

@@ -149,7 +149,7 @@
<p>在互联网后端日常开发接口的时候中不管你使用的是C、Java、PHP还是Golang都避免不了需要调用mysql、redis等组件来获取数据可能还需要执行一些rpc远程调用或者再调用一些其它restful api。 在这些调用的底层基本都是在使用TCP协议进行传输。这是因为在传输层协议中TCP协议具备可靠的连接错误重传拥塞控制等优点所以目前应用比UDP更广泛一些。但相对而言tcp 连接也有一些缺点,例如建立连接的延时较长等。因此也会出现像 QUIC ,即 快速UDP网络连接 ( Quick UDP Internet Connections )这样的替代方案。</p>
<p>tcp 连接延时分析对于网络性能分析优化或者故障排查都能起到不少作用。</p>
<h2 id="tcpconnlat-的实现原理"><a class="header" href="#tcpconnlat-的实现原理">tcpconnlat 的实现原理</a></h2>
<p>tcpconnlat 这个工具跟踪执行活动TCP连接的内核函数 (例如通过connect()系统调用),并显示本地测量的连接的延迟(时间),即从发送 SYN 到响应包的时间。</p>
<p>tcpconnlat 这个工具跟踪执行活动TCP连接的内核函数例如通过connect()系统调用),并显示本地测量的连接的延迟(时间),即从发送 SYN 到响应包的时间。</p>
<h3 id="tcp-连接原理"><a class="header" href="#tcp-连接原理">tcp 连接原理</a></h3>
<p>tcp 连接的整个过程如图所示:</p>
<p><img src="tcpconnlat1.png" alt="tcpconnlate" /></p>
@@ -264,15 +264,13 @@ cleanup:
}
</code></pre>
<h2 id="编译运行"><a class="header" href="#编译运行">编译运行</a></h2>
<ul>
<li><code>git clone https://github.com/libbpf/libbpf-bootstrap libbpf-bootstrap-cloned</code></li>
<li><a href="libbpf-bootstrap">libbpf-bootstrap</a>目录下的文件复制到 <code>libbpf-bootstrap-cloned/examples/c</code></li>
<li>修改 <code>libbpf-bootstrap-cloned/examples/c/Makefile</code> ,在其 <code>APPS</code> 项后添加 <code>tcpconnlat</code></li>
<li><code>libbpf-bootstrap-cloned/examples/c</code> 下运行 <code>make tcpconnlat</code></li>
<li><code>sudo ./tcpconnlat</code></li>
</ul>
<h2 id="效果"><a class="header" href="#效果">效果</a></h2>
<pre><code class="language-plain">root@yutong-VirtualBox:~/libbpf-bootstrap/examples/c# ./tcpconnlat
<pre><code class="language-console">$ make
...
BPF .output/tcpconnlat.bpf.o
GEN-SKEL .output/tcpconnlat.skel.h
CC .output/tcpconnlat.o
BINARY tcpconnlat
$ sudo ./tcpconnlat
PID COMM IP SADDR DADDR DPORT LAT(ms)
222564 wget 4 192.168.88.15 110.242.68.3 80 25.29
222684 wget 4 192.168.88.15 167.179.101.42 443 246.76

View File

@@ -1,131 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2020 Wenbo Zhang
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
#include "tcpconnlat.h"
#define AF_INET 2
#define AF_INET6 10
const volatile __u64 targ_min_us = 0;
const volatile pid_t targ_tgid = 0;
struct piddata {
char comm[TASK_COMM_LEN];
u64 ts;
u32 tgid;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 4096);
__type(key, struct sock *);
__type(value, struct piddata);
} start SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(u32));
} events SEC(".maps");
static int trace_connect(struct sock *sk)
{
u32 tgid = bpf_get_current_pid_tgid() >> 32;
struct piddata piddata = {};
if (targ_tgid && targ_tgid != tgid)
return 0;
bpf_get_current_comm(&piddata.comm, sizeof(piddata.comm));
piddata.ts = bpf_ktime_get_ns();
piddata.tgid = tgid;
bpf_map_update_elem(&start, &sk, &piddata, 0);
return 0;
}
static int handle_tcp_rcv_state_process(void *ctx, struct sock *sk)
{
struct piddata *piddatap;
struct event event = {};
s64 delta;
u64 ts;
if (BPF_CORE_READ(sk, __sk_common.skc_state) != TCP_SYN_SENT)
return 0;
piddatap = bpf_map_lookup_elem(&start, &sk);
if (!piddatap)
return 0;
ts = bpf_ktime_get_ns();
delta = (s64)(ts - piddatap->ts);
if (delta < 0)
goto cleanup;
event.delta_us = delta / 1000U;
if (targ_min_us && event.delta_us < targ_min_us)
goto cleanup;
__builtin_memcpy(&event.comm, piddatap->comm,
sizeof(event.comm));
event.ts_us = ts / 1000;
event.tgid = piddatap->tgid;
event.lport = BPF_CORE_READ(sk, __sk_common.skc_num);
event.dport = BPF_CORE_READ(sk, __sk_common.skc_dport);
event.af = BPF_CORE_READ(sk, __sk_common.skc_family);
if (event.af == AF_INET) {
event.saddr_v4 = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr);
event.daddr_v4 = BPF_CORE_READ(sk, __sk_common.skc_daddr);
} else {
BPF_CORE_READ_INTO(&event.saddr_v6, sk,
__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
BPF_CORE_READ_INTO(&event.daddr_v6, sk,
__sk_common.skc_v6_daddr.in6_u.u6_addr32);
}
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU,
&event, sizeof(event));
cleanup:
bpf_map_delete_elem(&start, &sk);
return 0;
}
SEC("kprobe/tcp_v4_connect")
int BPF_KPROBE(tcp_v4_connect, struct sock *sk)
{
return trace_connect(sk);
}
SEC("kprobe/tcp_v6_connect")
int BPF_KPROBE(tcp_v6_connect, struct sock *sk)
{
return trace_connect(sk);
}
SEC("kprobe/tcp_rcv_state_process")
int BPF_KPROBE(tcp_rcv_state_process, struct sock *sk)
{
return handle_tcp_rcv_state_process(ctx, sk);
}
SEC("fentry/tcp_v4_connect")
int BPF_PROG(fentry_tcp_v4_connect, struct sock *sk)
{
return trace_connect(sk);
}
SEC("fentry/tcp_v6_connect")
int BPF_PROG(fentry_tcp_v6_connect, struct sock *sk)
{
return trace_connect(sk);
}
SEC("fentry/tcp_rcv_state_process")
int BPF_PROG(fentry_tcp_rcv_state_process, struct sock *sk)
{
return handle_tcp_rcv_state_process(ctx, sk);
}
char LICENSE[] SEC("license") = "GPL";

View File

@@ -4,7 +4,7 @@
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
#include "tcpconnlat.bpf.h"
#include "tcpconnlat.h"
#define AF_INET 2
#define AF_INET6 10
@@ -110,4 +110,22 @@ int BPF_KPROBE(tcp_rcv_state_process, struct sock *sk)
return handle_tcp_rcv_state_process(ctx, sk);
}
char LICENSE[] SEC("license") = "GPL";
SEC("fentry/tcp_v4_connect")
int BPF_PROG(fentry_tcp_v4_connect, struct sock *sk)
{
return trace_connect(sk);
}
SEC("fentry/tcp_v6_connect")
int BPF_PROG(fentry_tcp_v6_connect, struct sock *sk)
{
return trace_connect(sk);
}
SEC("fentry/tcp_rcv_state_process")
int BPF_PROG(fentry_tcp_rcv_state_process, struct sock *sk)
{
return handle_tcp_rcv_state_process(ctx, sk);
}
char LICENSE[] SEC("license") = "GPL";

View File

@@ -1,26 +0,0 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __TCPCONNLAT_H
#define __TCPCONNLAT_H
#define TASK_COMM_LEN 16
struct event {
// union {
unsigned int saddr_v4;
unsigned char saddr_v6[16];
// };
// union {
unsigned int daddr_v4;
unsigned char daddr_v6[16];
// };
char comm[TASK_COMM_LEN];
unsigned long long delta_us;
unsigned long long ts_us;
unsigned int tgid;
int af;
unsigned short lport;
unsigned short dport;
};
#endif /* __TCPCONNLAT_H_ */

View File

@@ -145,138 +145,6 @@
<div id="content" class="content">
<main>
<h1 id="ebpf-入门实践教程编写-ebpf-程序-tcpconnlat-测量-tcp-连接延时"><a class="header" href="#ebpf-入门实践教程编写-ebpf-程序-tcpconnlat-测量-tcp-连接延时">eBPF 入门实践教程:编写 eBPF 程序 tcpconnlat 测量 tcp 连接延时</a></h1>
<h2 id="代码解释"><a class="header" href="#代码解释">代码解释</a></h2>
<h3 id="背景"><a class="header" href="#背景">背景</a></h3>
<p>在互联网后端日常开发接口的时候中不管你使用的是C、Java、PHP还是Golang都避免不了需要调用mysql、redis等组件来获取数据可能还需要执行一些rpc远程调用或者再调用一些其它restful api。 在这些调用的底层基本都是在使用TCP协议进行传输。这是因为在传输层协议中TCP协议具备可靠的连接错误重传拥塞控制等优点所以目前应用比UDP更广泛一些。但相对而言tcp 连接也有一些缺点,例如建立连接的延时较长等。因此也会出现像 QUIC ,即 快速UDP网络连接 ( Quick UDP Internet Connections )这样的替代方案。</p>
<p>tcp 连接延时分析对于网络性能分析优化或者故障排查都能起到不少作用。</p>
<h3 id="tcpconnlat-的实现原理"><a class="header" href="#tcpconnlat-的实现原理">tcpconnlat 的实现原理</a></h3>
<p>tcpconnlat 这个工具跟踪执行活动TCP连接的内核函数 (例如通过connect()系统调用),并显示本地测量的连接的延迟(时间),即从发送 SYN 到响应包的时间。</p>
<h3 id="tcp-连接原理"><a class="header" href="#tcp-连接原理">tcp 连接原理</a></h3>
<p>tcp 连接的整个过程如图所示:</p>
<p><img src="tcpconnlat1.png" alt="tcpconnlate" /></p>
<p>在这个连接过程中,我们来简单分析一下每一步的耗时:</p>
<ol>
<li>客户端发出SYNC包客户端一般是通过connect系统调用来发出 SYN 的,这里牵涉到本机的系统调用和软中断的 CPU 耗时开销</li>
<li>SYN传到服务器SYN从客户端网卡被发出这是一次长途远距离的网络传输</li>
<li>服务器处理SYN包内核通过软中断来收包然后放到半连接队列中然后再发出SYN/ACK响应。主要是 CPU 耗时开销</li>
<li>SYC/ACK传到客户端长途网络跋涉</li>
<li>客户端处理 SYN/ACK客户端内核收包并处理SYN后经过几us的CPU处理接着发出 ACK。同样是软中断处理开销</li>
<li>ACK传到服务器长途网络跋涉</li>
<li>服务端收到ACK服务器端内核收到并处理ACK然后把对应的连接从半连接队列中取出来然后放到全连接队列中。一次软中断CPU开销</li>
<li>服务器端用户进程唤醒正在被accpet系统调用阻塞的用户进程被唤醒然后从全连接队列中取出来已经建立好的连接。一次上下文切换的CPU开销</li>
</ol>
<p>在客户端视角在正常情况下一次TCP连接总的耗时也就就大约是一次网络RTT的耗时。但在某些情况下可能会导致连接时的网络传输耗时上涨、CPU处理开销增加、甚至是连接失败。这种时候在发现延时过长之后就可以结合其他信息进行分析。</p>
<h3 id="ebpf-实现原理"><a class="header" href="#ebpf-实现原理">ebpf 实现原理</a></h3>
<p>在 TCP 三次握手的时候Linux 内核会维护两个队列,分别是:</p>
<ul>
<li>半连接队列,也称 SYN 队列;</li>
<li>全连接队列,也称 accepet 队列;</li>
</ul>
<p>服务端收到客户端发起的 SYN 请求后,内核会把该连接存储到半连接队列,并向客户端响应 SYN+ACK接着客户端会返回 ACK服务端收到第三次握手的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来。</p>
<p>我们的 ebpf 代码实现在 <a href="https://github.com/yunwei37/Eunomia/blob/master/bpftools/tcpconnlat/tcpconnlat.bpf.c">https://github.com/yunwei37/Eunomia/blob/master/bpftools/tcpconnlat/tcpconnlat.bpf.c</a> 中:</p>
<p>它主要使用了 trace_tcp_rcv_state_process 和 kprobe/tcp_v4_connect 这样的跟踪点:</p>
<pre><code class="language-c">
SEC(&quot;kprobe/tcp_v4_connect&quot;)
int BPF_KPROBE(tcp_v4_connect, struct sock *sk)
{
return trace_connect(sk);
}
SEC(&quot;kprobe/tcp_v6_connect&quot;)
int BPF_KPROBE(tcp_v6_connect, struct sock *sk)
{
return trace_connect(sk);
}
SEC(&quot;kprobe/tcp_rcv_state_process&quot;)
int BPF_KPROBE(tcp_rcv_state_process, struct sock *sk)
{
return handle_tcp_rcv_state_process(ctx, sk);
}
</code></pre>
<p>在 trace_connect 中,我们跟踪新的 tcp 连接,记录到达时间,并且把它加入 map 中:</p>
<pre><code class="language-c">struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 4096);
__type(key, struct sock *);
__type(value, struct piddata);
} start SEC(&quot;.maps&quot;);
static int trace_connect(struct sock *sk)
{
u32 tgid = bpf_get_current_pid_tgid() &gt;&gt; 32;
struct piddata piddata = {};
if (targ_tgid &amp;&amp; targ_tgid != tgid)
return 0;
bpf_get_current_comm(&amp;piddata.comm, sizeof(piddata.comm));
piddata.ts = bpf_ktime_get_ns();
piddata.tgid = tgid;
bpf_map_update_elem(&amp;start, &amp;sk, &amp;piddata, 0);
return 0;
}
</code></pre>
<p>在 handle_tcp_rcv_state_process 中,我们跟踪接收到的 tcp 数据包,从 map 从提取出对应的 connect 事件,并且计算延迟:</p>
<pre><code class="language-c">static int handle_tcp_rcv_state_process(void *ctx, struct sock *sk)
{
struct piddata *piddatap;
struct event event = {};
s64 delta;
u64 ts;
if (BPF_CORE_READ(sk, __sk_common.skc_state) != TCP_SYN_SENT)
return 0;
piddatap = bpf_map_lookup_elem(&amp;start, &amp;sk);
if (!piddatap)
return 0;
ts = bpf_ktime_get_ns();
delta = (s64)(ts - piddatap-&gt;ts);
if (delta &lt; 0)
goto cleanup;
event.delta_us = delta / 1000U;
if (targ_min_us &amp;&amp; event.delta_us &lt; targ_min_us)
goto cleanup;
__builtin_memcpy(&amp;event.comm, piddatap-&gt;comm,
sizeof(event.comm));
event.ts_us = ts / 1000;
event.tgid = piddatap-&gt;tgid;
event.lport = BPF_CORE_READ(sk, __sk_common.skc_num);
event.dport = BPF_CORE_READ(sk, __sk_common.skc_dport);
event.af = BPF_CORE_READ(sk, __sk_common.skc_family);
if (event.af == AF_INET) {
event.saddr_v4 = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr);
event.daddr_v4 = BPF_CORE_READ(sk, __sk_common.skc_daddr);
} else {
BPF_CORE_READ_INTO(&amp;event.saddr_v6, sk,
__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
BPF_CORE_READ_INTO(&amp;event.daddr_v6, sk,
__sk_common.skc_v6_daddr.in6_u.u6_addr32);
}
bpf_perf_event_output(ctx, &amp;events, BPF_F_CURRENT_CPU,
&amp;event, sizeof(event));
cleanup:
bpf_map_delete_elem(&amp;start, &amp;sk);
return 0;
}
</code></pre>
<h3 id="编译运行"><a class="header" href="#编译运行">编译运行</a></h3>
<p>TODO</p>
<h3 id="总结"><a class="header" href="#总结">总结</a></h3>
<p>通过上面的实验我们可以看到tcpconnlat 工具的实现原理是基于内核的TCP连接的跟踪并且可以跟踪到 tcp 连接的延迟时间除了命令行使用方式之外还可以将其和容器、k8s 等元信息综合起来,通过 <code>prometheus</code><code>grafana</code> 等工具进行网络性能分析。</p>
<blockquote>
<p><code>Eunomia</code> 是一个使用 C/C++ 开发的基于 eBPF的轻量级高性能云原生监控工具旨在帮助用户了解容器的各项行为、监控可疑的容器安全事件力求提供覆盖容器全生命周期的轻量级开源监控解决方案。它使用 <code>Linux</code> <code>eBPF</code> 技术在运行时跟踪您的系统和应用程序,并分析收集的事件以检测可疑的行为模式。目前,它包含性能分析、容器集群网络可视化分析*、容器安全感知告警、一键部署、持久化存储监控等功能,提供了多样化的 ebpf 追踪点。其核心导出器/命令行工具最小仅需要约 4MB 大小的二进制程序,即可在支持的 Linux 内核上启动。</p>
</blockquote>
<p>项目地址:<a href="https://github.com/yunwei37/Eunomia">https://github.com/yunwei37/Eunomia</a></p>
<h3 id="参考资料"><a class="header" href="#参考资料">参考资料</a></h3>
<ol>
<li><a href="http://kerneltravel.net/blog/2020/tcpconnlat/">http://kerneltravel.net/blog/2020/tcpconnlat/</a></li>
<li><a href="https://network.51cto.com/article/640631.html">https://network.51cto.com/article/640631.html</a></li>
</ol>
</main>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB