Intro
某日辦公室要斷電維護(hù),作為 IT 狗需要在斷電之前把辦公室的服務(wù)器都停掉,來(lái)電后再把這些服務(wù)器啟起來(lái),其中有臺(tái)服務(wù)器,跑的是 CoreOS 系統(tǒng),上面一堆的 docker container。系統(tǒng)起來(lái)后,我發(fā)現(xiàn)這些 docker container,一個(gè)都不通,顯然,踩著大坑了......
環(huán)境
- CoreOS stable (1185.5.0)
- 4.7.3-coreos-r3
- ip utility, iproute2-ss150210
- Pipework 20150123
現(xiàn)象
簡(jiǎn)言之,就是用 net=none 啟動(dòng)的 docker 實(shí)例,用 Pipework 配置 ip 地址后 ping 不通,用代碼表示是這樣的:
docker run -i -t -d \
--name=test \
-h test \
--net none \
centos:6;
sudo pipework br0 \
-i eth0 \
test \
10.0.0.3/24@10.0.0.1;
# br0 是橋,ip 地址是 10.0.0.2
# enp0s25 是直接在 10.0.0.0/24 網(wǎng)段的物理設(shè)備
# enp0s25 同時(shí)是橋 br0 的一個(gè) interface
# eth0 是 docker 的網(wǎng)卡設(shè)備名
# 這個(gè)測(cè)試 docker 的 ip 地址是 10.0.0.3
# 10.0.0.0/24 的網(wǎng)關(guān)是 10.0.0.1
結(jié)果 10.0.0.0/24 段的其他機(jī)器上 ping 不通 10.0.0.3,
docker exec -it test /bin/bash
進(jìn)入 docker 實(shí)例 test 內(nèi)部以后也 ping 不通網(wǎng)關(guān) 10.0.0.1
原因
最早懷疑是 proxy_arp 的問(wèn)題,但實(shí)際證明不是,最后發(fā)現(xiàn)是由于 docker 建立的 veth 設(shè)備(宿主機(jī)這端的)沒(méi)有正確添加到橋設(shè)備 br0 里去,從而導(dǎo)致怎么都不通。
解決起來(lái)也很簡(jiǎn)單,直接用
sudo brctl addif br0 vethlxxx
# vethlxxx 是這個(gè) docker 實(shí)例在宿主機(jī)側(cè)的網(wǎng)絡(luò)設(shè)備
然后就通了。
分析問(wèn)題
找到表面原因容易,可為什么沒(méi)能正確把 vethlxxx 設(shè)備加到橋設(shè)備 br0 里去呢?也不是第一次跑 Pipework 了,以前一直是好的。
看了看 Pipework 的代碼,并調(diào)試執(zhí)行了幾遍,發(fā)現(xiàn)這個(gè)問(wèn)題不是百分百出現(xiàn)的,只是有很大一部分概率。
最后大概定位問(wèn)題在 Pipework 的這幾句代碼上:
(ip link set "$LOCAL_IFNAME" master "$IFNAME" > /dev/null 2>&1) \
|| (brctl addif "$IFNAME" "$LOCAL_IFNAME")
和后面的
ip link set "$LOCAL_IFNAME" up
為什么這么說(shuō)呢,因?yàn)樵诟戒浀哪莻€(gè)鏈接里,有段話(huà)說(shuō)道:
To add an interface (e.g. eth0) into the bridge, its state must be up:
這也就是說(shuō)將某個(gè) interface 加入到橋設(shè)備之前,必須要先保證這個(gè) interface 的狀態(tài)是 up 的,但顯然在 Pipework 這份代碼里不是這樣的,這里是先加入到 br0,然后再將這個(gè) interface 設(shè)置為 up 的。
為了證明這個(gè),寫(xiě)了一段 bash 腳本,來(lái)模擬這個(gè)情況,具體如下(文件名叫 test.sh):
#!/bin/bash
set -x
IFNAME="brtest"
MTU=1500
(ip link add dev "$IFNAME" type bridge ) \
|| (brctl addbr "$IFNAME")
ip link set "$IFNAME" up
for i in {10..90}; do
LOCAL_IFNAME="vethltest${i}"
GUEST_IFNAME="vethgtest${i}"
ip link add name "$LOCAL_IFNAME" \
mtu "$MTU" type veth \
peer name "$GUEST_IFNAME" \
mtu "$MTU"
# ip link show "$LOCAL_IFNAME"
if ((${i}%2)); then
ip link set "$LOCAL_IFNAME" down
else
ip link set "$LOCAL_IFNAME" up
fi
(ip link set "$LOCAL_IFNAME" master "$IFNAME") \
|| (brctl addif "$IFNAME" "$LOCAL_IFNAME")
ip link set "$LOCAL_IFNAME" up
done
在 CoreOS 上執(zhí)行:
chmod +x test.sh # 這句執(zhí)行一遍即可
sudo ./test.sh
最后再
brctl show brtest
看結(jié)果,發(fā)現(xiàn):
- vethltest${i}(i 為偶數(shù)的設(shè)備)都被正確添加到 brtest 里
- vethltest${i}(i 為奇數(shù)的設(shè)備)有一些沒(méi)有被正確添加到 brtest 里
結(jié)論:
在我所測(cè)試的平臺(tái)(CoreOS)上,Pipework 是有問(wèn)題的,原因來(lái)自于調(diào)用的命令 ip(來(lái)自于軟件包 iproute2)
多說(shuō)一句
隨后我把測(cè)試程序 test.sh 拷貝到一臺(tái) CentOS 7 上跑,發(fā)現(xiàn) CentOS 7 沒(méi)有這個(gè)問(wèn)題。
Appendix
參考了一些網(wǎng)上的文檔: