自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Kubernetes 網(wǎng)絡(luò)學(xué)習(xí)之 Cilium 與 eBPF

云計(jì)算 云原生
Linux 內(nèi)核一直是實(shí)現(xiàn)監(jiān)控/可觀測(cè)性、網(wǎng)絡(luò)和安全功能的理想地方。 不過(guò)很多情況下這并非易事,因?yàn)檫@些工作需要修改內(nèi)核源碼或加載內(nèi)核模塊, 最終實(shí)現(xiàn)形式是在已有的層層抽象之上疊加新的抽象。 eBPF 是一項(xiàng)革命性技術(shù),它能在內(nèi)核中運(yùn)行沙箱程序(sandbox programs), 而無(wú)需修改內(nèi)核源碼或者加載內(nèi)核模塊。

開(kāi)始之前說(shuō)點(diǎn)題外話,距離上一篇 Flannel CNI 的發(fā)布已經(jīng)快一個(gè)月了。這篇本想趁著勢(shì)頭在去年底完成的,正好在一個(gè)月內(nèi)完成計(jì)劃的所有內(nèi)容。但上篇發(fā)布后不久,我中招了花了一個(gè)多周的時(shí)間才恢復(fù)。然而,恢復(fù)后的狀態(tài)讓我有點(diǎn)懵,總感覺(jué)很難集中精力,很容易精神渙散??赡芙咏W(wǎng)上流傳的“腦霧”吧,而且 Cilium 也有點(diǎn)類似一團(tuán)迷霧。再疊加網(wǎng)絡(luò)知識(shí)的不足,eBPF 也未從涉足,學(xué)習(xí)的過(guò)程中斷斷續(xù)續(xù),我曾經(jīng)一度懷疑這篇會(huì)不會(huì)流產(chǎn)。

文章中不免會(huì)有問(wèn)題,如果有發(fā)現(xiàn)問(wèn)題或者建議,望不吝賜教。

背景

去年曾經(jīng)寫(xiě)過(guò)一篇文章 《使用 Cilium 增強(qiáng) Kubernetes 網(wǎng)絡(luò)安全》[1] 接觸過(guò) Cilium,借助 Cilium 的網(wǎng)絡(luò)策略從網(wǎng)絡(luò)層面對(duì) pod 間的通信進(jìn)行限制。但當(dāng)時(shí)我不曾深入其實(shí)現(xiàn)原理,對(duì) Kubernetes 網(wǎng)絡(luò)和 CNI 的了解也不夠深入。這次我們通過(guò)實(shí)際的環(huán)境來(lái)探尋 Cilium 的網(wǎng)絡(luò)。

這篇文章使用的 Cilium 版本是 v1.12.3,操作系統(tǒng)是 Ubuntu 20.04,內(nèi)核版本是 5.4.0-91-generic。

Cilium 簡(jiǎn)介

Cilium[2] 是一個(gè)開(kāi)源軟件,用于提供、保護(hù)和觀察容器工作負(fù)載(云原生)之間的網(wǎng)絡(luò)連接,由革命性的內(nèi)核技術(shù) eBPF[3] 推動(dòng)。

圖片

cilium-on-kubernetes

eBPF 是什么?

Linux 內(nèi)核一直是實(shí)現(xiàn)監(jiān)控/可觀測(cè)性、網(wǎng)絡(luò)和安全功能的理想地方。 不過(guò)很多情況下這并非易事,因?yàn)檫@些工作需要修改內(nèi)核源碼或加載內(nèi)核模塊, 最終實(shí)現(xiàn)形式是在已有的層層抽象之上疊加新的抽象。 eBPF 是一項(xiàng)革命性技術(shù),它能在內(nèi)核中運(yùn)行沙箱程序(sandbox programs), 而無(wú)需修改內(nèi)核源碼或者加載內(nèi)核模塊。

