我是 LEE,老李,一個(gè)在 IT 行業(yè)摸爬滾打 16 年的技術(shù)老兵。
事件背景
最近不少 k8s 底層網(wǎng)絡(luò)模塊都從 kubenet 升級到了 cilium,cilium 還是相對比較易用的,就是運(yùn)維、監(jiān)控和報(bào)警方案需要花點(diǎn)時(shí)間來適應(yīng)。但是最近我們有一組 k8s 結(jié)構(gòu)比較特殊,導(dǎo)致它在從 kubenet 升級到 cilium 的時(shí)候出現(xiàn)了一個(gè)小插曲:在 kubenet 網(wǎng)絡(luò)的節(jié)點(diǎn)上的 pod 無法訪問 cilium 網(wǎng)絡(luò)的節(jié)點(diǎn)上且部署了 networkpolicy 的 pod,導(dǎo)致了這個(gè) k8s 上部分商戶在新發(fā)應(yīng)用 pod 時(shí),pod 被調(diào)度到 cilium 網(wǎng)絡(luò)的節(jié)點(diǎn)上,此時(shí)無法與運(yùn)行在 kubenet 網(wǎng)絡(luò)的節(jié)點(diǎn)上 pod 互訪問題。
現(xiàn)象獲取
當(dāng)我們接到監(jiān)控系統(tǒng)的報(bào)警,以及客戶的反饋,所以部分商戶出現(xiàn)了服務(wù)異常,而且只是少量商戶的出現(xiàn)了問題。 針對這些商戶的服務(wù)我第一時(shí)間看了下共性,發(fā)現(xiàn)這些商戶的服務(wù)性質(zhì),受眾群體,以及觸發(fā)報(bào)警時(shí)間都不一樣,難道是巧合? 我隨機(jī)找一個(gè)商戶做了簡單的排查:
直接測試商戶的接口

發(fā)現(xiàn)商戶接口直接返回:upstream connect error or disconnect/reset before headers. reset reason: connection failure ,單看這個(gè)信息,就知道這個(gè)應(yīng)該不是服務(wù)返回的內(nèi)容,應(yīng)該是后端服務(wù)前某一層轉(zhuǎn)發(fā)層的問題。
查看但是商戶服務(wù)的異常日志

查看應(yīng)用 sidecar 的日志, 把有問題的部分過濾出來,發(fā)現(xiàn)是錯(cuò)誤代碼是 503。
通過上面的故障,我頓時(shí)覺得整個(gè)問題很奇怪。 Http 503 的意思是說后端的服務(wù)資源不存在了,但是后端的 IP 還是通的,要不然就會(huì)是 502。
所以這里踩了一個(gè)小坑,覺得是商戶應(yīng)用的問題,還跟商戶確認(rèn)了很多內(nèi)容,以及最近發(fā)布的情況。但是實(shí)際是因?yàn)榫W(wǎng)絡(luò)層請求被截?cái)啵瑢?dǎo)致數(shù)據(jù)包無法正常轉(zhuǎn)發(fā),才導(dǎo)致這次故障,具體我們看下一個(gè)環(huán)節(jié)。
原理分析
故障測試
通過大量的排查和分析,而且確定商戶的服務(wù)啟動(dòng)正常,沒有運(yùn)行報(bào)錯(cuò)日志。只是服務(wù)無法對外正常提供,我就快速將排障范圍縮小到了網(wǎng)絡(luò)層面。
這里我提供一份網(wǎng)絡(luò)流量結(jié)構(gòu)圖:

- Ingress pod:是 istio Ingress gateway
- App pod: 是商戶應(yīng)用服務(wù)的 pod,擁有 istio 的 sidecar
為了確認(rèn)丟包的位置,我們分在如下幾個(gè)點(diǎn)抓包測試:

