【问题标题】:docker: how to get veth bridge interface pair easily?docker:如何轻松获得 veth 桥接接口对?
【发布时间】: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,可以得到索引,可以登录容器查看索引或猜测该对

标签: networking docker bridge


【解决方案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 规则。

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

  1. 添加一个 iptables 规则来记录,例如到达 Docker 网桥的 ICMP 流量:

    sudo iptables -I INPUT -i docker0 -p icmp -j LOG

  2. 向您要识别的容器发送 ping:

    IPADDR=$(docker inspect -format='{{.NetworkSettings.IPAddress}}' 0c33)

    ping -c 1 $IPADDR

  3. 检查内核日志:

    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=…

  4. 删除 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 来的。

            【讨论】:

              猜你喜欢
              • 2021-06-30
              • 2014-04-13
              • 2012-01-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-10-08
              • 1970-01-01
              相关资源
              最近更新 更多