將 Linux 內(nèi)核變成可編程之后,就能基于現(xiàn)有的(而非增加新的)抽象層來(lái)打造更加智能、 功能更加豐富的基礎(chǔ)設(shè)施軟件,而不會(huì)增加系統(tǒng)的復(fù)雜度,也不會(huì)犧牲執(zhí)行效率和安全性。

Linux 的內(nèi)核在網(wǎng)絡(luò)棧上提供了一組 BPF 鉤子,通過(guò)這些鉤子可以觸發(fā) BPF 程序的執(zhí)行。Cilium datapah 使用這些鉤子加載 BPF 程序,創(chuàng)建出更高級(jí)的網(wǎng)絡(luò)結(jié)構(gòu)。

通過(guò)閱讀 Cilium 參考文檔 eBPF Datapath[4] 得知 Cilium 使用了下面幾種鉤子:

  • XDP:這是網(wǎng)絡(luò)驅(qū)動(dòng)中接收網(wǎng)絡(luò)包時(shí)就可以觸發(fā) BPF 程序的鉤子,也是最早的點(diǎn)。由于此時(shí)還沒(méi)有執(zhí)行其他操作,比如將網(wǎng)絡(luò)包寫(xiě)入內(nèi)存,所以它非常適合運(yùn)行刪除惡意或意外流量的過(guò)濾程序,以及其他常見(jiàn)的 DDOS 保護(hù)機(jī)制。
  • Traffic Control Ingress/Egress:附加到流量控制(traffic control,簡(jiǎn)稱 tc)ingress 鉤子上的 BPF 程序,可以被附加到網(wǎng)絡(luò)接口上。這種鉤子在網(wǎng)絡(luò)棧的 L3 之前執(zhí)行,并可以訪問(wèn)網(wǎng)絡(luò)包的大部分元數(shù)據(jù)。適合處理本節(jié)點(diǎn)的操作,比如應(yīng)用 L3/L4 的端點(diǎn) [^1] 策略、轉(zhuǎn)發(fā)流量到端點(diǎn)。CNI 通常使用虛擬機(jī)以太接口對(duì) veth 將容器連接到主機(jī)的網(wǎng)絡(luò)命名空間。使用附加到主機(jī)端 veth 的 tc ingress 鉤子,可以監(jiān)控離開(kāi)容器的所有流量,并執(zhí)行策略。同時(shí)將另一個(gè) BPF 程序附加到 tc egress 鉤子,Cilium 可以監(jiān)控所有進(jìn)出節(jié)點(diǎn)的流量并執(zhí)行策略 .
  • Socket operations:套接字操作鉤子附加到特定的 cgroup 并在 TCP 事件上運(yùn)行。Cilium 將 BPF 套接字操作程序附加到根 cgroup,并使用它來(lái)監(jiān)控 TCP 狀態(tài)轉(zhuǎn)換,特別是 ESTABLISHED 狀態(tài)轉(zhuǎn)換。當(dāng)套接字狀態(tài)變?yōu)?ESTABLISHED 時(shí),如果 TCP 套接字的對(duì)端也在當(dāng)前節(jié)點(diǎn)(也可能是本地代理),則會(huì)附加 Socket send/recv 程序。
  • Socket send/recv:這個(gè)鉤子在 TCP 套接字執(zhí)行的每個(gè)發(fā)送操作上運(yùn)行。此時(shí)鉤子可以檢查消息并丟棄消息、將消息發(fā)送到 TCP 層,或者將消息重定向到另一個(gè)套接字。Cilium 使用它來(lái)加速數(shù)據(jù)路徑重定向。

因?yàn)楹竺鏁?huì)用到,這里著重介紹了這幾種鉤子。

環(huán)境搭建

前面幾篇文章,我都是使用 k3s 并手動(dòng)安裝 CNI 插件來(lái)搭建實(shí)驗(yàn)環(huán)境。這次,我們直接使用 k8e[5],因?yàn)?k8e 使用 Cilium 作為默認(rèn)的 CNI 實(shí)現(xiàn)。

