tcpdump抓包docker容器之间网络内容

前言

客户生产环境的客户端上传小文件是可以正常上传的,但在上传大文件到服务器时经常发生卡慢和超时问题,导致无法上传大文件。

为了分析这个问题,我和同事捋了一下服务端架构,架构如下:

Docker内部有容器A和容器B两个服务,容器B的端口12000映射到了宿主机上的端口12000,外部客户端可以正常访问。服务A的端口没有做映射,只允许Docker内部的容器互相访问。

问题分析

客户端到服务器能正常上传小文件,说明网络是正常的。企业内部宽理论上也是够的,因此可以排除前面两个网络存在问题的可能性。剩下的可能性在于服务器内部容器B到容器A两个微服务之间的网络问题。

方案设计

现在要用tcpdump对容器A和容器B的网络进行抓包,该如何做到呐?

第一个方案

从宿主机监听容器A的端口2100,理论上行不通,因为端口没有映射。

第二个方案

在容器内部搭一个tcpdump容器,容器内部网络互通,端口2100可以被tcpdump容器访问,理论上可行,但实际验证下来没有抓到包。因为tcpdump容器本身可以理解为一个完整的操作系统,在里面开启监听,监听到的网卡只是tcpdump容器内部的虚拟网卡,而不是容器A和容器B通信的虚拟网卡。

第三个方案

参考了网上的文章,使用nsenter命令工具,借助这个工具,我们可以进入Linux系统的不同命名空间中并执行命令,docker中运行的容器本身就是一种被隔离的进程。

要实现抓包大致逻辑是:

  1. 在宿主机安装tcpdump工具;
  2. 使用docker命令找到Service A容器对应的宿主机的pid;
  3. 借助nsenter命令,使得tcpdump能够监听到流经Service A容器内部虚拟网卡的流量。

方案验证

安装tcpdump

网上下载tcpdump包,在宿主机安装tcpdump。

tcpdump依赖于 libpcap,因此需要先安装 libpcap。

解压libpcap-1.10.3.tar.gz包,并编译安装。

tar -zxvf libpcap-1.10.3.tar.gz 
cd libpcap-1.10.3 
./configure 
make 
make install

解压tcpdump并编译安装

tar -zxvf tcpdump-4.99.3.tar.gz 
cd tcpdump-4.99.3 
./configure
make 
make install

映射关系查找

用下面命令查找要抓包的docker容器的容器ID,这里查找Service A的容器ID

docker ps | grep 容器名

得到Service A容器ID后,执行下面这条命令,找到容器在宿主机上对应的PID

docker inspect --format "{{.State.Pid}}" 容器ID

下图是容器ID(f0541503c0e1)对应的宿主机上的PID(31891)

网络内容抓包

下面命令,PID替换为上一步找到的PID,port 是Service A服务的端口,host 后面是nginx容器的IP,这个IP需要进入容器内部使用命令 ip addr 查看,默认是网卡eth0对应的IP。

nsenter -n -t PID tcpdump -i eth0 dst port 端口 and src host 容器内部IP
  • nsenter 是一个用于进入指定命名空间的工具。在 Linux 系统中,命名空间用于隔离资源,如进程、网络、挂载点等。
  • -n:表示进入网络命名空间。
  • -t PID:表示目标进程的 PID(进程ID)。这个 PID 通常是一个容器进程的 ID,所以该命令的意图是进入容器的网络命名空间。
  • -i eth0: 指定要捕获流量的网络接口为 eth0
  • dst port:表示要监听的目标端口
  • src host:表示要监听的源地址IP

如果要输出抓包内容到文件,可以在上面命令后面追加参数-w /xxx/network.pcap,表示把抓包内容输出到宿主机/xxx路径下的network.pcap文件中,文件名可以自定义。

nsenter -n -t PID tcpdump -i eth0 dst port 2048 and src host 容器内部ip -w /xxx/network.cap
  • -w:这是一个 tcpdump 的选项,用于指定将捕获的数据包写入文件的路径

修改完上面这条命令为对应的服务参数后,在宿主机上执行,开始抓包从nginx服务到serviceA的网络。

上传一个273byte(字节)的文件,下面这个是抓到的包内容

16:05:21.396649 IP 10.0.2.22.40270 > xxx-server.dls-monitor: Flags [S], seq 2702839680, win 28200, options [mss 1410,sackOK,TS val 475563759 ecr 0,nop,wscale 7], length 0
16:05:21.396682 IP 10.0.2.22.40270 > xxx-server.dls-monitor: Flags [.], ack 3865746325, win 221, options [nop,nop,TS val 475563759 ecr 475563759], length 0
16:05:21.396792 IP 10.0.2.22.40270 > xxx-server.dls-monitor: Flags [P.], seq 0:563, ack 1, win 221, options [nop,nop,TS val 475563759 ecr 475563759], length 563
16:05:21.396829 IP 10.0.2.22.40270 > xxx-server.dls-monitor: Flags [P.], seq 563:2021, ack 1, win 221, options [nop,nop,TS val 475563759 ecr 475563759], length 1458
16:05:21.476387 IP 10.0.2.22.40270 > xxx-server.dls-monitor: Flags [.], ack 165, win 229, options [nop,nop,TS val 475563839 ecr 475563839], length 0
16:05:21.476548 IP 10.0.2.22.40270 > xxx-server.dls-monitor: Flags [F.], seq 2021, ack 165, win 229, options [nop,nop,TS val 475563839 ecr 475563839], length 0
16:05:21.476822 IP 10.0.2.22.40270 > xxx-server.dls-monitor: Flags [.], ack 166, win 229, options [nop,nop,TS val 475563839 ecr 475563839], length 0

