<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          好家伙,hostPort 竟然劫持了我的請(qǐng)求

          共 9477字,需瀏覽 19分鐘

           ·

          2021-08-06 00:43


          最近排查了一個(gè) kubernetes 中使用了 hostport 后遇到比較坑的問(wèn)題,奇怪的知識(shí)又增加了。

          問(wèn)題背景

          集群環(huán)境為 K8s v1.15.9,cni 指定了 flannel-vxlan 跟 portmap, kube-proxy 使用 mode 為 ipvs,集群 3 臺(tái) master,同時(shí)也是 node,這里以 node-1,node-2,node-3 來(lái)表示。

          集群中有 2 個(gè) mysql, 部署在兩個(gè) ns 下,mysql 本身不是問(wèn)題重點(diǎn),這里就不細(xì)說(shuō),這里以 mysql-A,mysql-B 來(lái)表示。

          mysql-A 落在 node-1 上,mysql-B 落在 node-2 上, 兩個(gè)數(shù)據(jù)庫(kù) svc 名跟用戶(hù)、密碼完全不相同

          出現(xiàn)詭異的現(xiàn)象這里以一張圖來(lái)說(shuō)明會(huì)比較清楚一些:

          其中綠線(xiàn)的表示訪(fǎng)問(wèn)沒(méi)有問(wèn)題,紅線(xiàn)表示連接 Mysql-A 提示用戶(hù)名密碼錯(cuò)誤

          特別詭異的是,當(dāng)在 Node-2 上通過(guò) svc 訪(fǎng)問(wèn) Mysql-A 時(shí),輸入 Mysql-A 的用戶(hù)名跟密碼提示密碼錯(cuò)誤,密碼確認(rèn)無(wú)疑,但當(dāng)輸入 Mysql-B 的用戶(hù)名跟密碼,居然能夠連接上,看了下數(shù)據(jù),連上的是 Mysql-B 的數(shù)據(jù)庫(kù),給人的感覺(jué)就是請(qǐng)求轉(zhuǎn)到了 Mysql-A, 最后又轉(zhuǎn)到了 Mysql-B,當(dāng)時(shí)讓人大跌眼鏡

          碰到詭異的問(wèn)題那就排查吧,排查的過(guò)程倒是不費(fèi)什么事,最主要的是要通過(guò)這次踩坑機(jī)會(huì)挖掘一些奇怪的知識(shí)出來(lái)。

          排查過(guò)程

          既然在 Node-1 上連接 Mysql-A/Mysql-B 都沒(méi)有問(wèn)題,那基本可以排查是 Mysql-A 的問(wèn)題

          經(jīng)實(shí)驗(yàn),在 Node-2 上所有的服務(wù)想要連 Mysql-A 時(shí),都有這個(gè)問(wèn)題,但是訪(fǎng)問(wèn)其它的服務(wù)又都沒(méi)有問(wèn)題,說(shuō)明要么是 mysql-A 的 3306 這個(gè)端口有問(wèn)題,通過(guò)上一步應(yīng)該排查了 mysql-A 的問(wèn)題,那問(wèn)題只能出在 Node-2 上

          在 k8s 中像這樣的請(qǐng)求轉(zhuǎn)發(fā)出現(xiàn)詭異現(xiàn)象,當(dāng)排除了一些常見(jiàn)的原因之外,最大的嫌疑就是 iptables 了,作者遇到過(guò)多次

          這次也不例外,雖然當(dāng)前集群使用的 ipvs, 但還是照例看下 iptables 規(guī)則,查看 Node-2 上的 iptables 與 Node-1 的 iptables 比對(duì),結(jié)果有蹊蹺, 在 Node-2 上發(fā)現(xiàn)有以下的規(guī)則在其它節(jié)點(diǎn)上沒(méi)有

          -A CNI-DN-xxxx -p tcp -m tcp --dport 3306 -j DNAT --to-destination 10.224.0.222:3306
          -A CNI-HOSTPORT-DNAT -m comment --comment "dnat name": \"cni0\" id: \"xxxxxxxxxxxxx\"" -j CNI-DN-xxx
          -A CNI-HOSTPORT-SNAT -m comment --comment "
          snat name": \"cni0\" id: \"xxxxxxxxxxxxx\"" -j CNI-SN-xxx
          -A CNI-SN-xxx -s 127.0.0.1/32 -d 10.224.0.222/32 -p tcp -m tcp --dport 80 -j MASQUERADE

          其中 10.224.0.222 為 Mysql-B 的 pod ip, xxxxxxxxxxxxx 經(jīng)查實(shí)為 Mysql-B 對(duì)應(yīng)的 pause 容器的 id

          從上面的規(guī)則總結(jié)一下就是目的為 3306 端口的請(qǐng)求都會(huì)轉(zhuǎn)發(fā)到 10.224.0.222 這個(gè)地址,即 Mysql-B

          看到這里,作者明白了為什么在 Node-2 上去訪(fǎng)問(wèn) Node-1 上 Mysql-A 的 3306 會(huì)提示密碼錯(cuò)誤而輸入 Mysql-B 的密碼卻可以正常訪(fǎng)問(wèn)

          雖然兩個(gè) mysql 的 svc 名不一樣,但上面的 iptables 只要目的端口是 3306 就轉(zhuǎn)發(fā)到 Mysql-B 了,當(dāng)請(qǐng)求到達(dá) mysql 后,使用正確的用戶(hù)名密碼自然可以登錄成功

          原因是找到了,但是又引出來(lái)了更多的問(wèn)題?

          1. 這幾條規(guī)則是誰(shuí)入到 iptables 中的?
          2. 怎么解決呢,是不是刪掉就可以?

          問(wèn)題復(fù)現(xiàn)

          同樣是 Mysql,為何 Mysql-A 沒(méi)有呢? 那么比對(duì)一下這兩個(gè) Mysql 的部署差異

          比對(duì)發(fā)現(xiàn), 除了用戶(hù)名密碼,ns 不一樣外,Mysql-B 部署時(shí)使用了 hostPort=3306, 其它的并無(wú)異常

          難道是因?yàn)?hostPort?

          作者日常會(huì)使用 NodePort,倒卻是沒(méi)怎么在意 hostPort,也就停留在 hostPort 跟 NodePort 的差別在于 NodePort 是所有 Node 上都會(huì)開(kāi)啟端口,而 hostPort 只會(huì)在運(yùn)行機(jī)器上開(kāi)啟端口,由于 hostPort 使用的也少,也就沒(méi)太多關(guān)注,網(wǎng)上短暫搜了一番,描述的也不是很多,看起來(lái)大家也用的不多

          那到底是不是因?yàn)?hostPort 呢?

          Talk is cheap, show me the code

          通過(guò)實(shí)驗(yàn)來(lái)驗(yàn)證,這里簡(jiǎn)單使用了三個(gè) nginx 來(lái)說(shuō)明問(wèn)題, 其中兩個(gè)使用了 hostPort,這里特意指定了不同的端口,其它的都完全一樣,發(fā)布到集群中,yaml 文件如下

          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: nginx-hostport2
            labels:
              k8s-app: nginx-hostport2
          spec:
            replicas: 1
            selector:
              matchLabels:
                k8s-app: nginx-hostport2
            template:
              metadata:
                labels:
                  k8s-app: nginx-hostport2
              spec:
                nodeName: spring-38
                containers:
                  - name: nginx
                    image: nginx:latest
                    ports:
                      - containerPort: 80
                        hostPort: 31123

          Finally,問(wèn)題復(fù)現(xiàn):

          可以肯定,這些規(guī)則就是因?yàn)槭褂昧?hostPort 而寫(xiě)入的,但是由誰(shuí)寫(xiě)入的這個(gè)問(wèn)題還是沒(méi)有解決?

          罪魁禍?zhǔn)?/span>

          作者開(kāi)始以為這些 iptables 規(guī)則是由 kube-proxy 寫(xiě)入的, 但是查看 kubelet 的源碼并未發(fā)現(xiàn)上述規(guī)則的關(guān)鍵字

          再次實(shí)驗(yà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:

          {
            "name""k8s-pod-network",
            "cniVersion""0.3.0",
            "plugins": [
              {
                  # ...其它的plugin
              }
              {
                "type""portmap",
                "capabilities": {"portMappings"true}
              }
            ]
          }

          參考: https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/

          也就是如果使用了 hostPort, 是由 portmap 這個(gè) cni 提供 portMapping 能力,同時(shí),如果想使用這個(gè)能力,在配置文件中一定需要開(kāi)啟 portmap,這個(gè)在作者的集群中也開(kāi)啟了,這點(diǎn)對(duì)應(yīng)上了

          另外一個(gè)比較重要的結(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

          翻譯過(guò)來(lái)就是使用 hostPort 后,會(huì)在 iptables 的 nat 鏈中插入相應(yīng)的規(guī)則,而且這些規(guī)則是在 KUBE- SERVICES 規(guī)則之前插入的,也就是說(shuō)會(huì)優(yōu)先匹配 hostPort 的規(guī)則,我們常用的 NodePort 規(guī)則其實(shí)是在 KUBE- SERVICES 之中,也排在其后

          從 portmap 的源碼中果然是可以看到相應(yīng)的代碼

          感興趣的可以的plugins[2]項(xiàng)目的 meta/portmap/portmap.go 中查看完整的源碼

          所以,最終是調(diào)用 portmap 寫(xiě)入的這些規(guī)則.

          端口占用

          進(jìn)一步實(shí)驗(yàn)發(fā)現(xiàn),hostport 可以通過(guò) iptables 命令查看到, 但是無(wú)法在 ipvsadm 中查看到

          使用 lsof/netstat 也查看不到這個(gè)端口,這是因?yàn)?hostport 是通過(guò) iptables 對(duì)請(qǐng)求中的目的端口進(jìn)行轉(zhuǎn)發(fā)的,并不是在主機(jī)上通過(guò)端口監(jiān)聽(tīng)

          既然 lsof 跟 netstat 都查不到端口信息,那這個(gè)端口相當(dāng)于沒(méi)有處于 listen 狀態(tài)?

          如果這時(shí)再部署一個(gè) hostport 提定相同端口的應(yīng)用會(huì)怎么樣呢?

          結(jié)論是: 使用 hostPort 的應(yīng)用在調(diào)度時(shí)無(wú)法調(diào)度在已經(jīng)使用過(guò)相同 hostPort 的主機(jī)上,也就是說(shuō),在調(diào)度時(shí)會(huì)考慮 hostport

          如果強(qiáng)行讓其調(diào)度在同一臺(tái)機(jī)器上,那么就會(huì)出現(xiàn)以下錯(cuò)誤,如果不刪除的話(huà),這樣的錯(cuò)誤會(huì)越來(lái)越多,嚇的作者趕緊刪了.

          如果這個(gè)時(shí)候創(chuàng)建一個(gè) nodePort 類(lèi)型的 svc, 端口也為 31123,結(jié)果會(huì)怎么樣呢?

          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: nginx-nodeport2
            labels:
              k8s-app: nginx-nodeport2
          spec:
            replicas: 1
            selector:
              matchLabels:
                k8s-app: nginx-nodeport2
            template:
              metadata:
                labels:
                  k8s-app: nginx-nodeport2
              spec:
                nodeName: spring-38
                containers:
                  - name: nginx
                    image: nginx:latest
                    ports:
                      - containerPort: 80
          ---
          apiVersion: v1
          kind: Service
          metadata:
            name: nginx-nodeport2
          spec:
            type: NodePort
            ports:
            - port: 80
              targetPort: 80
              nodePort: 31123
            selector:
              k8s-app: nginx-nodeport2

          可以發(fā)現(xiàn),NodePort 是可以成功創(chuàng)建的,同時(shí)監(jiān)聽(tīng)的端口也出現(xiàn)了.

          從這也可以說(shuō)明使用 hostposrt 指定的端口并沒(méi)有 listen 主機(jī)的端口,要不然這里就會(huì)提示端口重復(fù)之類(lèi)

          那么問(wèn)題又來(lái)了,同一臺(tái)機(jī)器上同時(shí)存在有 hostPort 跟 nodePort 的端口,這個(gè)時(shí)候如果 curl 31123 時(shí), 訪(fǎng)問(wèn)的是哪一個(gè)呢?

          經(jīng)多次使用 curl 請(qǐng)求后,均是使用了 hostport 那個(gè) nginx pod 收到請(qǐng)求

          原因還是因?yàn)?KUBE-NODE-PORT 規(guī)則在 KUBE-SERVICE 的鏈中是處于最后位置,而 hostPort 通過(guò) portmap 寫(xiě)入的規(guī)則排在其之前

          因此會(huì)先匹配到 hostport 的規(guī)則,自然請(qǐng)求就被轉(zhuǎn)到 hostport 所在的 pod 中,這兩者的順序是沒(méi)辦法改變的,因此無(wú)論是 hostport 的應(yīng)用發(fā)布在前還是在后都無(wú)法影響請(qǐng)求轉(zhuǎn)發(fā)

          另外再提一下,hostport 的規(guī)則在 ipvsadm 中是查詢(xún)不到的,而 nodePort 的規(guī)則則是可以使用 ipvsadm 查詢(xún)得到

          問(wèn)題解決

          要想把這些規(guī)則刪除,可以直接將 hostport 去掉,那么規(guī)則就會(huì)隨著刪除,比如下圖中去掉了一個(gè) nginx 的 hostport

          另外使用較多的 port-forward 也是可以進(jìn)行端口轉(zhuǎn)發(fā)的,它又是個(gè)什么情況呢? 它其實(shí)使用的是 socat 及 netenter 工具,網(wǎng)上看到一篇文章,原理寫(xiě)的挺好的,感興趣的可以看一看

          參考: https://vflong.github.io/sre/k8s/2020/03/15/how-the-kubectl-port-forward-command-works.html

          生產(chǎn)建議

          一句話(huà),生產(chǎn)環(huán)境除非是必要且無(wú)他法,不然一定不要使用 hostport,除了會(huì)影響調(diào)度結(jié)果之外,還會(huì)出現(xiàn)上述問(wèn)題,可能造成的后果是非常嚴(yán)重的

          參考文章

          • https://www.qikqiak.com/post/how-to-use-ipvs-in-kubernetes/[3]
          • https://serenafeng.github.io/2020/03/26/kube-proxy-in-iptables-mode/[4]
          • https://zhuanlan.zhihu.com/p/94418251[5]
          • https://ronaknathani.com/blog/2020/07/kubernetes-nodeport-and-iptables-rules/[6]
          • https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/[7]
          • https://vflong.github.io/sre/k8s/2020/03/15/how-the-kubectl-port-forward-command-works.html[8]

          腳注

          [1]

          portmap: https://github.com/containernetworking/plugins/tree/master/plugins/meta/portmap

          [2]

          plugins: https://github.com/containernetworking/plugins.git

          [3]

          https://www.qikqiak.com/post/how-to-use-ipvs-in-kubernetes/: https://www.qikqiak.com/post/how-to-use-ipvs-in-kubernetes/

          [4]

          https://serenafeng.github.io/2020/03/26/kube-proxy-in-iptables-mode/: https://serenafeng.github.io/2020/03/26/kube-proxy-in-iptables-mode/

          [5]

          https://zhuanlan.zhihu.com/p/94418251: https://zhuanlan.zhihu.com/p/94418251

          [6]

          https://ronaknathani.com/blog/2020/07/kubernetes-nodeport-and-iptables-rules/: https://ronaknathani.com/blog/2020/07/kubernetes-nodeport-and-iptables-rules/

          [7]

          https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/: https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/

          [8]

          https://vflong.github.io/sre/k8s/2020/03/15/how-the-kubectl-port-forward-command-works.html: https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/

          原文鏈接:https://izsk.me/2021/08/01/Kubernetes-hostport/


          你可能還喜歡

          點(diǎn)擊下方圖片即可閱讀

          容器應(yīng)用優(yōu)雅關(guān)閉的終極大招

          云原生是一種信仰 ??

          關(guān)注公眾號(hào)

          后臺(tái)回復(fù)?k8s?獲取史上最方便快捷的 Kubernetes 高可用部署工具,只需一條命令,連 ssh 都不需要!



          點(diǎn)擊 "閱讀原文" 獲取更好的閱讀體驗(yàn)!


          發(fā)現(xiàn)朋友圈變“安靜”了嗎?

          瀏覽 58
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  丁香五月资源 | 国产成人午夜精品一区 | 欧美另类欧美另类欧美另类 | 日韩免费福利视频 | 1级片黄页网站 |