docker: how to get veth bridge interface pair easi

2019-03-15 16:53发布

i have 2 containers by docker, and bridge like this:

root@venus-166:~# 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 

bridge name bridge id       STP enabled interfaces
docker0     8000.72b675c52895   no      vethRQOy1I
                            vethjKYWka

How can i get which container match veth* ?

ef99 => vethRQOy1I or ef99 => vethjKYWka

//----------------------------------------------------------

I know it works by ethtool, but is there any better way?

6条回答
贼婆χ
2楼-- · 2019-03-15 17:25

I don't know how to get it properly, but you use a hack: you can scan the syslog for added interfaces after you run your container:

#!/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"
查看更多
Animai°情兽
3楼-- · 2019-03-15 17:27

Try this script:

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

Explains:

  • avoid to execute commands in container.
  • avoid to create temporary folders and files.
  • MOST importantly, avoid to get incorrect answers for containers whose NetworkMode is host, none, or container:<name|id> (share network stack with another container's. For example: user's containers in one pod in kubernetes share the network stack with the pause pod container's network stack)
查看更多
Animai°情兽
4楼-- · 2019-03-15 17:27
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)
查看更多
姐就是有狂的资本
5楼-- · 2019-03-15 17:28

Here's a variation on the ethtool trick mentioned above, without actually using 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}"
}
查看更多
甜甜的少女心
6楼-- · 2019-03-15 17:31

If anyone is still interested in this. I found this on the docker mailing list: http://permalink.gmane.org/gmane.comp.sysutils.docker.user/3182

You can define the name of the veth yourself by passing the lxc-conf parameter "lxc.network.veth.pair". E.g.:

docker run -rm -i -t --lxc-conf="lxc.network.veth.pair=foobar" ubuntu /bin/bash

Creates a container with a veth interface named "foobar".

See this page for more handy lxc-conf parameters: http://manpages.ubuntu.com/manpages/precise/man5/lxc.conf.5.html

查看更多
欢心
7楼-- · 2019-03-15 17:37

There are multiple "hackish" ways to do it:

  • scan kernel logs, as mentioned by Jiri (but you have to do it right after starting the container, otherwise it gets messy);
  • check the interface counters (sent/received packets/bytes) in the container, then compare with the interfaces in the host, and find the pair that matches exactly (but with sent and receive directions flipped);
  • use an iptables LOG rule.

The last option is, IMHO, the more reliable one (and the easiest to use), but it's still very hackish. The idea is very simple:

  1. Add an iptables rule to log e.g. ICMP traffic arriving on the Docker bridge:

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

  2. Send a ping to the container you want to identify:

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

    ping -c 1 $IPADDR

  3. Check kernel logs:

    dmesg | grep $IPADDR

    You will see a line looking like this:

    […] 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 …

    If you want to be fancy, just extract PHYSIN=… with awk or sed.

  4. Remove the iptables logging rule (unless you want to leave it there because you will regularly ping containers to identify them).

If you need a bullet-proof version, you can install ulogd and use the ULOG target. Instead of just writing packet headers to the kernel log, it will send them through a netlink socket, and a userland program can then process them properly.

查看更多
登录 后发表回答