Pipework在CoreOS上的一个小bug
Intro
某日办公室要断电维护,作为 IT 狗需要在断电之前把办公室的服务器都停掉,来电后再把这些服务器启起来,其中有台服务器,跑的是 CoreOS 系统,上面一堆的 docker container。系统起来后,我发现这些 docker container,一个都不通,显然,踩着大坑了……
环境
- CoreOS stable (1185.5.0)
- 4.7.3-coreos-r3
- ip utility, iproute2-ss150210
- Pipework 20150123
现象
简言之,就是用 net=none 启动的 docker 实例,用 Pipework 配置 ip 地址后 ping 不通,用代码表示是这样的:
1 | docker run -i -t -d \ |
结果 10.0.0.0/24 段的其他机器上 ping 不通 10.0.0.3,
1 | docker exec -it test /bin/bash |
进入 docker 实例 test 内部以后也 ping 不通网关 10.0.0.1
原因
最早怀疑是 proxy_arp 的问题,但实际证明不是,最后发现是由于 docker 建立的 veth 设备(宿主机这端的)没有正确添加到桥设备 br0 里去,从而导致怎么都不通。
解决起来也很简单,直接用
1 | sudo brctl addif br0 vethlxxx |
然后就通了。
分析问题
找到表面原因容易,可为什么没能正确把 vethlxxx 设备加到桥设备 br0 里去呢?也不是第一次跑 Pipework 了,以前一直是好的。
看了看 Pipework 的代码,并调试执行了几遍,发现这个问题不是百分百出现的,只是有很大一部分概率。
最后大概定位问题在 Pipework 的这几句代码上:
1 | (ip link set "$LOCAL_IFNAME" master "$IFNAME" > /dev/null 2>&1) \ |
和后面的
1 | ip link set "$LOCAL_IFNAME" up |
为什么这么说呢,因为在附录的那个链接里,有段话说道:
To add an interface (e.g. eth0) into the bridge, its state must be up:
这也就是说将某个 interface 加入到桥设备之前,必须要先保证这个 interface 的状态是 up 的,但显然在 Pipework 这份代码里不是这样的,这里是先加入到 br0,然后再将这个 interface 设置为 up 的。
为了证明这个,写了一段 bash 脚本,来模拟这个情况,具体如下(文件名叫 test.sh):
1 |
|
在 CoreOS 上执行:
1 | chmod +x test.sh # 这句执行一遍即可 |
最后再
1 | brctl show brtest |
看结果,发现:
- vethltest${i}(i 为偶数的设备)都被正确添加到 brtest 里
- vethltest${i}(i 为奇数的设备)有一些没有被正确添加到 brtest 里
结论:
在我所测试的平台(CoreOS)上,Pipework 是有问题的,原因来自于调用的命令 ip(来自于软件包 iproute2)
多说一句
随后我把测试程序 test.sh 拷贝到一台 CentOS 7 上跑,发现 CentOS 7 没有这个问题。
Appendix
参考了一些网上的文档: