docker:如何轻松获得 veth 桥接接口对?
Posted
技术标签:
【中文标题】docker:如何轻松获得 veth 桥接接口对?【英文标题】:docker: how to get veth bridge interface pair easily? 【发布时间】:2014-03-10 13:25:55 【问题描述】:我有 2 个 docker 容器,像这样桥接:
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ef99087167cb images.docker.sae.sina.com.cn/ubuntu:latest /bin/bash -c /home/c 2 days ago Up 21 minutes 0.0.0.0:49240->22223/tcp night_leve3
c8a7b18ec20d images.docker.sae.sina.com.cn/ubuntu:latest /bin/bash -c /home/c 2 days ago Up 54 minutes 0.0.0.0:49239->22223/tcp night_leve2
#brctl show cbr0
bridge name bridge id STP enabled interfaces
docker0 8000.72b675c52895 no vethRQOy1I
vethjKYWka
我如何获得与 veth*
匹配的容器?
ef99 => vethRQOy1I or ef99 => vethjKYWka
//--------------------------------------------- -------------
我知道ethtool
有效,但有没有更好的方法?
【问题讨论】:
ethtool 如何做到这一点? 管道接口索引好像是连续的,例如:vethXXX是19那么容器的eth索引必须是18,ethtool -S vethXXX,可以得到索引,可以登录容器查看索引或猜测该对 【参考方案1】:这是上面提到的 ethtool 技巧的一种变体,但实际上并未使用 ethtool:
function veth_interface_for_container()
# Get the process ID for the container named $1:
local pid=$(docker inspect -f '.State.Pid' "$1")
# Make the container's network namespace available to the ip-netns command:
mkdir -p /var/run/netns
ln -sf /proc/$pid/ns/net "/var/run/netns/$1"
# Get the interface index of the container's eth0:
local index=$(ip netns exec "$1" ip link show eth0 | head -n1 | sed s/:.*//)
# Increment the index to determine the veth index, which we assume is
# always one greater than the container's index:
let index=index+1
# Write the name of the veth interface to stdout:
ip link show | grep "^$index:" | sed "s/$index: \(.*\):.*/\1/"
# Clean up the netns symlink, since we don't need it anymore
rm -f "/var/run/netns/$1"
【讨论】:
我似乎没有足够的声誉来评论“lxc.network.veth.pair”的答案,但我会注意到最近版本的 Docker 默认使用 libcontainer 而不是 LXC,在这种情况下,任何通过 --lxc-conf 传递的选项都会被忽略。 这个答案在 2020 年绝对不适用于 GKE :-D。准备再发一个【参考方案2】:有多种“hackish”方法可以做到这一点:
扫描内核日志,如 Jiri 所说(但必须在启动容器后立即进行,否则会变得混乱); 检查容器中的接口计数器(发送/接收的数据包/字节),然后与主机中的接口进行比较,找到完全匹配的对(但发送和接收方向相反); 使用 iptablesLOG
规则。
最后一个选项是,恕我直言,它更可靠(并且最容易使用),但它仍然非常老套。这个想法很简单:
添加一个 iptables 规则来记录,例如到达 Docker 网桥的 ICMP 流量:
sudo iptables -I INPUT -i docker0 -p icmp -j LOG
向您要识别的容器发送 ping:
IPADDR=$(docker inspect -format='.NetworkSettings.IPAddress' 0c33)
ping -c 1 $IPADDR
检查内核日志:
dmesg | grep $IPADDR
您将看到如下所示的一行:
[…] IN=docker0 OUT= PHYSIN=vethv94jPK MAC=fe:2c:7f:2c:ab:3f:42:83:95:74:0b:8f:08:00 SRC=172.17.0.79 …
如果你想花哨,只需用 awk 或 sed 提取 PHYSIN=…
。
删除 iptables 日志记录规则(除非您想将其保留在那里,因为您会定期 ping 容器以识别它们)。
如果您需要防弹版本,可以安装ulogd
并使用ULOG
目标。它不只是将数据包头写入内核日志,而是通过 netlink 套接字发送它们,然后用户级程序可以正确处理它们。
【讨论】:
【参考方案3】:试试这个脚本:
get_network_mode()
docker inspect --format='.HostConfig.NetworkMode' "$1"
created_by_kubelet()
[[ $(docker inspect --format='.Name' "$1") =~ ^/k8s_ ]]
for container_id in $(docker ps -q); do
network_mode=$(get_network_mode "$container_id")
# skip the containers whose network_mode is 'host' or 'none',
# but do NOT skip the container created by kubelet.
if [[ "$network_mode" == "host" || \
$(! created_by_kubelet "$container_id") && "$network_mode" == "none" ]]; then
echo "$container_id => $network_mode"
continue
fi
# if one container's network_mode is 'other container',
# then get its root parent container's network_mode.
while grep container <<< "$network_mode" -q; do
network_mode=$(get_network_mode "$network_mode/container:/")
# skip the containers whose network_mode is 'host' or 'none',
# but do NOT skip the container created by kubelet.
if [[ "$network_mode" == "host" || \
$(! created_by_kubelet "$container_id") && "$network_mode" == "none" ]]; then
echo "$container_id => $network_mode"
continue 2
fi
done
# get current container's 'container_id'.
pid=$(docker inspect --format='.State.Pid' "$container_id")
# get the 'id' of veth device in the container.
veth_id=$(nsenter -t "$pid" -n ip link show eth0 |grep -oP '(?<=eth0@if)\d+(?=:)')
# get the 'name' of veth device in the 'docker0' bridge (or other name),
# which is the peer of veth device in the container.
veth_name=$(ip link show |sed -nr "s/^$veth_id: *([^ ]*)@if.*/\1/p")
echo "$container_id => $veth_name"
done
解释:
避免在container
中执行命令。
避免创建临时文件夹和文件。
最重要的是,避免为NetworkMode
为host
、none
或container:<name|id>
的容器得到错误答案(与另一个容器共享network stack
。例如:user's
容器在一个@987654329 @在 kubernetes 中与pause pod
容器的network stack
共享network stack
)
【讨论】:
【参考方案4】:如果还有人对此感兴趣。我在 docker 邮件列表中找到了这个:http://permalink.gmane.org/gmane.comp.sysutils.docker.user/3182
您可以通过传递 lxc-conf 参数“lxc.network.veth.pair”自己定义 veth 的名称。例如:
docker run -rm -i -t --lxc-conf="lxc.network.veth.pair=foobar" ubuntu /bin/bash
使用名为“foobar”的 veth 接口创建一个容器。
更多方便的 lxc-conf 参数请参见此页面:http://manpages.ubuntu.com/manpages/precise/man5/lxc.conf.5.html
【讨论】:
这些标志已被弃用【参考方案5】:据我所知,您需要与容器关联的虚拟网络设备吗?
您可以通过以下方式获得它:
1:
docker exec -it <container> cat /sys/class/net/<physical-device>/iflink
# the output looks like this -> 20
然后
2:
# ip ad | grep <the output, like 20>:
ip ad | grep 20:
# the output looks, like this:
# 20: vetha5531eb@if19: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-0595ab5d7c95 state UP group default qlen 1000
# where vetha5531eb is what I think you're looking for.
【讨论】:
【参考方案6】:我不知道如何正确获取它,但您使用了一个 hack:您可以在运行容器后扫描 syslog 以查找添加的接口:
#!/bin/sh
JOB=$(sudo docker run -d ...)
sleep 1s
INTERFACE=$(grep "docker0: port" /var/log/syslog | tail -n 1 | sed -r s/^.*\(veth[^\)]+\).*$/\\1/)
echo "job: $JOB interface: $INTERFACE"
【讨论】:
【参考方案7】:dmesg --clear
for i in $(docker inspect $(docker ps -a -q) | grep IPAddress | cut -d\" -f4); do ping -c 1 -w 1 $i >/dev/null; done
while read line
do
IPADDRESS=$(docker inspect $line | grep IPAddress | cut -d\" -f4)
NAME=$(docker inspect $line | grep Name | cut -d/ -f2 | cut -d\" -f1)
FOUND=$(dmesg | grep $IPADDRESS | grep -Po 'vet[A-Za-z0-9]+' | sort -u)
echo "GEVONDEN $IPADDRESS MET NAAM : $NAME en INTERFACE: $FOUND" | grep NAAM
done < <(docker ps -a -q)
【讨论】:
【参考方案8】:这只是 Joel Dice 在“2015 年 2 月 19 日”给出的答案的更新
原始码(2015年有效)
# Get the interface index of the container's eth0:
local index=$(ip netns exec "$1" ip link show eth0 | head -n1 | sed s/:.*//)
# Increment the index to determine the veth index, which we assume is
# always one greater than the container's index:
let index=index+1
# Write the name of the veth interface to stdout:
ip link show | grep "^$index:" | sed "s/$index: \(.*\):.*/\1/"
结果:
$ index=$(sudo ip netns exec "ns-4012085" ip link show eth0 | head -n1 | sed s/:.*//)
$ echo $index
3
$ let index=index+1
$ echo $index
4
$ sudo ip link show | grep "^$index:" | sed "s/$index: \(.*\):.*/\1/"
cbr0
而:
index=$(sudo ip netns exec "ns-4012085" ip link show type veth | grep eth0 | sed s/.*@if// | sed s/:.*// )
$ echo $index
14
$ ip link show | grep "^$index:" | sed "s/$index: \(.*\):.*/\1/"
veth240a8f81@if3
希望对某人有所帮助。 :)
附:我是从this thread 来的。
【讨论】:
以上是关于docker:如何轻松获得 veth 桥接接口对?的主要内容,如果未能解决你的问题,请参考以下文章
docker 实战---多台物理主机的联网,容器桥接到物理网络