記一次 K8S HostPort 引發(fā)的服務(wù)故障排錯指南
問題背景
![記一次 K8S HostPort 引發(fā)的服務(wù)故障排錯指南](http://haohuigou.com/wp-content/themes/module/themer/assets/images/lazy.png)
排查過程
![記一次 K8S HostPort 引發(fā)的服務(wù)故障排錯指南](http://haohuigou.com/wp-content/themes/module/themer/assets/images/lazy.png)
-
這幾條規(guī)則是誰入到 iptables 中的? -
怎么解決呢,是不是刪掉就可以?
問題復(fù)現(xiàn)
同樣是 Mysql,為何 Mysql-A 沒有呢? 那么比對一下這兩個 Mysql 的部署差異
比對發(fā)現(xiàn), 除了用戶名密碼,ns 不一樣外,Mysql-B 部署時使用了 hostPort=3306, 其它的并無異常
難道是因為 hostPort?
作者日常會使用 NodePort,倒卻是沒怎么在意 hostPort,也就停留在 hostPort 跟 NodePort 的差別在于 NodePort 是所有 Node 上都會開啟端口,而 hostPort 只會在運行機(jī)器上開啟端口,由于 hostPort 使用的也少,也就沒太多關(guān)注,網(wǎng)上短暫搜了一番,描述的也不是很多,看起來大家也用的不多
那到底是不是因為 hostPort 呢?
Talk is cheap, show me the code
通過實驗來驗證,這里簡單使用了三個 nginx 來說明問題, 其中兩個使用了 hostPort,這里特意指定了不同的端口,其它的都完全一樣,發(fā)布到集群中,yaml 文件如下
![記一次 K8S HostPort 引發(fā)的服務(wù)故障排錯指南](http://haohuigou.com/wp-content/themes/module/themer/assets/images/lazy.png)
Finally,問題復(fù)現(xiàn):
![記一次 K8S HostPort 引發(fā)的服務(wù)故障排錯指南](http://haohuigou.com/wp-content/themes/module/themer/assets/images/lazy.png)
可以肯定,這些規(guī)則就是因為使用了 hostPort 而寫入的,但是由誰寫入的這個問題還是沒有解決?
罪魁禍?zhǔn)?/span>
作者開始以為這些 iptables 規(guī)則是由 kube-proxy 寫入的, 但是查看 kubelet 的源碼并未發(fā)現(xiàn)上述規(guī)則的關(guān)鍵字
再次實驗及結(jié)合網(wǎng)上的探索,可以得到以下結(jié)論:
首先從 kubernetes 的官方發(fā)現(xiàn)以下描述:
The CNI networking plugin supports hostPort. You can use the official portmap[1] plugin offered by the CNI plugin team or use your own plugin with portMapping functionality.
If you want to enable hostPort support, you must specify portMappings capability in your cni-conf-dir. For example:
![記一次 K8S HostPort 引發(fā)的服務(wù)故障排錯指南](http://haohuigou.com/wp-content/themes/module/themer/assets/images/lazy.png)
也就是如果使用了 hostPort, 是由 portmap 這個 cni 提供 portMapping 能力,同時,如果想使用這個能力,在配置文件中一定需要開啟 portmap,這個在作者的集群中也開啟了,這點對應(yīng)上了
另外一個比較重要的結(jié)論是:
The CNI ‘portmap’ plugin, used to setup HostPorts for CNI, inserts rules at the front of the iptables nat chains; which take precedence over the KUBE- SERVICES chain. Because of this, the HostPort/portmap rule could match incoming traffic even if there were better fitting, more specific service definition rules like NodePorts later in the chain
參考:?https://ubuntu.com/security/CVE-2019-9946
翻譯過來就是使用 hostPort 后,會在 iptables 的 nat 鏈中插入相應(yīng)的規(guī)則,而且這些規(guī)則是在 KUBE- SERVICES 規(guī)則之前插入的,也就是說會優(yōu)先匹配 hostPort 的規(guī)則,我們常用的 NodePort 規(guī)則其實是在 KUBE- SERVICES 之中,也排在其后
從 portmap 的源碼中果然是可以看到相應(yīng)的代碼
![記一次 K8S HostPort 引發(fā)的服務(wù)故障排錯指南](http://haohuigou.com/wp-content/themes/module/themer/assets/images/lazy.png)
所以,最終是調(diào)用 portmap 寫入的這些規(guī)則。
端口占用
進(jìn)一步實驗發(fā)現(xiàn),hostport 可以通過 iptables 命令查看到, 但是無法在 ipvsadm 中查看到
使用 lsof/netstat 也查看不到這個端口,這是因為 hostport 是通過 iptables 對請求中的目的端口進(jìn)行轉(zhuǎn)發(fā)的,并不是在主機(jī)上通過端口監(jiān)聽
![記一次 K8S HostPort 引發(fā)的服務(wù)故障排錯指南](http://haohuigou.com/wp-content/themes/module/themer/assets/images/lazy.png)
既然 lsof 跟 netstat 都查不到端口信息,那這個端口相當(dāng)于沒有處于 listen 狀態(tài)?
如果這時再部署一個 hostport 提定相同端口的應(yīng)用會怎么樣呢?
結(jié)論是: 使用 hostPort 的應(yīng)用在調(diào)度時無法調(diào)度在已經(jīng)使用過相同 hostPort 的主機(jī)上,也就是說,在調(diào)度時會考慮 hostport
如果強(qiáng)行讓其調(diào)度在同一臺機(jī)器上,那么就會出現(xiàn)以下錯誤,如果不刪除的話,這樣的錯誤會越來越多,嚇的作者趕緊刪了.
![記一次 K8S HostPort 引發(fā)的服務(wù)故障排錯指南](http://haohuigou.com/wp-content/themes/module/themer/assets/images/lazy.png)
如果這個時候創(chuàng)建一個 nodePort 類型的 svc, 端口也為 31123,結(jié)果會怎么樣呢?
![記一次 K8S HostPort 引發(fā)的服務(wù)故障排錯指南](http://haohuigou.com/wp-content/themes/module/themer/assets/images/lazy.png)
![記一次 K8S HostPort 引發(fā)的服務(wù)故障排錯指南](http://haohuigou.com/wp-content/themes/module/themer/assets/images/lazy.png)
可以發(fā)現(xiàn),NodePort 是可以成功創(chuàng)建的,同時監(jiān)聽的端口也出現(xiàn)了.
從這也可以說明使用 hostposrt 指定的端口并沒有 listen 主機(jī)的端口,要不然這里就會提示端口重復(fù)之類
那么問題又來了,同一臺機(jī)器上同時存在有 hostPort 跟 nodePort 的端口,這個時候如果 curl 31123 時, 訪問的是哪一個呢?
經(jīng)多次使用 curl 請求后,均是使用了 hostport 那個 nginx pod 收到請求
原因還是因為 KUBE-NODE-PORT 規(guī)則在 KUBE-SERVICE 的鏈中是處于最后位置,而 hostPort 通過 portmap 寫入的規(guī)則排在其之前
![記一次 K8S HostPort 引發(fā)的服務(wù)故障排錯指南](http://haohuigou.com/wp-content/themes/module/themer/assets/images/lazy.png)
因此會先匹配到 hostport 的規(guī)則,自然請求就被轉(zhuǎn)到 hostport 所在的 pod 中,這兩者的順序是沒辦法改變的,因此無論是 hostport 的應(yīng)用發(fā)布在前還是在后都無法影響請求轉(zhuǎn)發(fā)
另外再提一下,hostport 的規(guī)則在 ipvsadm 中是查詢不到的,而 nodePort 的規(guī)則則是可以使用 ipvsadm 查詢得到
問題解決
要想把這些規(guī)則刪除,可以直接將 hostport 去掉,那么規(guī)則就會隨著刪除,比如下圖中去掉了一個 nginx 的 hostport
![記一次 K8S HostPort 引發(fā)的服務(wù)故障排錯指南](http://haohuigou.com/wp-content/themes/module/themer/assets/images/lazy.png)
另外使用較多的 port-forward 也是可以進(jìn)行端口轉(zhuǎn)發(fā)的,它又是個什么情況呢? 它其實使用的是 socat 及 netenter 工具,網(wǎng)上看到一篇文章,原理寫的挺好的,感興趣的可以看一看
參考:?https://vflong.github.io/sre/k8s/2020/03/15/how-the-kubectl-port-forward-command-works.html
生產(chǎn)建議
一句話,生產(chǎn)環(huán)境除非是必要且無他法,不然一定不要使用 hostport,除了會影響調(diào)度結(jié)果之外,還會出現(xiàn)上述問題,可能造成的后果是非常嚴(yán)重的
來源:https://izsk.me/2021/08/01/Kubernetes-hostport/
文章轉(zhuǎn)載:高效運維
(版權(quán)歸原作者所有,侵刪)