還是在我的 homelab 上做個(gè)雙節(jié)點(diǎn)(ubuntu-dev2: 192.168.1.12、ubuntu-dev3: 192.168.1.13)的集群。

Master 節(jié)點(diǎn):

curl -sfL https://getk8e.com/install.sh | API_SERVER_IP=192.168.1.12 K8E_TOKEN=ilovek8e INSTALL_K8E_EXEC="server --cluster-init --write-kubeconfig-mode 644 --write-kubeconfig ~/.kube/config" sh -

Worker 節(jié)點(diǎn):

curl -sfL https://getk8e.com/install.sh | K8E_TOKEN=ilovek8e K8E_URL=https://192.168.1.12:6443 sh -

部署示例應(yīng)用,將其調(diào)度到不同的節(jié)點(diǎn)上:

NODE1=ubuntu-dev2
NODE2=ubuntu-dev3
kubectl apply -n default -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
labels:
app: curl
name: curl
spec:
containers:
- image: curlimages/curl
name: curl
command: ["sleep", "365d"]
nodeName: $NODE1
---
apiVersion: v1
kind: Pod
metadata:
labels:
app: httpbin
name: httpbin
spec:
containers:
- image: kennethreitz/httpbin
name: httpbin
nodeName: $NODE2
EOF

為了使用方便,將示例應(yīng)用、cilium pod 等信息設(shè)置為環(huán)境變量:

NODE1=ubuntu-dev2
NODE2=ubuntu-dev3

cilium1=$(kubectl get po -n kube-system -l k8s-app=cilium --field-selector spec.nodeName=$NODE1 -o jsnotallow='{.items[0].metadata.name}')
cilium2=$(kubectl get po -n kube-system -l k8s-app=cilium --field-selector spec.nodeName=$NODE2 -o jsnotallow='{.items[0].metadata.name}')

Debug 流量

還是以前的套路,從請(qǐng)求發(fā)起方開(kāi)始一路追尋網(wǎng)絡(luò)包。這次使用 Service 來(lái)進(jìn)行訪問(wèn):curl http://10.42.0.51:80/get。

kubectl get po httpbin -n default -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
httpbin 1/1 Running 0 3m 10.42.0.51 ubuntu-dev3 <none> <none>

第 1 步:容器發(fā)送請(qǐng)求

檢查 pod curl 的路由表:

kubectl exec curl -n default -- ip route get 10.42.0.51
10.42.0.51 via 10.42.1.247 dev eth0 src 10.42.1.80

可知網(wǎng)絡(luò)包就發(fā)往以太接口 eth0,然后從使用 arp 查到其 MAC 地址 ae:36:76:3e:c3:03:

kubectl exec curl -n default -- arp -n
? (10.42.1.247) at ae:36:76:3e:c3:03 [ether] on eth0

查看接口 eth0 的信息:

kubectl exec curl -n default -- ip link show eth0
42: eth0@if43: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP qlen 1000
link/ether f6:00:50:f9:92:a1 brd ff:ff:ff:ff:ff:ff

發(fā)現(xiàn)其 MAC 地址并不是 ae:36:76:3e:c3:03,從名字上的 @if43 可以得知其 veth 對(duì)的索引是 43,接著 登錄到節(jié)點(diǎn) NODE1 查詢?cè)撍饕涌诘男畔ⅲ?/p>

ip link | grep -A1 ^43
43: lxc48c4aa0637ce@if42: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether ae:36:76:3e:c3:03 brd ff:ff:ff:ff:ff:ff link-netns cni-407cd7d8-7c02-cfa7-bf93-22946f923ffd

我們看到這個(gè)接口 lxc48c4aa0637ce 的 MAC 正好就是 ae:36:76:3e:c3:03。

按照 過(guò)往的經(jīng)驗(yàn)[6],這個(gè)虛擬的以太接口 lxc48c4aa0637ce 是個(gè) 虛擬以太網(wǎng)口,位于主機(jī)的根網(wǎng)絡(luò)命名空間,一方面與容器的以太接口 eth0 間通過(guò)隧道相連,發(fā)送到任何一端的網(wǎng)絡(luò)包都會(huì)直達(dá)對(duì)端;另一方面應(yīng)該與主機(jī)命名空間上的網(wǎng)橋相連,但是從上面的結(jié)果中并未找到網(wǎng)橋的名字。

通過(guò) ip link 查看:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether fa:cb:49:4a:28:21 brd ff:ff:ff:ff:ff:ff
3: cilium_net@cilium_host: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 36:d5:5a:2a:ce:80 brd ff:ff:ff:ff:ff:ff
4: cilium_host@cilium_net: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 12:82:fb:78:16:6a brd ff:ff:ff:ff:ff:ff
5: cilium_vxlan: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ether fa:42:4d:22:b7:d0 brd ff:ff:ff:ff:ff:ff
25: lxc_health@if24: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 3e:4f:b3:56:67:2b brd ff:ff:ff:ff:ff:ff link-netnsid 0
33: lxc113dd6a50a7a@if32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 32:3a:5b:15:44:ff brd ff:ff:ff:ff:ff:ff link-netns cni-07cffbd8-83dd-dcc1-0b57-5c59c1c037e9
43: lxc48c4aa0637ce@if42: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether ae:36:76:3e:c3:03 brd ff:ff:ff:ff:ff:ff link-netns cni-407cd7d8-7c02-cfa7-bf93-22946f923ffd

我們看到了多個(gè)以太接口:cilium_net、cilium_host、cilium_vxlan、cilium_health 以及與容器網(wǎng)絡(luò)命名空間的以太接口的隧道對(duì)端 lxcxxxx。

圖片

cilium-cross-node

網(wǎng)絡(luò)包到了 lxcxxx 這里再怎么走?接下來(lái)就輪到 eBPF 出場(chǎng)了。

注意 cilium_net、cilium_host 和 cilium_health 在文中不會(huì)涉及,因此不在后面的圖中體現(xiàn)。

第 2 步:Pod1 LXC BPF Ingress

進(jìn)入到當(dāng)前節(jié)點(diǎn)的 cilium pod 也就是前面設(shè)置的變量 $cilium1 中使用 bpftool 命令檢查附加該 veth 上 BPF 程序。

kubectl exec -n kube-system $cilium1 -c cilium-agent -- bpftool net show dev lxc48c4aa0637ce
xdp:

tc:
lxc48c4aa0637ce(43) clsact/ingress bpf_lxc.o:[from-container] id 2901

flow_dissector:

也可以登錄到節(jié)點(diǎn) $NODE1 上使用 tc 命令來(lái)查詢。注意,這里我們指定了 ingress,在文章開(kāi)頭 datapath 部分。因?yàn)槿萜鞯?nbsp;eth0 與主機(jī)網(wǎng)絡(luò)命名空間的 lxc 組成通道,因此容器的出口(Egress)流量就是 lxc 的入口 Ingress 流量。同理,容器的入口流量就是 lxc 的出口流量。

#on NODE1
tc filter show dev lxc48c4aa0637ce ingress
filter protocol all pref 1 bpf chain 0
filter protocol all pref 1 bpf chain 0 handle 0x1 bpf_lxc.o:[from-container] direct-action not_in_hw id 2901 tag d578585f7e71464b jited

可以通過(guò)程序 id 2901 查看詳細(xì)信息。

kubectl exec -n kube-system $cilium1 -c cilium-agent -- bpftool prog show id 2901
2901: sched_cls name handle_xgress tag d578585f7e71464b gpl
loaded_at 2023-01-09T19:29:52+0000 uid 0
xlated 688B jited 589B memlock 4096B map_ids 572,86
btf_id 301

可以看出,這里加載了 BPF 程序 bpf_lxc.o 的 from-container 部分。到 Cilium 的源碼 bpf_lxc.c[7]的 __section("from-container") 部分,程序名 handle_xgress:

handle_xgress #1
validate_ethertype(ctx, &proto)
tail_handle_ipv4 #2
handle_ipv4_from_lxc #3
lookup_ip4_remote_endpoint => ipcache_lookup4 #4
policy_can_access #5
if TUNNEL_MODE #6
encap_and_redirect_lxc
ctx_redirect(ctx, ENCAP_IFINDEX, 0)
if ENABLE_ROUTING
ipv4_l3
return CTX_ACT_OK;

(1):網(wǎng)絡(luò)包的頭信息發(fā)送給 handle_xgress,然后檢查其 L3 的協(xié)議。

(2):所有 IPv4 的網(wǎng)絡(luò)包都交由 tail_handle_ipv4 來(lái)處理。

(3):核心的邏輯都在 handle_ipv4_from_lxc。tail_handle_ipv4 是如何跳轉(zhuǎn)到 handle_ipv4_from_lxc,這里用到了 Tails Call[8] 。Tails call 允許我們配置在某個(gè) BPF 程序執(zhí)行完成并滿足某個(gè)條件時(shí)執(zhí)行指定的另一個(gè)程序,且無(wú)需返回原程序。這里不做展開(kāi)有興趣的可以參考 官方的文檔[9]。

(4):接著從 eBPF map cilium_ipcache 中查詢目標(biāo) endpoint,查詢到 tunnel endpoint 192.168.1.13,這個(gè)地址是目標(biāo)所在的節(jié)點(diǎn) IP 地址,類型是。

kubectl exec -n kube-system $cilium1 -c cilium-agent -- cilium map get cilium_ipcache | grep 10.42.0.51
10.42.0.51/32 identity=15773 encryptkey=0 tunnelendpoint=192.168.1.13 sync

(5):policy_can_access 這里是執(zhí)行出口策略的檢查,本文不涉及故不展開(kāi)。

(6):之后的處理會(huì)有兩種模式:

  • 直接路由:交由內(nèi)核網(wǎng)絡(luò)棧進(jìn)行處理,或者 underlaying SDN 的支持。
  • 隧道:會(huì)將網(wǎng)絡(luò)包再次封裝,通過(guò)隧道傳輸,比如 vxlan。

這里我們使用的也是隧道模式。網(wǎng)絡(luò)包交給 encap_and_redirect_lxc 處理,使用 tunnel endpoint 作為隧道對(duì)端。最終轉(zhuǎn)發(fā)給 ENCAP_IFINDEX(這個(gè)值是接口的索引值,由 cilium-agent 啟動(dòng)時(shí)獲取的),就是以太網(wǎng)接口 cilium_vxlan。

第 3 步:NODE 1 vxlan BPF Egress

先看下這個(gè)接口上的 BPF 程序。

kubectl exec -n kube-system $cilium1 -c cilium-agent -- bpftool net show dev cilium_vxlan
xdp:

tc:
cilium_vxlan(5) clsact/ingress bpf_overlay.o:[from-overlay] id 2699
cilium_vxlan(5) clsact/egress bpf_overlay.o:[to-overlay] id 2707

flow_dissector:

容器的出口流量對(duì) cilium_vxlan 來(lái)說(shuō)也是 engress,因此這里的程序是 to-overlay。

程序位于 `bpf_overlay.c`[10] 中,這個(gè)程序的處理很簡(jiǎn)單,如果是 IPv6 協(xié)議會(huì)將封包使用 IPv6 的地址封裝一次。這里是 IPv4 ,直接返回 CTX_ACT_OK。將網(wǎng)絡(luò)包交給內(nèi)核網(wǎng)絡(luò)棧,進(jìn)入 eth0 接口。

第 4 步:NODE1 NIC BPF Egress

先看看 BPF 程序。

