找出属于Docker容器的网络接口是哪个

42

Docker创建这些虚拟以太网接口veth[唯一标识符],列在ifconfig中。我该如何找出哪个接口属于特定的Docker容器?

我想监听TCP流量。


看起来这是一个开放问题,链接在这里 https://github.com/docker/docker/issues/14666 并且有一些解决方法。我不擅长网络,也不太理解那些解决方法,所以我决定在使用tcpdump时仅过滤主机。对我来说有效。如果你也使用tcpdump或者你的工具有类似的过滤器,也许对你也有效。 - Artem Malinko
6个回答

38

定位接口
在我的情况下,从容器中获取值的方式是这样的(请检查eth0):

$ docker exec -it my-container cat /sys/class/net/eth1/iflink
123

然后:

$ ip ad | grep 123
123: vethd3234u4@if122: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker_gwbridge state UP group default

使用tcpdump -i vethd3234u4命令进行检查。


http://lxr.free-electrons.com/source/Documentation/ABI/testing/sysfs-class-net中有关神秘的iflink的参考信息:

150 What:           /sys/class/net/<iface>/iflink
151 Date:           April 2005
152 KernelVersion:  2.6.12
153 Contact:        netdev@vger.kernel.org
154 Description:
155                 Indicates the system-wide interface unique index identifier a
156                 the interface is linked to. Format is decimal. This attribute is
157                 used to resolve interfaces chaining, linking and stacking.
158                 Physical interfaces have the same 'ifindex' and 'iflink' values.

1
对我不起作用。容器中的iflink为14。主机上的“ip ad”没有14号接口,“容器中的”ip ad”有。Docker版本为17.03.1-ce。 - Curtis Yallop
我注意到我的容器中的接口12恰好是客户机上的接口13。而我的另一个容器中的接口14是客户机上的接口15。我尝试在主机上运行Wireshark进行捕获,并根据流量找出了哪个接口是哪个。 - Curtis Yallop
1
我还注意到,如果在容器中我看到了例如63293的端口号,但是ip addr show | grep 63293没有返回任何结果,那么值得检查一下63292和63294周围的接口。 - Sysanin
有没有一种方法可以找到容器的网络接口,而不是特定于Linux的?对于我们Mac用户来说,似乎没有/sys/class/net/eth1/iflink。 - Nikita Rybak

12

根据提供的答案(对我有效),我编写了这个简单的bash脚本:

#!/bin/bash

export containers=$(sudo docker ps --format "{{.ID}}|{{.Names}}")
export interfaces=$(sudo ip ad);
for x in $containers
        do
                export name=$(echo "$x" |cut -d '|' -f 2);
                export id=$(echo "$x"|cut -d '|' -f 1)
                export ifaceNum="$(echo $(sudo docker exec -it "$id" cat /sys/class/net/eth0/iflink) | sed s/[^0-9]*//g):"
                export ifaceStr=$( echo "$interfaces" | grep $ifaceNum | cut -d ':' -f 2 | cut -d '@' -f 1);
                echo -e "$name: $ifaceStr";
done

1
注意:此程序适用于大多数Linux发行版,但不适用于OSX。 - ln -s

8
我的回答更像是对这个重要主题的改进,因为它并没有帮助找出哪个网络接口属于docker容器,但正如作者所注意到的,他想要在docker容器内部“监听tcp流量”,我会在您的网络故障排除期间尝试帮助解决这个问题。
考虑到veth网络设备涉及到网络命名空间,我们可以通过nsenter工具在另一个命名空间中执行程序,方法如下(记住 - 您需要特权权限(sudo/root)才能执行此操作):
获取任何您感兴趣的容器的ID以捕获流量,例如它将是78334270b8f8 然后我们需要获取该容器化应用程序的PID(我假设您只运行了一个与网络相关的进程,并且想要捕获其流量。否则,该方法很难适用): sudo docker inspect 78334270b8f8 | grep -i pid 例如,pid 的输出将是 111380 - 这是您容器化应用程序的 ID,您还可以通过 ps 命令检查它:ps aux | grep 111380,只是出于好奇。

下一步是检查您在容器内拥有哪些网络接口:

sudo nsenter -t 111380 -n ifconfig

此命令将返回容器化应用程序的网络命名空间中的网络设备列表(您不应该在容器中安装 ifconfig 工具,只需在节点/机器上安装即可)

例如,您需要在接口 eth2 上捕获流量,并使用以下命令过滤到 tcp 目标端口 80(当然可能会有所不同):

sudo nsenter -t 111380 -n tcpdump -nni eth2 -w nginx_tcpdump_test.pcap 'tcp dst port 80'

请记住,在这种情况下,您不需要在容器内安装 tcpdump 工具。

然后,在捕获数据包后,.pcap文件将可在您的机器/节点上使用,您可以使用任何您喜欢的工具来读取它:tcpdump -r nginx_tcpdump_test.pcap 方法的优点:
  • 不需要在容器内部安装网络工具,仅需要在Docker节点上安装
  • 不需要在容器和节点之间搜索网络设备映射
缺点:
  • 您需要在节点/机器上拥有特权用户才能运行nsenter工具

1
这是最佳答案,因为它不依赖于容器内部的任何内容。请注意使用 docker inspect <id> --format '{{.State.Pid}}' 而不是使用 grep。 - Dave

6

@pbaranski的解决方案一句话概括:

num=$(docker exec -i my-container cat /sys/class/net/eth0/iflink | tr -d '\r'); ip ad | grep -oE "^${num}: veth[^@]+" | awk '{print $2}'

如果您需要在不包含cat命令的容器上查找信息,则可以尝试使用此工具:https://github.com/micahculpepper/dockerveth


3
您可以通过/proc/PID/net/igmp(将容器名称作为参数1)来读取接口名称,例如:

#!/bin/bash

NAME=$1
PID=$(docker inspect $NAME --format "{{.State.Pid}}")
while read iface id; do
    [[ "$iface" == lo ]] && continue
    veth=$(ip -br addr | sed -nre "s/(veth.*)@if$id.*/\1/p")
    echo -e "$NAME\t$iface\t$veth"
done < <(</proc/$PID/net/igmp awk '/^[0-9]+/{print $2 " " $1;}')

1
我添加了一个Github链接:https://github.com/dicho-usp/dockerveth - Johannes Di

0
稍微改进了@ln-s的解决方案(无法作为评论发布,因此单独回答)。
考虑到连接/断开容器与网络后,eth0变成eth1eth2等。
#!/bin/bash

export containers=$(sudo docker ps --format "{{.ID}}|{{.Names}}")
export interfaces=$(sudo ip ad);
for x in $containers
        do
                export name=$(echo "$x" |cut -d '|' -f 2);
                export id=$(echo "$x"|cut -d '|' -f 1)
                export ethNum="$(echo $(sudo docker exec -it "$id" ls /sys/class/net) | grep -oP 'eth[0-9]')"
                export ifaceNum="$(echo $(sudo docker exec -it "$id" cat /sys/class/net/$ethNum/iflink) | sed s/[^0-9]*//g):"
                export ifaceStr=$( echo "$interfaces" | grep $ifaceNum | cut -d ':' -f 2 | cut -d '@' -f 1);
                echo -e "$name: $ifaceStr";
done

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接