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 所说(但必须在启动容器后立即进行,否则会变得混乱); 检查容器中的接口计数器(发送/接收的数据包/字节),然后与主机中的接口进行比较,找到完全匹配的对(但发送和接收方向相反); 使用 iptables LOG 规则。

最后一个选项是,恕我直言,它更可靠(并且最容易使用),但它仍然非常老套。这个想法很简单:

    添加一个 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中执行命令。 避免创建临时文件夹和文件。 最重要的是,避免为NetworkModehostnonecontainer:&lt;name|id&gt; 的容器得到错误答案(与另一个容器共享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多台物理主机之间的容器互联

docker桥接模式

docker 实战---多台物理主机的联网,容器桥接到物理网络

docker+openvswitch实现主机与容器的网络通信

Docker 之容器网络隔离模型

docker--linux network namespace