kubectl exec -n kube-system $cilium1 -c cilium-agent -- bpftool net show dev eth0
xdp:

tc:
eth0(2) clsact/ingress bpf_netdev_eth0.o:[from-netdev] id 2823
eth0(2) clsact/egress bpf_netdev_eth0.o:[to-netdev] id 2832

flow_dissector:

egress 程序 to-netdev 位于 `bpf_host.c`[11]。實(shí)際上沒(méi)做重要的處理,只是返回 CTX_ACT_OK 交給內(nèi)核網(wǎng)絡(luò)棧繼續(xù)處理:將網(wǎng)絡(luò)包發(fā)送到 vxlan 隧道發(fā)送到對(duì)端,也就是節(jié)點(diǎn) 192.168.1.13 。中間數(shù)據(jù)的傳輸,實(shí)際上用的還是 underlaying 網(wǎng)絡(luò),從主機(jī)的 eth0 接口經(jīng)過(guò) underlaying 網(wǎng)絡(luò)到達(dá)目標(biāo)主機(jī)的 eth0接口。

第 5 步:NODE2 NIC BPF Ingress

vxlan 網(wǎng)絡(luò)包到達(dá)節(jié)點(diǎn)的 eth0 接口,也會(huì)觸發(fā) BPF 程序。

kubectl exec -n kube-system $cilium2 -c cilium-agent -- bpftool net show dev eth0
xdp:

tc:
eth0(2) clsact/ingress bpf_netdev_eth0.o:[from-netdev] id 4556
eth0(2) clsact/egress bpf_netdev_eth0.o:[to-netdev] id 4565

flow_dissector:

這次觸發(fā)的是 from-netdev,位于 bpf_host.c[12] 中。

from_netdev
if vlan
allow_vlan
return CTX_ACT_OK

對(duì) vxlan tunnel 模式來(lái)說(shuō),這里的邏輯很簡(jiǎn)單。當(dāng)判斷網(wǎng)絡(luò)包是 vxlan 的并確認(rèn)允許 vlan 后,直接返回 CTX_ACT_OK 將處理交給內(nèi)核網(wǎng)絡(luò)棧。

第 6 步:NODE2 vxlan BPF Ingress

網(wǎng)絡(luò)包通過(guò)內(nèi)核網(wǎng)絡(luò)棧來(lái)到了接口 cilium_vxlan。

kubectl exec -n kube-system $cilium2 -c cilium-agent -- bpftool net show dev cilium_vxlan
xdp:

tc:
cilium_vxlan(5) clsact/ingress bpf_overlay.o:[from-overlay] id 4468
cilium_vxlan(5) clsact/egress bpf_overlay.o:[to-overlay] id 4476

flow_dissector:

程序位于 `bpf_overlay.c`[13] 中。

from_overlay
validate_ethertype
tail_handle_ipv4
handle_ipv4
lookup_ip4_endpoint 1#
map_lookup_elem
ipv4_local_delivery 2#
tail_call_dynamic 3#

(1):lookup_ip4_endpoint 會(huì)在 eBPF map cilium_lxc 中檢查目標(biāo)地址是否在當(dāng)前節(jié)點(diǎn)中(這個(gè) map 只保存了當(dāng)前節(jié)點(diǎn)中的 endpoint)。

kubectl exec -n kube-system $cilium2 -c cilium-agent -- cilium map get cilium_lxc | grep 10.42.0.51
10.42.0.51:0 id=2826 flags=0x0000 ifindex=29 mac=96:86:44:A6:37:EC nodemac=D2:AD:65:4D:D0:7B sync

這里查到目標(biāo) endpoint 的信息:id、以太網(wǎng)口索引、mac 地址。在 NODE2 的節(jié)點(diǎn)上,查看接口信息發(fā)現(xiàn),這個(gè)網(wǎng)口是虛擬以太網(wǎng)設(shè)備 lxc65015af813d1,正好是 pod httpbin 接口 eth0 的對(duì)端。

