docker 网络工具 pipework 分析 - Herman.Liu
pipework介绍
docker 使用中,网络部分是其中最重要的一环,对docker容器进行网络设置,官方提供了 fannel ,同时也可以借助高大上的ovs来搞,但是这里也有一个小巧的工具 pipework,可以针对单个docker容器实现简单的网络设置,虽然不比上述两者全面,但是小、简是pipework的优势,个人非常喜欢这个工具。
pipework 地址:https://github.com/jpetazzo/pipework
给一个简单的例子来说下,比如,我要为我创建的docker使用一个我指定的ip:
$did=$(docker run -it -d --net=none baseimage:v1)pipework br0 $did 172.30.36.100/16@172.30.0.254
分析下,第一行,创建docker容器,注意其中的 --net=none 标示,不用自带的net分配模式。
第二行,我为这个容器分配ip:172.30.36.100,掩码16位,对应的gateway为:172.30.0.254,并将这个ip添加到bridge br0上。
在容器中查看:
~# docker exec $did ifconfig ~# eth1 Link encap:Ethernet HWaddr CA:81:40:85:5E:EAinet addr:172.30.36.100 Bcast:0.0.0.0 Mask:255.255.0.0 inet6 addr: fe80::c881:40ff:fe85:5eea/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:118503937 errors:0 dropped:1545004 overruns:0 frame:0 TX packets:389952 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:9431032966 (8.7 GiB) TX bytes:70378592 (67.1 MiB)lo Link encap:Local Loopbackinet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:345910 errors:0 dropped:0 overruns:0 frame:0 TX packets:345910 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:42510919 (40.5 MiB) TX bytes:42510919 (40.5 MiB)
~# docker exec $did route -n~# Destination Gateway Genmask Flags Metric Ref Use Iface0.0.0.0 172.30.0.254 0.0.0.0 UG 0 0 0 eth1172.30.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth1
ok,下面我们来看下,它这条命令究竟干了什么。
pipework流程代码解析
首先我先创建一个 net=none的容器:
docker run -it -d --net=none baseimage:v1
得到containerid : cdb492d88f1e
接下来进行操作
pipework br0 cdb492d88f1e 172.30.36.200/16@172.30.0.254
第一步,必然是先获取对应的变量值,在这里:
IFNAME=br0
GUESTNAME=cdb492d88f1e
IPADDR=172.30.36.100/16@172.30.0.254
之后判断br0的类型:
if [ -d "/sys/class/net/$IFNAME" ] then if [ -d "/sys/class/net/$IFNAME/bridge" ]; then IFTYPE=bridge BRTYPE=linux fifi
这时,IFTYPE=bridge,BRTYPE=linux
第二步,找到这个guest,目前只支持LXC container, 先找到cgroup地址:
while read _ mnt fstype options _; do [ "$fstype" != "cgroup" ] && continue echo "$options" | grep -qw devices || continue CGROUPMNT=$mntdone < /proc/mounts
此时, CGROUPMNT=/sys/fs/cgroup/devices (笔者使用的是centos7 x86_64,可能因人而异)
之后,从docker中获取对应的pid:
DOCKERPID=$(docker inspect --format='{{ .State.Pid }}' "$GUESTNAME")
此时, DOCKERPID=21927(因人而异),即 所对应docker容器中的init进程在宿主机上对应的pid;
分解 gateway:
GATEWAY="${IPADDR#*@}"GATEWAY="${GATEWAY%%@*}"IPADDR="${IPADDR%%@*}"
此时,GATEWAY=172.30.0.254, IPADDR=172.30.36.100/16
第三步,预操作结束,开始实际操作。
首先,添加net namespace,可参考文章 "linux network namespace 学习 "
因为ip netns list直接读取/var/run/netns中的内容,于是
rm -f "/var/run/netns/$DOCKERPID"ln -s "/proc/$DOCKERPID/ns/net" "/var/run/netns/$DOCKERPID" #将网络命名空间文件软链接到/var/run/netns,以便ip netns能够读取
之后创建一个veth pair
[ "$IFTYPE" = bridge ] && { LOCAL_IFNAME="v${CONTAINER_IFNAME}pl${NSPID}" GUEST_IFNAME="v${CONTAINER_IFNAME}pg${NSPID}" # Does the link already exist? if ip link show "$LOCAL_IFNAME" >/dev/null 2>&1; then # link exists, is it in use? if ip link show "$LOCAL_IFNAME" up | grep -q "UP"; then echo "Link $LOCAL_IFNAME exists and is up" exit 1 fi # delete the link so we can re-add it afterwards ip link del "$LOCAL_IFNAME" fi ip link add name "$LOCAL_IFNAME" mtu "$MTU" type veth peer name "$GUEST_IFNAME" mtu "$MTU" (ip link set "$LOCAL_IFNAME" master "$IFNAME" > /dev/null 2>&1) || (brctl addif "$IFNAME" "$LOCAL_IFNAME") ip link set "$LOCAL_IFNAME" up}
在这里,创建了一对veth pair ,LOCAL_IFNAME=veth1pl24305, GUEST_IFNAME=veth1pg24305
LOCAL_IFNAME 已和本地br0相绑定。
GUEST_IFNAME 待加入到容器中。
然后,将GUEST_IFNAME添加入docker 容器中:
ip link set "$GUEST_IFNAME" netns "$DOCKERPID"ip netns exec "$DOCKERPID" ip link set "$GUEST_IFNAME" name "$CONTAINER_IFNAME"
这样,容器内就添加了一个veth网卡和宿主机做了关联,但是现在在容器内部还看不见他,最后执行:
ip netns exec "$DOCKERPID" ip addr add "$IPADDR" dev "$CONTAINER_IFNAME"[ "$GATEWAY" ] && { ip netns exec "$DOCKERPID" ip route delete default >/dev/null 2>&1 && true}ip netns exec "$DOCKERPID" ip link set "$CONTAINER_IFNAME" up[ "$GATEWAY" ] && { ip netns exec "$DOCKERPID" ip route get "$GATEWAY" >/dev/null 2>&1 || \ ip netns exec "$DOCKERPID" ip route add "$GATEWAY/32" dev "$CONTAINER_IFNAME" ip netns exec "$DOCKERPID" ip route replace default via "$GATEWAY"}
OK,让我来查看下:
~# docker exec 17e5e58553ae ifconfig~# eth1 Link encap:Ethernet HWaddr 2A:E8:48:5C:77:9Finet addr:172.30.36.200 Bcast:0.0.0.0 Mask:255.255.255.255 inet6 addr: fe80::28e8:48ff:fe5c:779f/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:16 errors:0 dropped:0 overruns:0 frame:0 TX packets:16 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:960 (960.0 b) TX bytes:960 (960.0 b)lo Link encap:Local Loopbackinet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:16 errors:0 dropped:0 overruns:0 frame:0 TX packets:16 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:960 (960.0 b) TX bytes:960 (960.0 b)
搞定! 最后来个清理:
rm -f "/var/run/netns/$DOCKERPID"
Done