引子:
線上的k8s集群內部樟氢,每一臺機器,都有2塊網卡创倔,一塊兒em1(10網段)嗡害,一個 em2(192網段)。然后畦攘,線上的機器霸妹,通過iptables限制為:訪問192段的服務任意端口都是暢通的,而訪問10段的服務端口知押,只有333端口可以連通叹螟。333端口,是線上機器的ssh端口台盯。
線上集群的k8s罢绽,使用的calico網絡。
線上有一個問題静盅,就是容器訪問當前宿主機192段IP的999端口不通良价,訪問其他機器192段IP的9999端口暢通。
我們知道蒿叠,當從容器訪問宿主機的應用時明垢,會重新進入iptables的INPUT鏈,而線上的iptables INPUT鏈如下:
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 1477K 2519M cali-INPUT all -- * * 0.0.0.0/0 0.0.0.0/0 /* cali:Cz_u1IQiXIMmKD4c */
2 272K 20M KUBE-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
3 273K 20M KUBE-FIREWALL all -- * * 0.0.0.0/0 0.0.0.0/0
4 263K 20M ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
5 0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0
6 393 17292 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
7 11880 1019K ACCEPT all -- em2 * 0.0.0.0/0 0.0.0.0/0
8 7 308 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:333
9 228 62052 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
意思是:
①:如果報文是途徑em2網卡市咽,訪問任意其他網卡痊银。則全部放行。(這就是為什么192段的訪問全部放行)
②:如果報文是tcp施绎,不管報文從哪個網卡出來溯革,去往哪個網卡贞绳,只要目的端口是333,就放行致稀。(這就是為什么10段的訪問冈闭,只能訪問333)
我們容器發(fā)出的報文,網卡是 cal 網卡(calico的網卡名稱)豺裆,所以拒秘,容器只能訪問333。
錯誤操作:
要把9999端口放開臭猜,意味著,我要添加開放9999端口到INPUT鏈路中押蚤,為了讓iptables再重啟后依然生效蔑歌,我將規(guī)則,添加到
/etc/sysconfig/iptables
在添加完成后其實并未生效揽碘,然后次屠,有2種方式,用于讓iptables生效:
①:手動添加規(guī)則雳刺,比如 iptables -t filter -I INPUT 9 -s 172.20.0.0/14 -p tcp -m tcp --dport 9999 -j ACCEPT
②:重啟iptables(會自動加載 /etc/sysconfig/iptables內容 )
我選擇了后者劫灶,之所以選擇后者,其實是因為掖桦,即便重啟了iptables本昏,加載了剛剛的規(guī)則,原來的k8s和calico的iptables會丟失枪汪,但是涌穆,k8s會重建自己的整套iptables規(guī)則的。
事實上雀久,k8s確實也在我重啟iptables后宿稀,重建了iptables,但問題來了赖捌。
引發(fā)的2個網絡問題:
1祝沸、我們線上的CI系統(tǒng),是基于drone越庇,做的二次開發(fā)罩锐,改動了很多的東西,添加了很多特性悦荒,結果上面一系列操作之后唯欣,我發(fā)現,我們的CI系統(tǒng)中搬味,個別鏡像的構建(Dockerfile中的RUN操作境氢,有網絡相關操作時蟀拷,比如yum)會失敗。
2萍聊、線上的機器问芬,執(zhí)行 docker run,起來的容器寿桨,無法訪問外網了此衅。然而,k8s起來的pod里的容器亭螟,卻可以訪問外網挡鞍。
鏡像的構建,其實本質上预烙,也是起容器來做事情墨微,而且,你無法定制docker build操作時扁掸,使用的網絡模式翘县,只能是bridge模式。而 docker run 起來的容器谴分,默認也是 bridge 模式锈麸。結合上面2個內容,基本上可以確定牺蹄,此問題忘伞,和k8s無關,而是純粹的docker的問題了钞馁。
排查:
①:一開始以為是k8s的iptables重建不完整虑省,所以,多次用線上的機器的機器 nodeA(無人訪問的一臺)僧凰,不斷嘗試清空iptables探颈,等待k8s重建iptables,多次嘗試無效训措。
②:stop docker -> stop kubelet -> clean iptables -> start docker -> start kubelet伪节。這個操作的意義是,以為重啟docker绩鸣,重啟k8s怀大,docker 和 k8s 都會重建自己的 iptables。多次嘗試無效呀闻。
③:使用現在的k8s安裝工具:kubespray化借,重新安裝 nodeA,并添加到k8s集群內捡多。寄希望于安裝工具重裝docker和k8s蓖康,結果依然無效铐炫。
④:回歸問題本身,問題一定出在iptables上蒜焊。也是因為一開始重啟了iptables導致此一系列問題倒信。
解決過程:
既然問題出在iptables上,而我們要訪問外網泳梆,那么鳖悠,就得重iptables 報文轉出入手。好在線上的機器优妙,并沒有都為了適配9999端口重啟iptables乘综,有3臺 k8s 的 master 節(jié)點,并沒有重啟 iptables鳞溉。
然后瘾带,對比有問題機器、沒有問題機器的 FORWARD熟菲、POSTROUTING 的規(guī)則。發(fā)現朴恳,缺少DOCKER相關的規(guī)則抄罕,這些規(guī)則,本來是安裝docker之后于颖,由docker創(chuàng)建的呆贿。那么,解決此問題森渐,就需要重建docker的iptables做入。
①:找一臺干凈的機器(只安裝了docker,未啟動容器同衣,為安裝k8s)竟块,然后從中提取docker相關的規(guī)則
②:將提取的規(guī)則,與出問題的機器的iptables默認規(guī)則融合耐齐。
③:執(zhí)行 iptables-restore 恢復規(guī)則浪秘。
后續(xù)驗證:
在問題機器上,docker run centos 容器埠况,然后ping qq.com耸携,鏈路暢通。有一個小問題辕翰,就是如果你先啟動的容器執(zhí)行 ping 外網操作夺衍。然后才再目的機器上執(zhí)行 iptables-restore 的話,會發(fā)現喜命,容器的 ping沟沙,有一個短暫的耗時河劝,然后才會暢通,這是因為 iptables-restore 恢復規(guī)則后尝胆,有一個過程丧裁。
最后:
我整理一個相對標準的docker iptables規(guī)則備份,如果你也遇到了啟動的docker容器含衔,無法訪問外網的問題煎娇,可以通過下面的內容,恢復規(guī)則
將下面的規(guī)則贪染,保存到一個文件缓呛,比如 docker-iptables-backup
# Generated by iptables-save v1.4.21 on Thu Apr 30 20:48:42 2015
*nat
:PREROUTING ACCEPT [18:1080]
:INPUT ACCEPT [18:1080]
:OUTPUT ACCEPT [22:1550]
:POSTROUTING ACCEPT [22:1550]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 172.17.0.1/32 -d 172.17.0.1/32 -p tcp -m tcp --dport 80 -j MASQUERADE
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 3001 -j DNAT --to-destination 172.17.0.1:80
COMMIT
# Completed on Thu Apr 30 20:48:42 2015
# Generated by iptables-save v1.4.21 on Thu Apr 30 20:48:42 2015
*filter
:INPUT ACCEPT [495:53218]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [480:89217]
:DOCKER - [0:0]
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER -d 172.17.0.1/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPT
COMMIT
# Completed on Thu Apr 30 20:48:42 2015
然后,執(zhí)行恢復 iptables-restore docker-iptables-backup
注意:
①:如果你的docker在k8s集群內部杭隙,理論上來說哟绊,上面的操作會有清空iptables的能力,但是你不需要擔心痰憎,因為k8s會自行修復本身的iptables票髓,也就是說,k8s會重建自己的iptables铣耘。但是假如你的k8s沒能完整重建洽沟,就需要手動恢復k8s的iptables。如果是calico網絡蜗细,只需要刪除當前節(jié)點的calico pod即可裆操,刪除之后,k8s會重啟這個pod炉媒,自然也就會重建iptables了踪区。同理,flannel網絡也是一樣的吊骤。
②:上面的規(guī)則缎岗,有一個關鍵,是 172.17.0.0 和 172.17.0.1水援,這是docker默認安裝后的網段和docker0網卡的IP密强。如果你是默認安裝的docker,上面的規(guī)則不需要改動蜗元,但如果你定制了docker0網卡的IP或渤,記得修改一下上面的規(guī)則,適配你的環(huán)境奕扣。