分析抓包内容

将上面抓到的网络内容交由AI分析,AI对这段信息的分析如下:

  1. 第一行数据包:
    • 时间戳:16:05:21.396649
    • IP地址:10.0.2.22.40270 > xxx-server.dls-monitor
    • Flags:[S],表示这是一个SYN(同步)报文
    • seq:2702839680,表示序列号为2702839680
    • win:28200,表示窗口大小为28200
    • options:[mss 1410,sackOK,TS val 475563759 ecr 0,nop,wscale 7],表示使用了最大报文段长度(MSS)为1410,支持选择性确认(SACK),时间戳值为475563759,扩展最大报文段长度(WSCALE)为7
    • length:0,表示该报文没有负载数据
  1. 第二行数据包:
    • 时间戳:16:05:21.396682
    • IP地址:10.0.2.22.40270 > xxx-server.dls-monitor
    • Flags:[.],表示这是一个ACK(确认)报文
    • ack:3865746325,表示确认号为3865746325
    • win:221,表示窗口大小为221
    • options:[nop,nop,TS val 475563759 ecr 475563759],表示时间戳值为475563759,扩展最大报文段长度(ECR)为475563759
    • length:0,表示该报文没有负载数据
  1. 第三行数据包:
    • 时间戳:16:05:21.396792
    • IP地址:10.0.2.22.40270 > xxx-server.dls-monitor
    • Flags:[P.],表示这是一个PSH(推送)和ACK(确认)报文
    • seq:0:563,表示序列号范围为0到563
    • ack:1,表示确认号为1
    • win:221,表示窗口大小为221
    • options:[nop,nop,TS val 475563759 ecr 475563759],表示时间戳值为475563759,扩展最大报文段长度(ECR)为475563759
    • length:563,表示该报文的负载数据长度为563字节
  1. 第四行数据包:
    • 时间戳:16:05:21.396829
    • IP地址:10.0.2.22.40270 > xxx-server.dls-monitor
    • Flags:[P.],表示这是一个PSH(推送)和ACK(确认)报文
    • seq:563:2021,表示序列号范围为563到2021
    • ack:1,表示确认号为1
    • win:221,表示窗口大小为221
    • options:[nop,nop,TS val 475563759 ecr 475563759],表示时间戳值为475563759,扩展最大报文段长度(ECR)为475563759
    • length:1458,表示该报文的负载数据长度为1458字节
  1. 第五行数据包:
    • 时间戳:16:05:21.476387
    • IP地址:10.0.2.22.40270 > xxx-server.dls-monitor
    • Flags:[.],表示这是一个ACK(确认)报文
    • ack:165,表示确认号为165
    • win:229,表示窗口大小为229
    • options:[nop,nop,TS val 475563839 ecr 475563839],表示时间戳值为475563839,扩展最大报文段长度(ECR)为475563839
    • length:0,表示该报文没有负载数据
  1. 第六行数据包:
    • 时间戳:16:05:21.476548
    • IP地址:10.0.2.22.40270 > xxx-server.dls-monitor
    • Flags:[F.],表示这是一个FIN(结束)和ACK(确认)报文
    • seq:2021,表示序列号为2021
    • ack:165,表示确认号为165
    • win:229,表示窗口大小为229
    • options:[nop,nop,TS val 475563839 ecr 475563839],表示时间戳值为475563839,扩展最大报文段长度(ECR)为475563839
    • length:0,表示该报文没有负载数据
  1. 第七行数据包:
    • 时间戳:16:05:21.476822
    • IP地址:10.0.2.22.40270 > xxx-server.dls-monitor
    • Flags:[.],表示这是一个ACK(确认)报文
    • ack:166,表示确认号为166
    • win:229,表示窗口大小为229
    • options:[nop,nop,TS val 475563839 ecr 475563839],表示时间戳值为475563839,扩展最大报文段长度(ECR)为475563839
    • length:0,表示该报文没有负载数据

上面这段分析的内容是关于网络数据包的抓取和分析。根据tcpdump工具抓取的数据包信息,可以得出以下结论:

  1. 第一行数据包是一个SYN报文,表示建立连接的请求。
  2. 第二行数据包是一个ACK报文,表示确认连接的建立。
  3. 第三行和第四行数据包都是PSH和ACK报文,表示传输数据的请求和确认。
  4. 第五行数据包是一个ACK报文,表示确认数据的接收。
  5. 第六行数据包是一个FIN和ACK报文,表示结束连接的请求和确认。
  6. 第七行数据包是一个ACK报文,表示确认连接的关闭。

具体的网络数据包分析可使用wireshark。

备注说明:

  1. 报文分析和报文内容总结由AI讯飞星火生成。
  2. 上面的操作是在测试环境验证抓包的可行性,不是实际生产环境大文件上传慢时抓的包。

参考

计算机网络抓包工具——tcpdump详解-CSDN博客

docker(k8s)容器内tcpdump抓包 - 简书

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