ip link | grep -B1 -i d2:ad
29: lxc65015af813d1@if28: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether d2:ad:65:4d:d0:7b brd ff:ff:ff:ff:ff:ff link-netns cni-395674eb-172b-2234-a9ad-1db78b2a5beb

kubectl exec -n default httpbin -- ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
28: eth0@if29: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 96:86:44:a6:37:ec brd ff:ff:ff:ff:ff:ff link-netnsid

(2):ipv4_local_delivery 的邏輯位于 `l3.h`[14] 中,這里會(huì) tail-call 通過(guò) endpoint 的 LXC ID(29)定位的 BPF 程序。

第 7 步:Pod2 LXC BPF Egress

執(zhí)行下面的命令并不會(huì)找到想想中的 egress to-container(與 from-container)。

kubectl exec -n kube-system $cilium2 -c cilium-agent -- bpftool net show | grep 29
lxc65015af813d1(29) clsact/ingress bpf_lxc.o:[from-container] id 4670

前面用的 BPF 程序都是附加到接口上的,而這里是直接有 vxlan 附加的程序直接 tail call 的。to-container 可以在 `bpf-lxc.c`[15] 中找到。

handle_to_container
tail_ipv4_to_endpoint
ipv4_policy #1
policy_can_access_ingress
redirect_ep
ctx_redirect

(1):ipv4_policy 會(huì)執(zhí)行配置的策略

(2):如果策略通過(guò),會(huì)調(diào)用 redirect_ep 將網(wǎng)絡(luò)包發(fā)送到虛擬以太接口 lxc65015af813d1,進(jìn)入到 veth 后會(huì)直達(dá)與其相連的容器 eth0 接口。

第 8 步:到達(dá) Pod2

網(wǎng)絡(luò)包到達(dá) pod2,附上一張完成的圖。

圖片

cilium-packet-flow

總結(jié)

說(shuō)說(shuō)個(gè)人看法吧,本文設(shè)計(jì)的內(nèi)容還只是 Cilium 的冰山一角,對(duì)于內(nèi)核知識(shí)和 C 語(yǔ)言欠缺的我來(lái)說(shuō)研究起來(lái)非常吃力。Cilium 除此之外還有很多的內(nèi)容,也還沒(méi)有深入去研究。不得不感嘆,Cilium 真是復(fù)雜,以我目前的了解,Cilium 維護(hù)了一套自己的數(shù)據(jù)在 BPF map 中,比如 endpoint、節(jié)點(diǎn)、策略、路由、連接狀態(tài)等相當(dāng)多的數(shù)據(jù),這些都是保存在內(nèi)核中;再就是 BPF 程序的開(kāi)發(fā)和維護(hù)成本會(huì)隨著功能的復(fù)雜度而膨脹,很難想象如果用 BPF 程序去開(kāi)發(fā) L7 的功能會(huì)多復(fù)雜。這應(yīng)該是為什么會(huì)借助代理去處理 L7 的場(chǎng)景。

最后分享下學(xué)習(xí) Cilium 過(guò)程中的經(jīng)驗(yàn)吧。

首先是 BPF 程序的閱讀,在項(xiàng)目的 bpf 的代碼都是靜態(tài)的代碼,里面分布著很多的與配置相關(guān)的 if else,運(yùn)行時(shí)會(huì)根據(jù)配置進(jìn)行編譯。這種情況下可以進(jìn)入 Cilium pod,在目錄 /run/cilium/state/templates 下有應(yīng)用配置后的源文件,代碼量會(huì)少很多;在 /run/cilium/state/globals/node_config 下是當(dāng)前使用的配置,可以結(jié)合這些配置來(lái)閱讀代碼。