抓包信息匯總表:
| 測試點(diǎn) | 抓包結(jié)果 |
|---|---|
| A 點(diǎn) | 有 request,沒有 respond |
| B 點(diǎn) | 有 request,沒有 respond |
| C 點(diǎn) | 什么都沒有 |
測試完,我大吃一驚,感覺丟包了啊,而且是 cilium 節(jié)點(diǎn)把數(shù)據(jù)包丟了。帶著懷疑,有仔細(xì)檢查了兩個(gè) cilium 節(jié)點(diǎn)上的 ct 表,以及 networkpolicy 掛載點(diǎn)信息。
ENDPOINT 信息
ENDPOINT POLICY (ingress) POLICY (egress) IDENTITY LABELS (source:key[=value]) IPv6 IPv4 STATUS
ENFORCEMENT ENFORCEMENT
2636 Enabled Disabled 59271 k8s:app=wetown-java 10.242.2.149 ready
k8s:app_id=468205436992_wetown-java
k8s:app_name=wetown-java
k8s:appnew=468205436992_wetown-java
k8s:group=standard
k8s:io.cilium.k8s.namespace.labels.istio-injection=enabled
k8s:io.cilium.k8s.namespace.labels.project_id=468205436992_default
k8s:io.cilium.k8s.namespace.labels.project_name=default
k8s:io.cilium.k8s.namespace.labels.tenant_id=468205436992
k8s:io.cilium.k8s.policy.cluster=default
k8s:io.cilium.k8s.policy.serviceaccount=default
k8s:io.kubernetes.pod.namespace=468205436992-default
k8s:metric_source=customize
k8s:security.istio.io/tlsMode=istio
k8s:service.istio.io/canonical-name=wetown-java
k8s:service.istio.io/canonical-revision=latest
NETWORKPOLICY 信息
Defaulted container "cilium-agent" out of: cilium-agent, mount-cgroup (init), apply-sysctl-overwrites (init), clean-cilium-state (init)
POLICY DIRECTION LABELS (source:key[=value]) PORT/PROTO PROXY PORT BYTES PACKETS
Allow Ingress reserved:unknown 9999/TCP NONE 0 0
Allow Ingress reserved:host ANY NONE 408725 5154
Allow Ingress k8s:app=istiod ANY NONE 11824851 910
k8s:install.operator.istio.io/owning-resource=unknown
k8s:io.cilium.k8s.namespace.labels.istio-injection=disabled
k8s:io.cilium.k8s.namespace.labels.system=true
k8s:io.cilium.k8s.policy.cluster=default
k8s:io.cilium.k8s.policy.serviceaccount=istiod-1-11-8
k8s:io.kubernetes.pod.namespace=istio-system
k8s:istio.io/rev=1-11-8
k8s:istio=istiod
k8s:operator.istio.io/component=Pilot
k8s:sidecar.istio.io/inject=false
Allow Ingress k8s:app=istio-ingressgateway ANY NONE 8007 14
k8s:chart=gateways
k8s:heritage=Tiller
k8s:install.operator.istio.io/owning-resource=unknown
k8s:io.cilium.k8s.namespace.labels.istio-injection=disabled
k8s:io.cilium.k8s.namespace.labels.system=true
k8s:io.cilium.k8s.policy.cluster=default
k8s:io.cilium.k8s.policy.serviceaccount=istio-ingressgateway-service-account
k8s:io.kubernetes.pod.namespace=istio-system
k8s:istio.io/rev=1-11-8
k8s:istio=ingressgateway
k8s:operator.istio.io/component=IngressGateways
k8s:release=istio
k8s:service.istio.io/canonical-name=istio-ingressgateway
k8s:service.istio.io/canonical-revision=1-11-8
k8s:sidecar.istio.io/inject=false
Allow Ingress k8s:app=network-debug ANY NONE 0 0
k8s:io.cilium.k8s.namespace.labels.system=true
k8s:io.cilium.k8s.policy.cluster=default
k8s:io.cilium.k8s.policy.serviceaccount=default
k8s:io.kubernetes.pod.namespace=default
Allow Ingress k8s:app=wetown-java ANY NONE 0 0
k8s:app_id=468205436992_wetown-java
k8s:app_name=wetown-java
k8s:appnew=468205436992_wetown-java
k8s:group=standard
k8s:io.cilium.k8s.namespace.labels.istio-injection=enabled
k8s:io.cilium.k8s.namespace.labels.project_id=468205436992_default
k8s:io.cilium.k8s.namespace.labels.project_name=default
k8s:io.cilium.k8s.namespace.labels.tenant_id=468205436992
k8s:io.cilium.k8s.policy.cluster=default
k8s:io.cilium.k8s.policy.serviceaccount=default
k8s:io.kubernetes.pod.namespace=468205436992-default
k8s:metric_source=customize
k8s:security.istio.io/tlsMode=istio
k8s:service.istio.io/canonical-name=wetown-java
k8s:service.istio.io/canonical-revision=latest
k8s:tenant_id=468205436992
Allow Egress reserved:unknown ANY NONE 990293 6509
CT 信息
TCP IN 10.242.2.153:60064 -> 10.242.2.149:15021 expires=17276052 RxPackets=6 RxBytes=464 RxFlagsSeen=0x1b LastRxReport=17276043 TxPackets=4 TxBytes=371 TxFlagsSeen=0x1b LastTxReport=17276043 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:2464 -> 10.242.2.149:15020 expires=17276618 RxPackets=5 RxBytes=418 RxFlagsSeen=0x1b LastRxReport=17276609 TxPackets=5 TxBytes=376 TxFlagsSeen=0x1b LastTxReport=17276609 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:19582 -> 10.242.2.149:15021 expires=17275992 RxPackets=6 RxBytes=464 RxFlagsSeen=0x1b LastRxReport=17275983 TxPackets=4 TxBytes=371 TxFlagsSeen=0x1b LastTxReport=17275983 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:17164 -> 10.242.2.149:15021 expires=17276443 RxPackets=6 RxBytes=464 RxFlagsSeen=0x1b LastRxReport=17276434 TxPackets=4 TxBytes=371 TxFlagsSeen=0x1b LastTxReport=17276434 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:15068 -> 10.242.2.149:15020 expires=17276115 RxPackets=5 RxBytes=418 RxFlagsSeen=0x1b LastRxReport=17276106 TxPackets=5 TxBytes=376 TxFlagsSeen=0x1b LastTxReport=17276106 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:3468 -> 10.242.2.149:15021 expires=17276081 RxPackets=6 RxBytes=464 RxFlagsSeen=0x1b LastRxReport=17276072 TxPackets=4 TxBytes=371 TxFlagsSeen=0x1b LastTxReport=17276072 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:60072 -> 10.242.2.149:15021 expires=17276413 RxPackets=6 RxBytes=464 RxFlagsSeen=0x1b LastRxReport=17276404 TxPackets=4 TxBytes=371 TxFlagsSeen=0x1b LastTxReport=17276404 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:13152 -> 10.242.2.149:15021 expires=17276083 RxPackets=6 RxBytes=464 RxFlagsSeen=0x1b LastRxReport=17276074 TxPackets=4 TxBytes=371 TxFlagsSeen=0x1b LastTxReport=17276074 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:60054 -> 10.242.2.149:15021 expires=17276509 RxPackets=18 RxBytes=1392 RxFlagsSeen=0x1b LastRxReport=17276500 TxPackets=12 TxBytes=1113 TxFlagsSeen=0x1b LastTxReport=17276500 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:14902 -> 10.242.2.149:15020 expires=17276263 RxPackets=5 RxBytes=417 RxFlagsSeen=0x1b LastRxReport=17276254 TxPackets=5 TxBytes=376 TxFlagsSeen=0x1b LastTxReport=17276254 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:7100 -> 10.242.2.149:15021 expires=17276003 RxPackets=6 RxBytes=464 RxFlagsSeen=0x1b LastRxReport=17275994 TxPackets=4 TxBytes=371 TxFlagsSeen=0x1b LastTxReport=17275994 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:1396 -> 10.242.2.149:15020 expires=17276207 RxPackets=5 RxBytes=418 RxFlagsSeen=0x1b LastRxReport=17276198 TxPackets=5 TxBytes=376 TxFlagsSeen=0x1b LastTxReport=17276198 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:11408 -> 10.242.2.149:15020 expires=17275973 RxPackets=5 RxBytes=418 RxFlagsSeen=0x1b LastRxReport=17275964 TxPackets=5 TxBytes=376 TxFlagsSeen=0x1b LastTxReport=17275964 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:60010 -> 10.242.2.149:15020 expires=17276575 RxPackets=63 RxBytes=5171 RxFlagsSeen=0x1b LastRxReport=17276566 TxPackets=60 TxBytes=4512 TxFlagsSeen=0x1b LastTxReport=17276566 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:18134 -> 10.242.2.149:15020 expires=17276315 RxPackets=5 RxBytes=418 RxFlagsSeen=0x1b LastRxReport=17276306 TxPackets=5 TxBytes=376 TxFlagsSeen=0x1b LastTxReport=17276306 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:3438 -> 10.242.2.149:15021 expires=17276076 RxPackets=6 RxBytes=464 RxFlagsSeen=0x1b LastRxReport=17276067 TxPackets=4 TxBytes=371 TxFlagsSeen=0x1b LastTxReport=17276067 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:17962 -> 10.242.2.149:15020 expires=17276032 RxPackets=5 RxBytes=418 RxFlagsSeen=0x1b LastRxReport=17276023 TxPackets=5 TxBytes=376 TxFlagsSeen=0x1b LastTxReport=17276023 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:10100 -> 10.242.2.149:15020 expires=17276500 RxPackets=5 RxBytes=418 RxFlagsSeen=0x1b LastRxReport=17276491 TxPackets=5 TxBytes=376 TxFlagsSeen=0x1b LastTxReport=17276491 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:8154 -> 10.242.2.149:15021 expires=17276329 RxPackets=12 RxBytes=928 RxFlagsSeen=0x1b LastRxReport=17276320 TxPackets=8 TxBytes=742 TxFlagsSeen=0x1b LastTxReport=17276320 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
TCP IN 10.242.2.153:19556 -> 10.242.2.149:15021 expires=17275986 RxPackets=6 RxBytes=464 RxFlagsSeen=0x1b LastRxReport=17275977 TxPackets=4 TxBytes=371 TxFlagsSeen=0x1b LastTxReport=17275977 Flags=0x0013 [ RxClosing TxClosing SeenNonSyn ] RevNAT=0 SourceSecurityID=1 IfIndex=0
......
通過檢查,就是沒有發(fā)現(xiàn)從 kubenet 網(wǎng)絡(luò)的節(jié)點(diǎn)轉(zhuǎn)發(fā)過來的流量。 然后回想到應(yīng)用上有掛載 networkpolicy,我這邊果斷刪除這個(gè)業(yè)務(wù) pod 上掛載的 networkpolicy,然后在所有測試點(diǎn)抓包。 全部都通了,現(xiàn)在破案了,應(yīng)該是 networkpolicy 的問題。
這里又來一個(gè)隱含的問題,既然在同一個(gè) k8s 內(nèi),有 cilium 網(wǎng)絡(luò)的節(jié)點(diǎn),也有傳統(tǒng)的 kubenet 網(wǎng)絡(luò)的節(jié)點(diǎn)。 為什么會(huì)出現(xiàn)不通呢?為了驗(yàn)證這個(gè)問題,我們又做了如下一個(gè)實(shí)驗(yàn),還是上圖:

在做這個(gè)實(shí)驗(yàn)前,有一點(diǎn)可以確認(rèn):丟包點(diǎn)是在兩種不同的網(wǎng)絡(luò)模型,并不是外部進(jìn)入 k8s 集群,所以只要關(guān)注 C 點(diǎn)流量。
測試方法跟之間一樣,然后我們在不同的點(diǎn)抓包,這次結(jié)果也讓我驚掉了下巴。
| 測試內(nèi)容 | 狀態(tài)介紹 | 測試點(diǎn) | 測試結(jié)果 |
|---|---|---|---|
| kubenet 節(jié)點(diǎn)上 pod 訪問 cilium 節(jié)點(diǎn)上業(yè)務(wù) pod | 1. 發(fā)起請求的測試 pod 運(yùn)行在 kubenet 節(jié)點(diǎn)上 2. 被測試業(yè)務(wù) pod 運(yùn)行在 cilium 節(jié)點(diǎn)上 3. 被測試業(yè)務(wù) pod 配置有 networkpolicy |
C 點(diǎn) | 失敗 |
| cilium 節(jié)點(diǎn)上 pod 訪問 kubenet 節(jié)點(diǎn)上業(yè)務(wù) pod | 1. 發(fā)起請求的測試 pod 運(yùn)行在 kubenet 節(jié)點(diǎn)上 2. 被測試業(yè)務(wù) pod 運(yùn)行在 cilium 節(jié)點(diǎn)上 3. 被測試業(yè)務(wù) pod 配置有 networkpolicy |
B 點(diǎn) | 成功 |
| kubenet 節(jié)點(diǎn)上 pod 訪問 cilium 節(jié)點(diǎn)上業(yè)務(wù) pod (去除 cilium 節(jié)點(diǎn)上 pod 上的 networkpolicy) |
1. 發(fā)起請求的測試 pod 運(yùn)行在 kubenet 節(jié)點(diǎn)上 2. 被測試 pod 運(yùn)行在 cilium 節(jié)點(diǎn)上 3. 刪除被測試 pod 的 networkpolicy 4. 被測試 pod 監(jiān)聽 8080 端口 5. 測試 pod 發(fā)起 tcp 請求到被測試 pod |
C 點(diǎn) | 成功 |
| cilium 節(jié)點(diǎn)上 pod 訪問 kubenet 節(jié)點(diǎn)上業(yè)務(wù) pod (去除 cilium 節(jié)點(diǎn)上 pod 上的 networkpolicy) |
1. 發(fā)起請求的測試 pod 運(yùn)行在 kubenet 節(jié)點(diǎn)上 2. 被測試 pod 運(yùn)行在 cilium 節(jié)點(diǎn)上 3. 刪除被測試 pod 的 networkpolicy 4. 測試 pod 監(jiān)聽 8080 端口 5. 被測試 pod 發(fā)起 tcp 請求到測試 pod |
B 點(diǎn) | 成功 |
總結(jié)下: 從 kubenet 網(wǎng)絡(luò)的節(jié)點(diǎn)上 pod 訪問帶有 networkpolicy 的 cilium 網(wǎng)絡(luò)的節(jié)點(diǎn)上 pod,是不通,其他的場景都是 OK 的。
技術(shù)解析
那么是不是有這樣一種猜想,cilium 不識(shí)別其他的網(wǎng)絡(luò)模型的節(jié)點(diǎn)呢?讓我們看看 cilium cluster 信息:
Cluster health: 15/15 reachable (2022-12-12T08:19:02Z)
Name IP Node Endpoints
10.118.201.53 (localhost) 10.118.201.53 reachable reachable
10.118.201.48 10.118.201.48 reachable reachable
10.118.201.49 10.118.201.49 reachable reachable
10.118.201.50 10.118.201.50 reachable reachable
10.118.201.51 10.118.201.51 reachable reachable
10.118.201.52 10.118.201.52 reachable reachable
10.118.201.54 10.118.201.54 reachable reachable
10.118.201.55 10.118.201.55 reachable reachable
10.118.201.56 10.118.201.56 reachable reachable
10.118.201.57 10.118.201.57 reachable reachable
10.118.201.58 10.118.201.58 reachable reachable
10.118.201.59 10.118.201.59 reachable reachable
10.118.201.60 10.118.201.60 reachable reachable
10.118.201.61 10.118.201.61 reachable reachable
10.118.201.62 10.118.201.62 reachable reachable
通過節(jié)點(diǎn)信息,我們可以清楚看出,cilium 只管理部署了 cilium agent 的節(jié)點(diǎn)。其他的 kubenet 網(wǎng)絡(luò)節(jié)點(diǎn)他是不管的,也就是這些節(jié)點(diǎn)對他來說的跟外部服務(wù)器一樣,從這些節(jié)點(diǎn)傳入的網(wǎng)絡(luò)流量都當(dāng)成外部流量,即便在同一個(gè) k8s 內(nèi),cilium 也不看你流量信息,以及來源哪里。
說到這里,不得不提到之前寫一篇文章《ebpf 開發(fā)入門之核心概念篇》,那里面提到了一個(gè) HOOK 的概念,networkpolicy 就是通過 HOOK 掛載到容器的網(wǎng)卡的 Ingress 上。 既然 kubenet 網(wǎng)絡(luò)的節(jié)點(diǎn) 和 cilium 網(wǎng)絡(luò)的節(jié)點(diǎn)都在同一個(gè) k8s 內(nèi),但是 cilium 的只管理自己的節(jié)點(diǎn),其他的節(jié)點(diǎn)都認(rèn)為是外部的,如果不匹配 Ingress 的規(guī)則,都會(huì)被拒絕。
通過抓包測試,以及分析 cilium 的相關(guān)信息,我們找到問題所在:cilium 如果沒有管理全部的 k8s 所有節(jié)點(diǎn),剩余的節(jié)點(diǎn)就按照外部服務(wù)器的方式來處理。
處理方法
知道了原因,有了實(shí)驗(yàn)驗(yàn)證,那么辦法也當(dāng)然就有了,當(dāng)然也很簡單。
- 移動(dòng)所有的 pod 到 cilium 網(wǎng)絡(luò)的節(jié)點(diǎn)
- 移動(dòng) Ingress pod 到 cilium 網(wǎng)絡(luò)的節(jié)點(diǎn)
- 刪除 cilium 受影響 pod 的 networkpolicy
從解決辦法上看,最合理也最有可操作性的是:2 號(hào)
最終效果
通過調(diào)整后,最終結(jié)果當(dāng)然也復(fù)合預(yù)期,所有的業(yè)務(wù)互聯(lián)互通都沒有了問題。同時(shí)也在加速 pod 的遷移,盡量減少這樣的不合理的共存環(huán)境。