日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

追踪 Kubernetes 中的网络流量

發(fā)布時間:2024/8/23 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 追踪 Kubernetes 中的网络流量 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

作者 | Addo Zhang

來源 |?云原生指北

譯者注:

這篇文章很全面的羅列出了 Kubernetes 中涉及的網(wǎng)絡知識,從 Linux 內(nèi)核的網(wǎng)絡內(nèi)容,到容器、Kubernetes,一一進行了詳細的說明。

文章篇幅有點長,不得不說,網(wǎng)絡是很復雜很麻煩的一層,但恰恰這層多年來變化不大。希望翻譯的內(nèi)容對大家能有所幫助,有誤的地方,也歡迎大家指正。

本文翻譯獲得?Learnk8s 的授權,原文?Tracing the path of network traffic in Kubernetes[1]?作者?Kristijan Mitevski。


TL;DR:?本文將代理了解 Kubernetes 集群內(nèi)外的數(shù)據(jù)流轉。從最初的 Web 請求開始,一直到托管應用程序的容器。

目錄

  • Kubernetes 網(wǎng)絡要求

  • Linux 網(wǎng)絡命名空間如果在 pod 中工作

  • Pause 容器創(chuàng)建 Pod 中的網(wǎng)絡命名空間

  • 為 Pod 分配了 IP 地址

  • 檢查集群中 pod 到 pod 的流量

  • Pod 命名空間連接到以太網(wǎng)橋接器

  • 跟蹤同一節(jié)點上 pod 間的流量

  • 跟蹤不同節(jié)點上 pod 間的通信

    • 位運算的工作原理

  • 容器網(wǎng)絡接口 - CNI

  • 檢查 pod 到服務的流量

  • 使用 Netfilter 和 Iptables 攔截和重寫流量

  • 檢查服務的響應

  • 回顧

Kubernetes 網(wǎng)絡要求

在深入了解 Kubernetes 中的數(shù)據(jù)流轉之前,讓我們先澄清下 Kubernetes 網(wǎng)絡的要求。

Kubernetes 網(wǎng)絡模型定義了一套基本規(guī)則:

  • 集群中的 pod 應該能夠與任何其他 pod 自由通信,而無需使用網(wǎng)絡地址轉換(NAT)。

  • 在不使用 NAT 的情況下,集群節(jié)點上運行的任意程序都應該能夠與同一節(jié)點上的任意 pod 通信

  • 每個 pod 都有自己的 IP 地址(IP-per Pod),其他 pod 都可以使用同一個地址進行訪問。

這些要求不會將實現(xiàn)限制在單一方案上。

相反,他們概括了集群網(wǎng)絡的特性。