腳注

  • [^1]: Cilium 通過(guò)為容器分配 IP 地址使其在網(wǎng)絡(luò)上可用。多個(gè)容器可以共享同一個(gè) IP 地址,就像 一個(gè) Kubernetes Pod 中可以有多個(gè)容器,這些容器之間共享網(wǎng)絡(luò)命名空間,使用同一個(gè) IP 地址。這些共享同一個(gè)地址的容器,Cilium 將其組合起來(lái),成為 Endpoint(端點(diǎn))。
  • [^2]: eBPF 的 map 可以用來(lái)存儲(chǔ)數(shù)據(jù),在 Cilium 中 cilium-agent 監(jiān)控 api-server,將信息寫(xiě)入 map 中。比如這里cilium_lb4_services_v2 中維護(hù)著所有 Kubernetes Service 的信息。

參考資料

[1] 《使用 Cilium 增強(qiáng) Kubernetes 網(wǎng)絡(luò)安全》: https://atbug.com/enhance-kubernetes-network-security-with-cilium/

[2] Cilium: https://cilium.io

[3] eBPF: https://ebpf.io

[4] Cilium 參考文檔 eBPF Datapath: https://docs.cilium.io/en/stable/concepts/ebpf/intro/

[5] k8e: https://getk8e.com

[6] 過(guò)往的經(jīng)驗(yàn): https://atbug.com/deep-dive-k8s-network-mode-and-communication/

[7] bpf_lxc.c: https://github.com/cilium/cilium/blob/1c466d26ff0edfb5021d024f755d4d00bc744792/bpf/bpf_lxc.c#L1320

[8] Tails Call: https://docs.cilium.io/en/stable/bpf/#tail-calls

[9] 官方的文檔: https://docs.cilium.io/en/stable/bpf/#tail-calls

[10] bpf_overlay.c: https://github.com/cilium/cilium/blob/1c466d26ff0edfb5021d024f755d4d00bc744792/bpf/bpf_overlay.c#L528

[11] bpf_host.c: https://github.com/cilium/cilium/blob/1c466d26ff0edfb5021d024f755d4d00bc744792/bpf/bpf_host.c#L1081

[12] bpf_host.c: https://github.com/cilium/cilium/blob/1c466d26ff0edfb5021d024f755d4d00bc744792/bpf/bpf_host.c#L1040

[13] bpf_overlay.c: https://github.com/cilium/cilium/blob/1c466d26ff0edfb5021d024f755d4d00bc744792/bpf/bpf_overlay.c#L430

[14] l3.h: https://github.com/cilium/cilium/blob/1c466d26ff0edfb5021d024f755d4d00bc744792/bpf/lib/l3.h#L114

[15] bpf-lxc.c: https://github.com/cilium/cilium/blob/1c466d26ff0edfb5021d024f755d4d00bc744792/bpf/bpf_lxc.c#L2131

責(zé)任編輯:武曉燕 來(lái)源: 云原生指北
相關(guān)推薦

2022-03-03 10:00:28

CiliumKubernetes開(kāi)源

2023-06-29 08:38:37

2021-08-17 07:15:15

ciliumKubernetes集群

2024-08-08 16:03:15

2023-01-10 11:34:06

2022-08-05 08:22:10

eBPFHTTP項(xiàng)目

2022-12-15 08:30:35

Flannel網(wǎng)絡(luò)磁盤

2023-06-16 08:13:57

2022-06-27 17:58:42

pwrueBPF工具

2024-01-26 06:05:16

KuberneteseBPF網(wǎng)絡(luò)

2011-06-02 10:28:18

2019-10-24 10:25:32

Kubernetes網(wǎng)絡(luò)集群

2020-07-06 07:52:10

Kubernetes網(wǎng)絡(luò)通信

2011-06-27 09:47:43

2011-06-27 09:02:18

Qt UDP 網(wǎng)絡(luò)

2011-06-02 10:28:15

網(wǎng)絡(luò)命令Arp

2011-06-02 14:36:19

Route

2011-06-02 11:21:59

Tftp

2011-06-02 14:36:22

Runas

2021-06-23 10:00:46

eBPFKubernetesLinux
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)