《一個Docker附加參數引起的網絡服務異常》要點:
本文介紹了一個Docker附加參數引起的網絡服務異常,希望對您有用。如果有疑問,可以聯系我們。
事發在線上環境,OpenStack由容器化部署方案Kolla提供,各主要軟件版本如下:
2017/3/17: 測試完成修復方案,上線更新;
2017/3/15: 找到root cause,確定觸發場景,確定修復方案;
2017/3/14: 找到neutron產生無限多tap的場景2,能夠在模擬條件下復現問題,與環境日志記錄匹配;
2017/3/13: 找到neutron產生無限多tap的場景1,能夠在模擬條件下復現問題,但對比環境日志并不完全匹配,而且模擬條件的出現概率非常低,排除;
……
2017/3/11
09:00:00: 在控制節點恢復網絡服務(neutron-metadata-agent、neutron-dhcp-agent);
08:00:00: 自動化腳本清理完成所有無效tap設備,控制節點成功啟動neutron-*-agent,虛擬網絡服務能力恢復;
……
2017/3/10
22:00:00: 運行腳本自動化清理控制節點的無效tap設備
21:30:00: 原控制節點所屬(6臺)虛擬機恢復訪問在其他節點(nc05、nc03)恢復原控制節點的虛擬機
21:00:00: 環境更新包功能還原在其他節點(nc05)成功啟動neutron-metadata-agent
20:30:00: 整體環境新建虛機分配網絡還原在其他節點(nc05)成功啟動?neutron-dhcp-agent
18:30:00: 放棄在控制節點還原網絡服務,嘗試在其他節點還原網絡服務;
18:00:00: 嘗試刪除tap設備,但進度較慢;嘗試在代碼去掉neutron-openvsiwtch-agent中關于tap設備的預讀過程,因涉及點太多放棄在線修改;
17:00:00: 發現控制節點存在10000+tap設備,link狀態為DOWN(導致neutorn-openvswitch-agent啟動失敗) ;
16:40:00: 整體環境新建虛擬機分配網絡失效,處理人忙亂之中再次重啟neutron-dhcp-agent,發現循環錯誤,neutron-dhcp-agent重啟失敗;
16:30:00: 控制節點所屬(6臺)虛擬機網絡失效懷疑是控制節點流表未刷新原因,開發同事嘗試重啟控制節點的neutron-openvsiwtch-agent刷新,但重啟失敗;
16:25:00: 開發開始定位處理開發同事檢查虛擬機創建成功,但新建虛擬機無法獲取neutron-metadata服務接口,導致業務集群配置失敗;
16:10:00: 線上環境更新包功能失效項目維護同事反映培訓環境更新業務應用集群失效;
經過內部環境反復測試與現場日志對比,發現實際產生10000多個tap設備的循環位于neutron-dhcp-agent服務的定時同步功能函數中
下面這段整體邏輯由于一個設置namespace的異常而不斷重試循環:
1)?Neutron-dhcp-agent循環監聽是否存在更新需求(need_resunc_reasons);
2)?每次循環延遲間隔conf.resync_interval秒;
3)?setup_dhcp_port()方法中申請一個新的Port(新的tapid產生);
4)?add_veth()方法創建veth設備(新的tap設備產生);
5)?ensure_namespace()方法中確認namespace,如不存在則創建;
6)?set_netns()方法設置tap設備的network namespace,設置失敗;
7)?跳轉到第一步循環;
可以看到,這段邏輯本身有一定缺陷
1)?設置namespace失敗后,沒有正確的try…catch流程刪除之前創建的設備,導致失敗的tap設備積累越來越多
2)?ensure_namespace()方法只檢查namespace是否存在,沒有深入檢查namespace權限等可能導致后續設置失敗的屬性
接下來的問題是(可能也是neutron在最后一步不設防的原因):namespace是neutron-dhcp-agent進程自身創建的,tap設備也是neutron-dhcp-agent進程自身創建的,為什么設置時會失敗呢?
容器服務的隔離與共享
其中各容器共享主機的Network namespace,但每個容器具備非共享的Mount namespace;在各自獨立的Mount namespace中,共享主機/run/netns目錄,用于共享虛擬網絡的network namespace操作入口.
Mount
而在Docker實現中,(共享)使用外部存儲空間、數據卷功能都最終會依賴mount系統調用,代碼片段:
Docker對附加傳入的private、shared等不同屬性的處理,實際對應執行mount系統調用時傳入不同flags,不同的flags對應到不同的Mount Propagation Type:
MS_SHARED
This mount point shares mount and unmount events with other mount points that are members of its “peer group”. When a mount point is added or removed under this mount point, this change will propagate to the peer group, so that the mount or unmount will also take place under each of the peer mount points. Propagation also occurs in the reverse direction, so that mount and unmount events on a peer mount will also propagate to this mount point.
MS_PRIVATE
This is the converse of a shared mount point. The mount point does not propagate events to any peers, and does not receive propagation events from any peers.
MS_SLAVE
This propagation type sits midway between shared and private. A slave mount has a master—a shared peer group whose members propagate mount and unmount events to the slave mount. However, the slave mount does not propagate events to the master peer group.
MS_UNBINDABLE
This mount point is unbindable. Like a private mount point, this mount point does not propagate events to or from peers. In addition, this mount point can’t be the source for a bind mount operation.
找到原因
由于啟動容器時對/run目錄沒有使用MS_SHARED傳播類型,容器重啟后,之前創建的namespace文件會因/run/netns產生的`peer group`內其他peer的引用計數不能正常刪除(Device or resource busy)
umount(“/run/netns/ns1”, MNT_DETACH) = -1 EINVAL (Invalid argument)
unlink(“/run/netns/ns1”) ???????????= -1 EBUSY (Device or resource busy)
刪除失敗帶來的后續結果是,由于文件系統層的namespace文件沒有完全刪除,而實際的networknamespace已經釋放,所以這個“半刪除”的namespace從用戶態程序的角度就呈現出這樣的狀態:能夠查看到namespace,對應2.1第5)步中ensure_namespace()操作正常,但set操作時會失敗,對應2.1第6)步set_ns()操作失敗
setns(4, 1073741824) ???????????????????= -1 EINVAL (Invalid argument)
write(2, “seting the network namespace \”ns”…, 60seting the network namespace “ns1” failed: Invalid argument
解決方法
修改neutron-*-agent容器的啟動參數:
– -v /run/netns:/run/netns:shared ?-v /run:/run:rw
+ -v /run/netns:/run/netns:shared ?-v /run:/run:rw:shared
模擬再現故障
1)?創建網絡和子網
neutron net-create –shared –provider:network_type vlan –provider:physical_network physnet1 –provider:segmentation_id 108 vlan108
neutron subnet-create –name subnet108 vlan108 192.168.0.0/24
2)?重啟neutron_dhcp_agent容器
docker restart neutron_dhcp_agent
這時neutron_dhcp_agent所創建的namespace已被docker daemon和其他容器引用,權限已轉移不允許neutron_dhcp_agent刪除
3)?刪除該網絡的子網
neutron subnet-delete subnet108
neutron刪除子網成功,但后臺實際刪除namespace失敗,而且namespace命名空間已釋放,但文件系統接口任然存在,處于“半刪除”狀態,直接刪除網絡不會觸發后續異常.
4)?為該網絡重新創建子網
neutron subnet-create –name subnet108 vlan108 172.16.0.0/24
neutron創建子網成功,這時后臺應當重新創建namespace,但由于上一步刪除namespace動作失敗導致namespace非正常殘留,所以這里跳過創建namespace動作,接下來為tap設備設置namespace的動作失敗,開始進入2.1描述的循環狀態.
用Docker模擬局部故障
docker run -d –name testa -it –v /run:/run:rw -v /run/netns:/run/netns:shared –privileged –net=host nova-compute:latest bash
docker run -d –name testb -it –v /run:/run:rw -v /run/netns:/run/netns:shared –privileged –net=host nova-compute:latest bash
2)?在容器A中創建namespace?ns1
docker exec -u root testa ip netns add ns1
3)?重啟容器A
docker restart testa
4)?使用容器A刪除之前創建的namespace
docker exec -u root testa ip netns del ns1
Cannot remove namespace file “/var/run/netns/ns1”: Device or resource busy
5)?使用容器A設置namespace
docker exec -u root testa ip netns exec ns1 ip a
seting the network namespace “ns1” failed: Invalid argument
3.?小結
Mount陷阱
每個Mount namespace有自己獨立的文件系統視圖,但是這種隔離性同時也帶來一些問題:比如,當系統加載一塊新的磁盤時,在最初的實現中每個namespace必須單獨掛載磁盤.為此內核在2.6.15引入了shared subtrees feature:“The key benefit of shared subtrees is to allow automatic, controlled propagation of mount and unmount events between namespaces. This means, for example, that mounting an optical disk in one mount namespace can trigger a mount of that disk in all other namespaces.”?每個掛載點都會標記Propagation type,用于決定在當前掛載點下創建/刪除(子)掛載點時,是否傳播到別的掛載點.功能同樣帶來潛在的復雜,如2.2描述的權限傳播轉移出乎使用者的預料.
目前容器技術的存儲管理和使用最終都依賴Mount系統調用,后續的使用場景需注意.
應該溫柔的重試
如果neutron的重試機制“聰明”一點,就不會累計產生越來越多的tap設備,也就不會造成實際用戶可見的網絡異常.更好的方式是采用隨機化、指數型遞增的重試周期,有時候系統出現的一個小故障可能會導致重試請求同時出現,這些請求可能會逐漸放大故障.在不同場景應該考慮限制某個請求的重試次數或者進程整體在單位時間內的重試配額.
謹慎對待重啟
我們常使用重啟服務“快刀斬亂麻”,解決一般性問題,但從這件事的處理經過來看,兩次重啟服務使問題影響范圍不斷擴大,而且在其他很多場景下,重啟服務時程序會重新讀取外部資源,也常常會暴露出很多已經潛在、但尚未產生影響的問題.所以,在生產環境應該避免草率重啟,而且還應在服務可中斷時間有計劃的演練重啟,以便提前發現、解決未來被動重啟時才會暴露的問題.
容器化的利與弊
這次事故由容器啟動參數的不正確使用引起,但同樣因為容器利于部署應用的特性,現場較快的在其他節點部署恢復了網絡服務,減少了業務中斷時間.
附錄參考:
Docker基礎技術——Linux Namespace http://coolshell.cn/articles/17010.html
Mount namespace and mount propagation http://hustcat.github.io/mount-namespace-and-mount-propagation/
Shared Subtrees https://lwn.net/Articles/159077/
Mount namespaces and shared trees https://lwn.net/Articles/689856/
作者:崔昊之,云技術開發者和愛好者,Openstack化石玩家,Docker社區committer,就職于中電科華云,希望和大家分享云技術實踐過程中有趣的事兒.
文章來自微信公眾號:云計術實踐
轉載請注明本頁網址:
http://www.snjht.com/jiaocheng/4084.html