在滿足這些限制時,必須解決如下挑戰(zhàn)[2]

  • 如何保證同一 pod 中的容器間的訪問就像在同一主機上一樣?

  • Pod 能否訪問集群中的其他 pod?

  • Pod 能否訪問服務(service)?以及服務可以負載均衡請求嗎?

  • Pod 可以接收來自集群外的流量嗎?

  • 本文將專注于前三點,從 pod 內(nèi)部網(wǎng)絡或者容器間的通信說起。

    Linux 網(wǎng)絡命名空間如果在 pod 中工作

    我們想象下,有一個承載應用程序的主容器和另一個與它一起運行的容器。

    在示例 Pod 中有一個 Nginx 容器和 busybox 容器:

    apiVersion:?v1 kind:?Pod metadata:name:?multi-container-pod spec:containers:-?name:?container-1image:?busyboxcommand:?['/bin/sh',?'-c',?'sleep?1d']-?name:?container-2image:?nginx

    在部署時,會出現(xiàn)如下情況:

  • Pod 在節(jié)點上得到自己的網(wǎng)絡命名空間

  • Pod?分配到一個 IP 地址,兩個容器間共享端口。

  • 兩個容器共享同一個網(wǎng)絡命名空間,在本地互相可見。

  • 網(wǎng)絡配置在后臺很快完成。

    然后,我們退后一步,是這理解為什么上面是容器運行所必須的。

    在 Linux 中,網(wǎng)絡命名空間是獨立的、隔離的邏輯空間。[3]

    可以將網(wǎng)絡命名空間堪稱將物理網(wǎng)絡接口分割成更小的獨立部分。

    每部分都可以單獨配置,并使用自己的網(wǎng)絡規(guī)則和資源。

    這些可以包括防火墻規(guī)則、接口(虛擬或物理)、路由和其他所有與網(wǎng)絡相關的內(nèi)容。

  • 物理接口持有根命名空間。

  • 可以使用 Linux 網(wǎng)絡命名空間創(chuàng)建隔離的網(wǎng)絡。每個網(wǎng)絡都是獨立的,除非進行配置否則不會與其他命名空間通信。

  • 物理接口必須處理最后的所有真實數(shù)據(jù)包,因此所有的虛擬接口都是從中創(chuàng)建的。

    網(wǎng)絡命名空間可以通過?`ip-netns` 管理工具[4]?來管理,可以使用?ip netns list?列出主機上的命名空間。

    請注意,創(chuàng)建的網(wǎng)絡命名空間將會出現(xiàn)在?/var/run/netns?目錄下,但?Docker 并沒有遵循這一點[5]

    例如,下面是 Kubernetes 節(jié)點的命名空間:

    $?ip?netns?list cni-0f226515-e28b-df13-9f16-dd79456825ac?(id:?3) cni-4e4dfaac-89a6-2034-6098-dd8b2ee51dcd?(id:?4) cni-7e94f0cc-9ee8-6a46-178a-55c73ce58f2e?(id:?2) cni-7619c818-5b66-5d45-91c1-1c516f559291?(id:?1) cni-3004ec2c-9ac2-2928-b556-82c7fb37a4d8?(id:?0)

    注意?cni-?前綴意味著命名空間的創(chuàng)建由 CNI 來完成。

    當創(chuàng)建 pod 并分配給節(jié)點時,CNI[6]?會:

  • 為其創(chuàng)建網(wǎng)絡命名空間。

  • 分配 IP 地址。

  • 將容器連接到網(wǎng)絡。

  • 如果 pod 像上面的示例一樣包含多個容器,則所有容器都被置于同一個命名空間中。

  • 創(chuàng)建 pod 時,CNI 為容器創(chuàng)建網(wǎng)絡命名空間

  • 然后分配 IP 地址

  • 最后將容器連接到網(wǎng)絡的其余部分

  • 那么當列出節(jié)點上的容器時會看到什么?

    可以 SSH 到 Kubernetes 節(jié)點來查看命名空間:

    $?lsns?-t?netNS?TYPE?NPROCS???PID?USER?????NETNSID?NSFS???????????????????????????COMMAND 4026531992?net?????171?????1?root??unassigned?/run/docker/netns/default??????/sbin/init?noembed?norestore 4026532286?net???????2??4808?65535??????????0?/run/docker/netns/56c020051c3b?/pause 4026532414?net???????5??5489?65535??????????1?/run/docker/netns/7db647b9b187?/pause

    lsns?命令會列出主機上所有的命名空間。

    記住 Linux 中有多種命名空間類型[7]

    Nginx 容器在哪?

    那么?pause?容器又是什么?

    Pause 容器創(chuàng)建 Pod 中的網(wǎng)絡命名空間

    從節(jié)點上的所有進程中找出 Nginx 容器:

    $?lsnsNS?TYPE???NPROCS???PID?USER????????????COMMAND #?truncated?output 4026532414?net?????????5??5489?65535???????????/pause 4026532513?mnt?????????1??5599?root????????????sleep?1d 4026532514?uts?????????1??5599?root????????????sleep?1d 4026532515?pid?????????1??5599?root????????????sleep?1d 4026532516?mnt?????????3??5777?root????????????nginx:?master?process?nginx?-g?daemon?off; 4026532517?uts?????????3??5777?root????????????nginx:?master?process?nginx?-g?daemon?off; 4026532518?pid?????????3??5777?root????????????nginx:?master?process?nginx?-g?daemon?off;

    該容器出現(xiàn)在了掛在(mount?mnt)、Unix 分時系統(tǒng)(Unix time-sharing?uts)和 PID(pid)命名空間中,但是并不在網(wǎng)絡命名空間(net)中。

    不幸的是,lsns?只顯示了每個進程最低的 PID,不過可以根據(jù)進程 ID 進一步過濾。

    可以通過以下內(nèi)容檢索Nginx 容器的所有命名空間:

    $?sudo?lsns?-p?5777NS?TYPE???NPROCS???PID?USER??COMMAND 4026531835?cgroup????178?????1?root??/sbin/init?noembed?norestore 4026531837?user??????178?????1?root??/sbin/init?noembed?norestore 4026532411?ipc?????????5??5489?65535?/pause 4026532414?net?????????5??5489?65535?/pause 4026532516?mnt?????????3??5777?root??nginx:?master?process?nginx?-g?daemon?off; 4026532517?uts?????????3??5777?root??nginx:?master?process?nginx?-g?daemon?off; 4026532518?pid?????????3??5777?root??nginx:?master?process?nginx?-g?daemon?off;

    pause?進程再次出現(xiàn),這次它劫持了網(wǎng)絡命名空間。

    那是什么?

    集群中的每個 pod 都有一個在后臺運行的隱藏容器,被稱為?pause

    列出節(jié)點上的所有容器并過濾出 pause 容器:

    $?docker?ps?|?grep?pause fa9666c1d9c6???k8s.gcr.io/pause:3.4.1??"/pause"??k8s_POD_kube-dns-599484b884-sv2js… 44218e010aeb???k8s.gcr.io/pause:3.4.1??"/pause"??k8s_POD_blackbox-exporter-55c457d… 5fb4b5942c66???k8s.gcr.io/pause:3.4.1??"/pause"??k8s_POD_kube-dns-599484b884-cq99x… 8007db79dcf2???k8s.gcr.io/pause:3.4.1??"/pause"??k8s_POD_konnectivity-agent-84f87c…

    將看到對于節(jié)點分配到的每個 pod,都有一個匹配的?pause?容器。

    該?pause?容器負責創(chuàng)建和維持網(wǎng)絡命名空間。

    它包含的代碼極少,部署后立即進入睡眠狀態(tài)。

    然而,它在 Kubernetes 生態(tài)中的首當其沖,發(fā)揮著至關重要的作用。[8]

  • 創(chuàng)建 pod 時,CNI 會創(chuàng)建一個帶有睡眠容器的網(wǎng)絡命名空間

  • Pod 中的所有容器都會加入到它創(chuàng)建的網(wǎng)絡命名空間中

  • 此時 CNI 分配 IP 地址并將容器連接到網(wǎng)絡

  • 進入睡眠狀態(tài)的容器有什么用?

    要了解它的實用性,我們可以想象下如示例一樣有兩個容器的 pod,但沒有?pause?容器。

    容器啟動,CNI:

  • 為 Nginx 容器創(chuàng)建一個網(wǎng)絡命名空間。

  • 把 busybox 容器加入到前面創(chuàng)建的網(wǎng)絡命名空間中。

  • 為 pod 分配 IP 地址。

  • 將容器連接到網(wǎng)絡。

  • 假如 Nginx 容器崩潰了會發(fā)生什么?

    CNI 將不得不再次完成所有流程,兩個容器的網(wǎng)絡都會中斷。

    由于?sleep?容器不太可能有任何 bug,因此創(chuàng)建網(wǎng)絡命名空間通常是一個更保險、更健壯的選擇。

    如果 pod 中的一個容器崩潰,其余的仍可以處理網(wǎng)絡請求。

    為 Pod 分配了 IP 地址

    前面提到 pod 和所有容器獲得了同樣的 IP。

    這是怎么配置的?

    在 pod 網(wǎng)絡命名空間中,創(chuàng)建一個接口并分配 IP 地址。

    我們來驗證下。

    首先,找到 pod 的 IP 地址:

    $?kubectl?get?pod?multi-container-pod?-o?jsonpath={.status.podIP} 10.244.4.40

    接下來,找到相關的網(wǎng)絡命名空間。

    由于網(wǎng)絡命名空間是從物理接口創(chuàng)建的,需要訪問集群節(jié)點。

    如果你運行的是 minikube,可以通過?minikube ssh?訪問節(jié)點。如果在云提供商中運行,應該有某種方法通過 SSH 訪問節(jié)點。

    進入后,可以找到創(chuàng)建的最新的網(wǎng)絡命名空間:

    $?ls?-lt?/var/run/netns total?0 -r--r--r--?1?root?root?0?Sep?25?13:34?cni-0f226515-e28b-df13-9f16-dd79456825ac -r--r--r--?1?root?root?0?Sep?24?09:39?cni-4e4dfaac-89a6-2034-6098-dd8b2ee51dcd -r--r--r--?1?root?root?0?Sep?24?09:39?cni-7e94f0cc-9ee8-6a46-178a-55c73ce58f2e -r--r--r--?1?root?root?0?Sep?24?09:39?cni-7619c818-5b66-5d45-91c1-1c516f559291 -r--r--r--?1?root?root?0?Sep?24?09:39?cni-3004ec2c-9ac2-2928-b556-82c7fb37a4d8

    本示例中,它是?cni-0f226515-e28b-df13-9f16-dd79456825ac。此時,可以在該命名空間總執(zhí)行?exec命令:

    $?ip?netns?exec?cni-0f226515-e28b-df13-9f16-dd79456825ac?ip?a #?output?truncated 3:?eth0@if12:?<BROADCAST,MULTICAST,UP,LOWER_UP>?mtu?1450?qdisc?noqueue?state?UP?group?defaultlink/ether?16:a4:f8:4f:56:77?brd?ff:ff:ff:ff:ff:ff?link-netnsid?0inet?10.244.4.40/32?brd?10.244.4.40?scope?global?eth0valid_lft?forever?preferred_lft?foreverinet6?fe80::14a4:f8ff:fe4f:5677/64?scope?linkvalid_lft?forever?preferred_lft?forever

    10.244.4.40?就是 pod 的 IP 地址。

    通過查找?@if12?中的?12?找到網(wǎng)絡接口。

    $?ip?link?|?grep?-A1?^12 12:?vethweplb3f36a0@if16:?mtu?1376?qdisc?noqueue?master?weave?state?UP?mode?DEFAULT?group?defaultlink/ether?72:1c:73:d9:d9:f6?brd?ff:ff:ff:ff:ff:ff?link-netnsid?1

    還可以驗證 Nginx 容器是否從該命名空間中監(jiān)聽 HTTP 流量:

    $?ip?netns?exec?cni-0f226515-e28b-df13-9f16-dd79456825ac?netstat?-lnp Active?Internet?connections?(only?servers) Proto?Recv-Q?Send-Q?Local?Address???????????Foreign?Address?????????State???????PID/Program?name tcp????????0??????0?0.0.0.0:80??????????????0.0.0.0:*???????????????LISTEN??????692698/nginx:?master tcp6???????0??????0?:::80???????????????????:::*????????????????????LISTEN??????692698/nginx:?master

    如果無法通過 SSH 訪問集群的節(jié)點,可以試試?kubectl exec?進入到 busybox 容器,然后使用?ip?和?netstat?命令。

    太棒了!

    現(xiàn)在我們已經(jīng)介紹了容器間的通信,接下來看看 Pod 與 Pod 直接如何建立通信。

    檢查集群中 pod 到 pod 的流量

    當說起 pod 間通信時,會有兩種可能:

  • Pod 流量流向同一節(jié)點上的 pod。

  • Pod 流量流量另一個節(jié)點上的 pod。

  • 為了使整個設置正常工作,我們需要之前討論過的虛擬接口和以太網(wǎng)橋接。

    在繼續(xù)之前,我們先討論下他們的功能以及為什么他們時必需的。

    要完成 pod 與其他 pod 的通信,它必須先訪問節(jié)點的根命名空間。

    這是使用連接 pod 和根命名空間的虛擬以太網(wǎng)對來實現(xiàn)的。

    這些虛擬接口設備[9](veth?中的?v)連接并充當兩個命名空間間的隧道。

    使用此?veth?設備,將一端連接到 pod 的命名空間,另一端連接到根命名空間。

    這些 CNI 可以替你做,也可以手動操作:

    $?ip?link?add?veth1?netns?pod-namespace?type?veth?peer?veth2?netns?root

    現(xiàn)在 pod 的命名空間有了可以訪問根命名空間的隧道。

    節(jié)點上每個新建的 pod 都會設置如下所示的 veth 對。

    創(chuàng)建接口對時其中一部分。

    其他的就是為以太網(wǎng)設備分配地址,并創(chuàng)建默認路由。

    來看下如何在 pod 的命名空間中設置?veth1?接口:

    $?ip?netns?exec?cni-0f226515-e28b-df13-9f16-dd79456825ac?ip?addr?add?10.244.4.40/24?dev?veth1 $?ip?netns?exec?cni-0f226515-e28b-df13-9f16-dd79456825ac?ip?link?set?veth1?up $?ip?netns?exec?cni-0f226515-e28b-df13-9f16-dd79456825ac?ip?route?add?default?via?10.244.4.40

    在節(jié)點側,我們創(chuàng)建另一個?veth2?對:

    $?ip?addr?add?169.254.132.141/16?dev?veth2 $?ip?link?set?veth2?up

    可以像以前一樣檢查現(xiàn)有的?veth?對。

    在 pod 的命名空間中,檢查?eth0?接口的后綴。

    $?ip?netns?exec?cni-0f226515-e28b-df13-9f16-dd79456825ac?ip?link?show?type?veth 3:?eth0@if12:?<BROADCAST,MULTICAST,UP,LOWER_UP>?mtu?1450?qdisc?noqueue?state?UP?mode?DEFAULT?group?defaultlink/ether?16:a4:f8:4f:56:77?brd?ff:ff:ff:ff:ff:ff?link-netnsid?0

    這種情況下可以使用?grep -A1 ^12?進行查找(或者滾動到目標所在):

    $?ip?link?show?type?veth #?output?truncated 12:?cali97e50e215bd@if3:?<BROADCAST,MULTICAST,UP,LOWER_UP>?mtu?1450?qdisc?noqueue?state?UP?mode?DEFAULT?group?defaultlink/ether?ee:ee:ee:ee:ee:ee?brd?ff:ff:ff:ff:ff:ff?link-netns?cni-0f226515-e28b-df13-9f16-dd79456825ac

    也可以使用?ip -n cni-0f226515-e28b-df13-9f16-dd79456825ac link show type veth命令。

    注意?3: eth0@if12?和?12: cali97e50e215bd@if3?接口上的符號。

    在 pod 命名空間中,eth0?接口連接到根命名空間中編號為?12?的接口。因此是?@if12。

    在?veth?對的另一端,根命名空間連接到 pod 命名空間的?3?號接口。

    接下來是連接?veth?對兩端的橋接器(bridge)。

    Pod 命名空間連接到以太網(wǎng)橋接器

    橋接器將位于根命名空間中的虛擬接口的每一端“綁定”。

    該橋接器將允許流量在虛擬對之間流動,并通過公共根命名空間。

    理論時間。

    以太網(wǎng)橋接器位于OSI 網(wǎng)絡模型[10]的第二層。

    可以將橋接器看作一個虛擬交換機,接受來自不同命名空間和接口的連接。[11]

    以太網(wǎng)橋接器允許連接同一個節(jié)點上的多個可用網(wǎng)絡。

    因此,可以使用該設置連接兩個接口:從 pod 命名空間的?veth?連接到同一節(jié)點上另一個 pod 的?veth。

    我們繼續(xù)看下以太網(wǎng)橋接器和 veth 對的作用。

    跟蹤同一節(jié)點上 pod 間的流量

    假設同一個節(jié)點上有兩個 pod,Pod-A 想向 Pod-B 發(fā)送消息。

  • 由于目標不是同命名空間的容器,Pod-A 向其默認接口?eth0?發(fā)送數(shù)據(jù)包。這個接口與?veth?對的一端綁定,作為隧道。因此數(shù)據(jù)包將被轉發(fā)到節(jié)點的根命名空間。

  • 以太網(wǎng)橋接器作為虛擬交換機,必須以某種方式將目標 pod IP(Pod-B)解析為其 MAC 地址。

  • 輪到ARP 協(xié)議上場了。當幀到達橋接器時,會向所有連接的設備發(fā)送 ARP 廣播。橋接器喊道誰有 Pod-B 的 IP 地址。

  • 收到帶有連接 Pod-B 接口的 MAC 地址的回復,然后此信息存儲在橋接器 ARP 緩存(查找表)中。

  • IP 和 MAC 地址的映射存儲完成后,橋接器在表中查找,并將數(shù)據(jù)包轉發(fā)到正確的短點。數(shù)據(jù)包到達根命名空間中 Pod- B 的?veth,然后從那快速到達 Pod-B 命名空間內(nèi)的?eth0?接口。

  • 有了這個,Pod-A 和 Pod-B 之間的通信取得了成功。

    跟蹤不同節(jié)點上 pod 間的通信

    對于需要跨不同節(jié)點通信的 pod,需要額外的通信跳轉。

  • 前幾個步驟保持不變,直到數(shù)據(jù)包到達根命名空間并需要發(fā)送到 Pod- B。

  • 當目標地址不在本地網(wǎng)絡中,數(shù)據(jù)包將被轉發(fā)到本節(jié)點的默認網(wǎng)關。節(jié)點上退出或默認網(wǎng)關通常位于?eth0?接口上 -- 將節(jié)點連接到網(wǎng)絡的物理接口。

  • 這次并不會發(fā)生 ARP 解析,因為源和目標 IP 在不同網(wǎng)絡上。

    檢查使用位運算(Bitwise)操作完成。

    當目標 IP 不在當前網(wǎng)絡上時,數(shù)據(jù)包將被轉發(fā)到節(jié)點的默認網(wǎng)關。

    位運算的工作原理

    在確定數(shù)據(jù)包的轉發(fā)位置時,源節(jié)點必須執(zhí)行位運算。

    這也被稱為與操作。[12]

    作為復習,位與操作產(chǎn)生如下結果:

    0?AND?0?=?0 0?AND?1?=?0 1?AND?0?=?0 1?AND?1?=?1

    除了 1 與 1 以外的都是 false。

    如果源節(jié)點的 IP 為 192.168.1.1,子網(wǎng)掩碼為 /24,目標 IP 為 172.16.1.1/16,則按位與操作將確認他們不在同一網(wǎng)絡上。

    這意味著目標 IP 與數(shù)據(jù)包的源在不同的網(wǎng)絡上,因此數(shù)據(jù)包將在默認網(wǎng)關中轉發(fā)。

    數(shù)學時間。

    我們必須從二進制文件中的 32 位地址執(zhí)行與操作開始。

    先找出源和目標 IP 的網(wǎng)絡。

    |?Type?????????????|?Binary??????????????????????????????|?Converted??????????| |?----------------?|?-----------------------------------?|?------------------?| |?Src.?IP?Address??|?11000000.10101000.00000001.00000001?|?192.168.1.1????????| |?Src.?Subnet?Mask?|?11111111.11111111.11111111.00000000?|?255.255.255.0(/24)?| |?Src.?Network?????|?11000000.10101000.00000001.00000000?|?192.168.1.0????????| |??????????????????|?????????????????????????????????????|????????????????????| |?Dst.?IP?Address??|?10101100.00010000.00000001.00000001?|?172.16.1.1?????????| |?Dst.?Subnet?Mask?|?11111111.11111111.00000000.00000000?|?255.255.0.0(/16)???| |?Dst.?Network?????|?10101100.00010000.00000000.00000000?|?172.16.0.0?????????|

    位運算操作后,需要將目標 IP 與數(shù)據(jù)包源節(jié)點的子網(wǎng)進行比較。

    |?Type?????????????|?Binary??????????????????????????????|?Converted??????????| |?----------------?|?-----------------------------------?|?------------------?| |?Dst.?IP?Address??|?10101100.00010000.00000001.00000001?|?172.16.1.1?????????| |?Src.?Subnet?Mask?|?11111111.11111111.11111111.00000000?|?255.255.255.0(/24)?| |?Network??Result??|?10101100.00010000.00000001.00000000?|?172.16.1.0

    進行位比較后,ARP 會檢查其查詢表來查找默認網(wǎng)關的 MAC 地址。

    如果有條目,將立即轉發(fā)數(shù)據(jù)包。

    否則,先進行廣播以確定網(wǎng)關的 MAC 地址。

  • 數(shù)據(jù)包現(xiàn)在路由到另一個節(jié)點的默認接口,我們叫它 Node-B。

  • 以相反的順序。數(shù)據(jù)包現(xiàn)在位與 Node-B 的根命名空間,并到達橋接器,這里會進行 ARP 解析。

  • 收到帶有連接 Pod-B 的接口 MAC地址的回復。

  • 這次橋接器通過 Pod-B 的?veth?設備將幀轉發(fā),并到達 Pod-B 自己的命名空間。

  • 此時應該已經(jīng)熟悉了 pod 之間的流量如何流轉,接下來再探索下 CNI 如何創(chuàng)建上述內(nèi)容。

    容器網(wǎng)絡接口 - CNI

    容器網(wǎng)絡接口(CNI)關注當前節(jié)點的網(wǎng)絡。[13]

    可以將 CNI 看作網(wǎng)絡插件在解決 Kubernetes?某些?需求時要遵循的一套規(guī)則。

    然而,它不僅僅與 Kubernetes 或者特定網(wǎng)絡插件關聯(lián)。

    可以使用如下 CNI:

    • Calico[14]

    • Cilium[15]

    • Flannel[16]

    • Weave Net[17]

    • 其他網(wǎng)絡插件[18]

    他們都實現(xiàn)相同的 CNI 標準。

    如果沒有 CNI,你需要手動完成如下操作:

    • 創(chuàng)建 pod(容器)的網(wǎng)絡命名空間

    • 創(chuàng)建接口

    • 創(chuàng)建 veth 對

    • 設置命名空間網(wǎng)絡

    • 設置靜態(tài)路由

    • 配置以太網(wǎng)橋接器

    • 分配 IP 地址

    • 創(chuàng)建 NAT 規(guī)則

    還有太多其他需要手動完成的工作。

    更不用說刪除或重新啟動 pod 時刪除或調(diào)整上述所有內(nèi)容了。

    CNI 必須支持四個不同的操作[19]

    • ADD?- 將容器添加到網(wǎng)絡

    • DEL?- 從網(wǎng)絡中刪除容器

    • CHECK?- 如果容器的網(wǎng)絡出現(xiàn)問題,則返回錯誤

    • VERSION?- 顯示插件的版本

    讓我們在實踐中看看它是如何工作的。

    當 pod 分配到特定節(jié)點時,kubelet 本身不會初始化網(wǎng)絡。

    相反,它將任務交給了 CNI。

    然后,它指定了配置,并以 JSON 格式將其發(fā)送給 CNI 插件。

    可以在節(jié)點的?/etc/cni/net.d?目錄中,找到當前 CNI 的配置文件:

    $?cat?10-calico.conflist {"name":?"k8s-pod-network","cniVersion":?"0.3.1","plugins":?[{"type":?"calico","datastore_type":?"kubernetes","mtu":?0,"nodename_file_optional":?false,"log_level":?"Info","log_file_path":?"/var/log/calico/cni/cni.log","ipam":?{?"type":?"calico-ipam",?"assign_ipv4"?:?"true",?"assign_ipv6"?:?"false"},"container_settings":?{"allow_ip_forwarding":?false},"policy":?{"type":?"k8s"},"kubernetes":?{"k8s_api_root":"https://10.96.0.1:443","kubeconfig":?"/etc/cni/net.d/calico-kubeconfig"}},{"type":?"bandwidth","capabilities":?{"bandwidth":?true}},{"type":?"portmap",?"snat":?true,?"capabilities":?{"portMappings":?true}}] }

    每個插件都使用不同類型的配置來設置網(wǎng)絡。

    例如,Calico 使用 BGP 路由協(xié)議配對的第 3 層網(wǎng)絡來連接 pod。

    Cilium 在第 3 到 7 層使用 eBPF 配置覆蓋網(wǎng)絡。

    與 Calico 一起,Cilium 支持設置網(wǎng)絡策略來限制流量。

    那該如何選擇呢?

    這取決于。

    CNI 主要有兩組。

    第一組中,可以找到使用基本網(wǎng)絡設置(也稱為扁平網(wǎng)絡)的CNI,并將集群 IP 池 中的IP 地址分配給 pod。

    這可能會因為快速用盡可用的 IP 地址而成為負擔。

    相反,另一種方法是使用覆蓋網(wǎng)絡。

    簡而言之,覆蓋網(wǎng)絡是主(底層)網(wǎng)絡之上的輔助網(wǎng)絡。

    覆蓋網(wǎng)絡的工作原理是封裝來自底層網(wǎng)絡的所有數(shù)據(jù)包,這些數(shù)據(jù)包指向另一個節(jié)點上的 pod。

    覆蓋網(wǎng)絡的一項流行技術是?VXLAN[20],它允許在 L3 網(wǎng)絡上隧道傳輸 L2 域。

    那么哪種更好?

    沒有唯一的答案,通常取決于你的需求。

    你是在構建一個擁有數(shù)萬個節(jié)點的大集群嗎?

    可能覆蓋網(wǎng)絡更好。

    你是否在意更簡單的設置和在嵌套網(wǎng)絡中不失去檢查網(wǎng)絡流量的能力。

    扁平網(wǎng)絡更適合你。

    現(xiàn)在已經(jīng)討論了 CNI,讓我們繼續(xù)探索 Pod 到服務(service)的通信是如何完成的。

    檢查 pod 到服務的流量

    由于 Kubernetes 環(huán)境下 pod 的動態(tài)特性,分配給 pod 的 IP 地址不是靜態(tài)的。

    這些 IP 地址是短暫的,每次創(chuàng)建或者刪除 pod 時都會發(fā)生變化。

    服務解決了這個問題,為連接到一組 pod 提供了穩(wěn)定的機制。

    默認情況下,在 Kubernetes 中創(chuàng)建服務時,會為其預定并分配虛擬 IP?[21]

    使用選擇器將服務于目標 pod 進行管理。

    當刪除 pod 并添加新 pod 時會發(fā)生什么?

    該服務的虛擬 IP 保持不變。

    然而,無需敢于,流量將到達新創(chuàng)建的 pod。

    換句話說,Kubernetes 中的服務類似于負載均衡器。

    但他們時如何工作的?

    使用 Netfilter 和 Iptables 攔截和重寫流量

    Kubernetes 中的服務基于兩個 Linux 內(nèi)核組件:

  • Netfilter

  • Iptables

  • Netfilter 是一個框架,允許配置數(shù)據(jù)包過濾、創(chuàng)建 NAT或端口翻譯規(guī)則,并管理網(wǎng)絡中的流量。

    此外,它還屏蔽和阻止不請自來的連接訪問服務。

    另一方面,Iptables 是一個用戶空間程序,允許你配置 Linux 內(nèi)核防火墻的 IP 數(shù)據(jù)包過濾器規(guī)則。

    iptables 使用不同的 Netfilter 模塊實現(xiàn)。

    你可以使用 iptables CLI 實時更改過濾規(guī)則,并將其插入 netfilters 的掛點。

    過濾器組織在不同的表中,其中包含處理網(wǎng)絡流量數(shù)據(jù)包的鏈。

    每個協(xié)議都使用不同的內(nèi)核模塊和程序。

    當提到 iptables 時,通常說的是 IPV4。對于 IPV6 的規(guī)則,CLI 是 ip6tables。

    Iptables 有五種類型的鏈,每種鏈都直接映射到 Netfilter 鉤子。

    從 iptables 角度看是:

    • PRE_ROUTING

    • INPUT

    • FORWARD

    • OUTPUT

    • POST_ROUTING

    對應映射到 Netfilter 鉤子:

    • NF_IP_PRE_ROUTING

    • NF_IP_LOCAL_IN

    • NF_IP_FORWARD

    • NF_IP_LOCAL_OUT

    • NF_IP_POST_ROUTING

    當數(shù)據(jù)包到達時,根據(jù)所處的階段,會“出發(fā)” Netfilter 鉤子,該鉤子應用特定的 iptables 過濾。

    哎呀,看起來很復雜!

    不過不需要擔心。

    這就是為什么我們使用 Kubernetes,上面的所有內(nèi)容都是通過使用服務來抽象的,一個簡單的 YAML 定義就可以自動完成這些規(guī)則的設置。

    如果對這些 iptables 規(guī)則感興趣,可以登陸到節(jié)點并運行:

    iptables-save

    也可以使用可視化工具[22]瀏覽節(jié)點上的 iptables 鏈。

    記住,可能會有數(shù)百條規(guī)則。想象下手動創(chuàng)建的難度。

    我們已經(jīng)解釋了相同和不同節(jié)點上的 pod 間如何通信。

    在 Pod-to-Service 中,通信的前半部分保持不變。

    當 Pod-A 發(fā)出請求時,希望到達 Pod-B(這種情況下,Pod-B 位與服務之后),轉移的過程中會發(fā)生其他變化。

    原始請求從 Pod-A 命名空間中的?eth0?接口出來。

    從那里穿過?veth?對,到達根命名空間的以太網(wǎng)橋。

    一旦到達橋接器,數(shù)據(jù)包立即通過默認網(wǎng)關轉發(fā)。

    與 Pod-to-Pod 部分一樣,主機進行位比較,由于服務的 vIP 不是節(jié)點 CIDR 的一部分,數(shù)據(jù)包將立即通過默認網(wǎng)關轉發(fā)出去。

    如果查找表中尚沒有默認網(wǎng)關的 MAC 地址,則會進行相同的 ARP 解析。

    現(xiàn)在魔法發(fā)生了。

    在數(shù)據(jù)包經(jīng)過節(jié)點的路由處理之前,Netfilter 鉤子?NF_IP_PRE_ROUTING?被觸發(fā)并應用一條 iptables 規(guī)則。規(guī)則進行了 DNAT 轉換,重寫了 POD-A 數(shù)據(jù)包的目標 IP 地址。

    原來服務 vIP 地址被重寫稱 POD-B 的IP 地址。

    從那里,路由就像 Pod-to-Pod 直接通信一樣。

    然而,在所有這些通信之間,使用了第三個功能。

    這個功能被稱為 conntrack[23],或連接跟蹤。

    Conntrack 將數(shù)據(jù)包與連接關聯(lián)起來,并在 Pod-B 發(fā)送回響應時跟蹤其來源。

    NAT 嚴重依賴 contrack 工作。

    如果沒有連接跟蹤,它將不知道將包含響應的數(shù)據(jù)包發(fā)送回哪里。

    使用 conntrack 時,數(shù)據(jù)包的返回路徑可以輕松設置相同的源或目標 NAT 更改。

    另一半使用相反的順序執(zhí)行。

    Pod-B 接收并處理了請求,現(xiàn)在將數(shù)據(jù)發(fā)送回 Pod-A。

    此時會發(fā)生什么?

    檢查服務的響應

    現(xiàn)在 Pod-B 發(fā)送響應,將其 IP 地址設置為源地址,Pod-A IP 地址設置為目標地址。

  • 當數(shù)據(jù)包到達 Pod-A 所在節(jié)點的接口時,就會發(fā)生另一個 NAT

  • 這次,使用 conntrack 更改源 IP 地址,iptables 規(guī)則執(zhí)行 SNAT 將 Pod-B IP 地址替換為原始服務的 VIP 地址。

  • 從 Pod-A 來看像是服務發(fā)回的響應,而不是 Pod-B。

  • 其他部分都一樣;一旦 SNAT 完成,數(shù)據(jù)包到達根命名空間中的以太網(wǎng)橋接器,并通過 veth 對轉發(fā)到 Pod-A。

    回顧

    讓我們來總結下你在本文中學到的東西:

    • 容器如何在本地或 pod 內(nèi)通信。

    • 當 pod 位于相同和不同的節(jié)點上時,Pod-to- Pod 如何通信。

    • Pod-to-Service - 當 pod 向 Kubernetes 服務背后的 pod 發(fā)送流量時。

    • Kubernetes 網(wǎng)絡工具箱中有效通信所需的命名空間、veth、iptables、鏈、Netfilter、CNI、覆蓋網(wǎng)絡以及所有其他內(nèi)容。

    往期推薦

    好難啊……一個 try-catch 問出這么多花樣

    k8s集群居然可以圖形化安裝了?

    惡意流量威脅新趨勢,揭秘網(wǎng)絡黑產(chǎn)3大核心本質

    將 k8s 制作成 3D 射擊游戲,好玩到停不下來

    點分享

    點收藏

    點點贊

    點在看

    總結

    以上是生活随笔為你收集整理的追踪 Kubernetes 中的网络流量的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。