Kubernetes 系列(三):Pod 網路如何實際運作 - 從 localhost 到 Service VIP
前言
上一篇介紹了 Pod、Deployment 與 Service 的基本角色,但「有一個 Service」和「封包真的走得通」是兩件不同的事。本篇不再把網路元件當成名詞表,而是從一個 Pod 內的 localhost 開始,逐層追蹤封包如何走到另一個 Pod。
你會看到 network namespace、veth pair、Node routing、CNI、EndpointSlice、Service VIP 與 CoreDNS 各自負責哪一段。不同叢集可能使用 overlay、雲端 VPC 原生路由、eBPF 或其他資料平面,但 Kubernetes 對使用者提供的抽象仍然可以用同一套問題來理解:誰在查名字?誰在選後端?誰在搬封包?誰在阻擋封包?
本篇完成後,你應該能回答:
- 同一個 Pod 裡的兩個 container,為什麼可以用
localhost互相呼叫? - Pod A 直接連 Pod B 的 Pod IP,和連 Service 的 ClusterIP 有什麼不同?
- 同節點與跨節點的封包,通常會經過哪些網路介面與路由?
- CoreDNS 為什麼只參與 DNS lookup,而不是 HTTP/TCP 的代理?
EndpointSlice、kube-proxy或 CNI 的 eBPF dataplane,如何共同讓 Service VIP 找到後端?- 加上 NetworkPolicy 後,為什麼 DNS 可能突然失效?
先分清楚四種網路路徑
Kubernetes 官方網路模型其實把問題拆成四類。若沒有先區分它們,很容易把「Pod 有 IP」、「Service 有 DNS」和「外部流量能進來」誤認成同一件事。
| 路徑 | 目的地 | 位址是否穩定 | 中間主要處理者 | 適合用來理解什麼 |
|---|---|---|---|---|
| 同一 Pod 內的 container-to-container | 另一個 container 的 localhost:port 或 Pod IP |
Pod 網路位址相同 | 同一個 network namespace 的 Linux stack | sidecar、代理、主程式之間的本機通訊 |
| Pod IP 直連 | PodIP:port |
通常不穩定,Pod 重建可能換 IP | CNI、Node routing、底層網路 | Pod-to-Pod 封包如何在同節點或跨節點傳遞 |
| Service VIP | ClusterIP:port,通常先透過 DNS 找到 |
Service VIP 通常穩定,後端 Pod 可變 | CoreDNS 做 lookup;Service dataplane 選 endpoint | 用穩定名稱與虛擬 IP 存取一組後端 |
| 外部入口 | NodePort、外部 Load Balancer、Gateway 或 Ingress 等 | 取決於叢集與雲端整合 | 叢集外部設備加上叢集內資料平面 | 外部使用者如何進入叢集 |
可以先用下面這張圖建立邊界。圖中的箭頭不是表示每個叢集一定有相同的實作,而是表示每種路徑的抽象責任:
flowchart TB
subgraph POD[同一個 Pod]
C1[Container A]
C2[Container B]
C1 -->|localhost:8080| C2
end
subgraph CLUSTER[叢集內]
PA[Pod A]
PB[Pod B]
VIP[Service VIP]
PA -->|Pod IP 直連| PB
PA -->|ClusterIP:port| VIP
VIP -->|選擇 ready endpoint| PB
end
USER[叢集外 client] -->|外部入口| ENTRY[LB / NodePort / Gateway / Ingress]
ENTRY --> VIP
四個常見誤解
- DNS 名稱不是封包路徑:DNS 查詢只負責把名稱解析成 IP。解析完成後,應用程式會自行對該 IP 建立 TCP、UDP 或其他連線。
- Service 不是一個一定存在的伺服器程序:
ClusterIP通常是虛擬 IP,可能由 node 上的 iptables、nftables、IPVS 或 eBPF 規則處理。 - Pod IP 不是服務發現介面:它適合診斷與觀察,不適合由應用程式長期寫死。
- 外部入口不是本篇的重點:L4/L7 Load Balancing、TLS termination、Ingress、Gateway、來源 IP 與外部流量策略,會在後續 Part 8 集中說明。
Pod 為什麼可以使用 localhost?
Network namespace 是隔離網路的邊界
Linux 的 network namespace(網路命名空間,簡稱 netns)會隔離一組網路資源,例如:
- 網路介面,例如
lo、eth0 - IP 位址與 ARP/neighbor table
- routing table
- iptables/nftables 等部分網路狀態
- socket 與 port 的視野
Kubernetes 的 Pod 通常會先建立一個 Pod sandbox,再讓同一 Pod 的 containers 加入這個 network namespace。不同 container 仍然有各自的檔案系統、程序與環境變數,但它們共享網路介面、IP 與 port 空間。
因此,同一個 Pod 裡的 container 可以這樣互相呼叫:
1 | Container A -- http://127.0.0.1:8080 --> Container B |
這也代表兩個 container 不能同時在同一個 Pod 裡監聽相同的 IP:port。例如主程式已經使用 0.0.0.0:8080,sidecar 就不能再綁定同一個 port;但它可以使用另一個 port,例如 127.0.0.1:15000。
localhost 不是「同一台 Node」
localhost 只表示「目前這個 process 所在的 network namespace」。以下三個情境完全不同:
1 | 同一 Pod 的 container B -> 127.0.0.1:8080 可達 |
如果服務只綁定 127.0.0.1,它只會接受同一 network namespace 內的連線。要讓其他 Pod 透過 Pod IP 連線,服務通常要監聽 0.0.0.0 或 Pod eth0 的位址;Kubernetes 的 containerPort 欄位本身不會替程式打開 port,也不會改變程式的 bind address。
hostNetwork: true 是一個需要特別留意的例外:這類 Pod 會使用 Node 的 network namespace,因此它的 localhost 指向 Node,而不是獨立的 Pod 網路。這也會帶來 port 衝突與隔離性降低等取捨,不應和一般 Pod 混為一談。
veth pair:把 Pod 接到 Node
在常見的 Linux CNI 實作中,Pod netns 內的 eth0 會和 Node netns 裡的一個虛擬介面組成 veth pair。一端放在 Pod,另一端留在 Node;兩端像一條虛擬網路線,寫入其中一端的封包會從另一端出現。
flowchart LR
subgraph PNS[Pod network namespace]
LO[lo<br/>127.0.0.1]
PE[eth0<br/>Pod IP]
end
subgraph NNS[Node network namespace]
VE[vethXXXX]
ROUTE[bridge / Linux route / eBPF]
NIC[Node NIC]
end
PE <-->|veth pair| VE
VE --> ROUTE
ROUTE --> NIC
LO -.->|只留在同一 netns| PE
這是很常見的資料路徑,但不是 Kubernetes 的固定規格。某些 CNI 會使用 Linux bridge,某些會直接設定路由或雲端 ENI,某些會在 socket 或封包 hook 上使用 eBPF。學習重點是:Pod 的介面在自己的 netns,Node 必須有某種機制把它接到其他 Pod 與外部網路。
CNI、Node routing 與同節點/跨節點差異
CNI 到底負責什麼?
CNI(Container Network Interface)是 container runtime 與網路插件之間的介面與規格,不是一個單獨的「Kubernetes 網路」。當 Pod sandbox 建立或刪除時,runtime 會依設定呼叫 CNI plugin;實際插件可能由多個元件組成。
一個 Pod 啟動時,CNI 常見會處理以下工作:
| 工作 | 可能的結果 |
|---|---|
| IPAM | 從 Pod CIDR、VPC、ENI 或其他位址池分配 Pod IP |
| 建立介面 | 建立 veth、bridge、ENI 或其他網路裝置 |
| 加入 network namespace | 把 Pod 端介面命名為 eth0,設定 IP 與 link state |
| 設定路由 | 指定 default route、Pod CIDR 路由或下一跳 |
| 讓跨節點可達 | 使用 Node route、overlay、雲端路由或其他 dataplane |
| 實作政策與可觀測性 | 視插件能力加入 NetworkPolicy、流量追蹤與 metrics |
Pod IP 由網路插件的位址管理機制提供;Service 的 ClusterIP 則是 Kubernetes API/控制面分配的另一個位址範圍。 兩者不要混為同一個 IP pool。
同一節點:封包通常不必離開 Node
假設 Pod A 與 Pod B 在同一個 Node,抽象路徑可以畫成:
flowchart LR
A[Pod A netns<br/>eth0: Pod IP A]
AV[veth A]
R[Node bridge / route / eBPF]
BV[veth B]
B[Pod B netns<br/>eth0: Pod IP B]
A --> AV --> R --> BV --> B
封包通常會從 Pod A 的 eth0 進入 Node,再由同一個 Node 上的 bridge、routing table 或 eBPF dataplane 送到 Pod B 的介面。它不需要經過實體網卡,也不需要因為「是兩個 Pod」就經過 CoreDNS 或 Service。
要注意的是,Kubernetes 網路模型期待 Pod-to-Pod 通訊不需要 NAT;但「不需要 NAT」不等於每個 CNI 都有相同的 Linux 實作。你仍然可能看到 bridge、路由、封裝或 eBPF 等不同組合。
跨節點:Node 之間要知道 Pod 網段怎麼走
如果 Pod A 在 Node 1、Pod B 在 Node 2,封包需要先離開 Node 1,再到 Node 2,最後進入 Pod B 的 netns:
flowchart LR
A[Pod A netns] --> N1[Node 1 route / dataplane]
N1 -->|underlay、overlay 或雲端網路| N2[Node 2 route / dataplane]
N2 --> B[Pod B netns]
跨節點能否成功,取決於 CNI 如何把 Pod 網段告訴 Node 或底層網路,以及防火牆、MTU、封裝與回程路由是否一致。這也是「同一 Node 的 Pod 可以互通,但換到另一個 Node 就 timeout」時,應該檢查 CNI 與 Node routing,而不是先猜應用程式壞掉的原因。
Overlay、VPC 原生路由與 eBPF 是選項,不是答案
不同叢集可以用不同的 dataplane。常見取捨如下:
| 方式 | 封包如何跨節點 | 常見優點 | 常見代價或注意事項 |
|---|---|---|---|
| Overlay | 把 Pod 封包包在 Node-to-Node 的外層封包中,例如 VXLAN 或 Geneve | 對底層網路要求較少,Pod 網段彈性較高 | 有封裝成本、MTU 需要正確設定,故障排查多一層 |
| VPC/原生路由 | 讓雲端路由表、ENI 或 Node route 直接知道 Pod 網段 | 可減少封裝,和雲端網路整合較自然 | 需要管理路由與 IP 配額,跨環境可攜性較低 |
| eBPF dataplane | 在 kernel hook 或 socket 層執行 forwarding、policy、Service translation 等程式 | 可整合可觀測性與快速路徑,部分情況可取代 kube-proxy | 需要相容的 kernel、CNI 與運維工具,行為不能直接套用到其他 CNI |
這些選項不是互斥的。例如使用 eBPF 的 CNI 仍可能搭配 overlay,也可能使用雲端原生路由。排錯時請先確認實際使用的 CNI、版本、kube-proxy 模式與網路拓撲:
1 | kubectl get pods -n kube-system -o wide |
在有權限的 Node 上,才進一步查看 ip addr、ip route、iptables、nft、ipvsadm 或 CNI 自己的 CLI。不要因為某篇文章的圖畫了 veth,就假設你的雲端叢集一定存在同名的介面或規則。
Service VIP 到底怎麼找到 Pod?
Pod IP 適合用來觀察封包,但應用程式通常需要一個不會隨 Pod 重建而改變的入口。Service 提供的就是這個抽象:一個穩定的虛擬 IP、port 與一組後端 endpoint。
先看控制面:Service、selector 與 EndpointSlice
假設有一個 Service:
1 | apiVersion: v1 |
這裡有三個容易混淆的 port:
port: 80:client 看到的 Service port。targetPort: 8080:後端 Pod 中程式實際監聽的 port。- Pod 的
containerPort:描述性欄位,不會自動讓程式監聽該 port。
控制面會根據 Service selector 找出符合 app: api 的 Pod,然後由 EndpointSlice controller 建立或更新 EndpointSlice。EndpointSlice 會記錄 endpoint IP、port、所在 Node 與 ready、serving、terminating 等狀態;不是只要 Pod 有 IP 就一定會被導流。
可以把這段控制流程想成:
flowchart LR
S[Service selector<br/>app: api]
C[EndpointSlice controller]
E[EndpointSlice<br/>Pod IP + port + conditions]
D[Service dataplane<br/>kube-proxy 或 CNI]
V[ClusterIP:80]
P1[Ready Pod 1:8080]
P2[Ready Pod 2:8080]
S --> C --> E --> D
D --> V
D --> P1
D --> P2
EndpointSlice 是後續資料平面知道「有哪些後端」的重要來源。若 selector 寫錯、Pod 沒有 Ready、port 對不上,Service 可能存在、DNS 也能解析,但仍然沒有可用流量。
再看資料面:ClusterIP 是虛擬入口
client 執行 curl http://api.demo.svc.cluster.local/ 時,可以拆成以下步驟:
sequenceDiagram
participant A as Client Pod
participant DNS as CoreDNS
participant DP as Service dataplane
participant B as Ready backend Pod
A->>DNS: DNS lookup api.demo.svc.cluster.local
DNS-->>A: 回傳 Service ClusterIP
A->>DP: 連線到 ClusterIP:80
DP->>DP: 依 Service 與 EndpointSlice 選擇 endpoint
DP->>B: 轉送到 PodIP:8080
B-->>A: 回應
這裡最重要的分界是:CoreDNS 回答名稱查詢後,通常不會留在 HTTP/TCP 封包的路徑上。 後續封包由 Node 或 CNI 的 Service dataplane 處理。
Service dataplane 不一定是同一種實作
許多叢集使用 Node 上的 kube-proxy,也有叢集使用 CNI 提供的 kube-proxy replacement。常見選項如下:
| dataplane | 大致作法 | 不應該做的假設 |
|---|---|---|
kube-proxy 的 iptables mode |
建立 netfilter 規則,把 Service VIP/port 導向某個 endpoint,常見會做 DNAT | 不要把每次請求都想成固定的 Round Robin |
kube-proxy 的 IPVS mode |
使用 Linux IPVS 與相關規則處理虛擬服務與後端 | 是否可用、是否被棄用或預設啟用,取決於 Kubernetes 版本與叢集設定 |
kube-proxy 的 nftables mode |
使用 nftables 規則實作 Service 虛擬 IP | 不要假設所有 Linux Node 都還是 iptables |
| CNI eBPF dataplane | 由 CNI 在 kernel 層執行 Service translation、負載分配與 policy | 不要假設一定存在 kube-proxy 的規則或程序 |
Service 的後端選擇還可能受到 sessionAffinity、traffic policy、拓撲偏好與連線本身的 keep-alive 影響。Kubernetes 沒有保證你用幾次 curl 就會看見整齊的 Round Robin 序列;如果要了解實際分布,應檢查 Service 設定、EndpointSlice、dataplane 與應用程式連線行為。
ClusterIP、Pod IP 與 Headless Service 的比較
| 連線目標 | 是否經過一般 Service VIP | client 看到什麼 | 典型用途 |
|---|---|---|---|
| Pod IP | 否 | 單一 Pod IP | 除錯、低階網路測試 |
| 一般 Service | 是 | 穩定的 ClusterIP | 對一組可替換後端提供穩定入口 |
Headless Service(clusterIP: None) |
沒有 VIP proxy | DNS 可能回傳多個 endpoint Pod IP | StatefulSet、由 client 自己選擇後端 |
Headless Service 仍然會使用叢集 DNS 來提供服務發現,但它和一般 Service 的 VIP 與負載處理路徑不同。完整的 DNS record、SRV、StatefulSet hostname 與 Service Discovery 會在後續 Part 7 深入。
CoreDNS:只負責查名字,不代理你的 HTTP
CoreDNS 是 Kubernetes 叢集常見的 DNS server。Pod 啟動時,kubelet 會依 Pod 的 DNS policy 寫入 /etc/resolv.conf;一般 ClusterFirst 設定會讓叢集網域先送到叢集 DNS,再視設定轉送外部網域查詢。
在一個叫 net-lab 的 namespace 裡,Service echo 的完整名稱通常是:
1 | echo.net-lab.svc.cluster.local |
其中 cluster.local 是常見的 cluster domain,但不是永遠固定,實際值以叢集設定為準。一般 Service 的 DNS lookup 通常回傳 ClusterIP;Headless Service 則可能直接回傳多個後端 Pod IP。
可以在 Pod 裡觀察基本設定:
1 | cat /etc/resolv.conf |
接著應用程式可能做以下事情:
1 | 1. 對 CoreDNS 的 UDP/TCP 53 port 做 DNS query |
第 1 步會經過 CoreDNS;第 3、4 步不是 CoreDNS 在代理。這個界線在排錯時很有用:DNS 能解析,只代表名字查得到,不代表後續 port、路由、policy 或後端程式一定可達。
Service Discovery 的 DNS 搜尋路徑、ndots、Headless Service、SRV record、DNS cache 與跨 namespace 查詢,將在後續 Part 7 整理成獨立篇章;本篇只保留理解封包路徑所需的最小背景。
NetworkPolicy:誰可以進來,誰可以出去?
NetworkPolicy 是對 Pod 流量的 L4 層級規則,主要描述 TCP、UDP(以及視 CNI 支援程度而定的 SCTP)來源、目的地與 port。它不是 Kubernetes API Server 自己執行的防火牆;只有選用的網路插件支援 policy enforcement 時,建立 policy 才會真的影響封包。
Ingress 與 egress 的方向
方向是以「被 policy 選中的 Pod」為中心:
Ingress:誰可以連進被選中的 Pod,以及可以連哪些 port。Egress:被選中的 Pod 可以連到誰,以及可以連哪些 port。podSelector:選擇 Pod;在NetworkPolicy本身的 namespace 中,沒有另外指定 namespace 時,代表同一個 namespace 的 Pod。namespaceSelector:選擇 namespace;若只寫它,通常表示該 namespace 內符合條件的所有 Pod。- 同一個
from或to項目中同時寫namespaceSelector與podSelector:兩者是 AND,表示指定 namespace 裡再指定 label 的 Pod。 from/to陣列中分成兩個項目:兩個項目是 OR。
多個 NetworkPolicy 選中同一批 Pod 時,允許規則會合併;不是後建立的 policy 自動覆蓋先前的 policy。實務上常用「先 default deny,再逐條 allow」的方式建立白名單。
先拒絕 API 的所有入站,再開放指定來源
下面的兩個 policy 都放在 net-lab namespace,目標 Pod 是 label app: api。第一個 policy 選中 net-lab 裡所有 Pod 並拒絕 ingress;第二個 policy 再放行 app: client,以及 gateway-system namespace 中 label 為 app: gateway 的 Pod。
1 | apiVersion: networking.k8s.io/v1 |
這個例子沒有聲稱「所有 gateway namespace 的 Pod 都能連線」:只有同時符合 namespace label 與 Pod label 的來源才符合第二個項目。也沒有把 web、api 混在 selector 和註解裡,讀者可以直接對照 policy 的目標與來源。
default deny egress 最容易漏掉 DNS
如果對 net-lab 的所有 Pod 套用 default deny egress,Pod 連 CoreDNS 的 UDP/TCP 53 也會被拒絕。結果常見為:直接使用 IP 的測試還可能成功,但使用 Service 名稱的應用程式看起來像「Service 壞了」。
下面是允許 net-lab Pod 存取 CoreDNS 的示例。k8s-app: kube-dns 是許多叢集使用的 label,但託管服務或自訂部署可能不同,套用前應先用 kubectl get pods -n kube-system --show-labels 確認:
1 | apiVersion: networking.k8s.io/v1 |
只允許 DNS 不代表應用程式流量也被允許;在同一套 default deny egress 下,仍需要另外允許 client 到 API 的 egress port,以及 API Pod 的 ingress。DNS 可能先用 UDP,再因回應太大或其他狀況使用 TCP,因此實務上通常同時放行 UDP 與 TCP 53。
NetworkPolicy 對 ICMP、ARP 等非 TCP/UDP/SCTP 流量的行為可能因 CNI 而異。不要用 ping 一個失敗就直接判定 TCP 一定不通,應使用和實際服務相同的協定與 port 測試。
可執行的 kind 多節點實驗:把封包路徑看出來
下面的實驗使用 kind 建立一個 control-plane 加兩個 worker 的本地叢集,並使用 nicolaka/netshoot 作為除錯 Pod。實驗目標不是模擬所有雲端 CNI,而是讓你實際看到:
- Pod netns 內的
/etc/resolv.conf、介面與路由 - 同一 Pod 內的
localhost - 跨 Node 的 Pod IP 直連
- Service DNS 查詢、ClusterIP 與 EndpointSlice 的差異
1. 建立多節點 kind 叢集
需要先安裝 Docker(或相容的 container runtime)、kind 與 kubectl。kind 會使用自己的預設網路插件;本實驗觀察 Pod 網路與 Service 即可,不把它當成 NetworkPolicy enforcement 的驗證環境。
1 | kind create cluster --name k8s-net-lab --config=- <<'EOF' |
這份設定會產生:
1 | k8s-net-lab-control-plane |
為了刻意讓 client 與 server 分散到兩個 worker,替 Node 加上實驗用 label:
1 | kubectl label node k8s-net-lab-worker lab-role=client --overwrite |
2. 部署 server、Service、netshoot 與共享網路的 Pod
echo Deployment 被固定到 server Node;netshoot 被固定到另一個 client Node。因此從 netshoot 連 echo Pod IP 時,可以觀察跨節點路徑。實際 Pod IP、Service IP 與網路介面名稱會由叢集動態分配,不要照抄下方的數字。
1 | kubectl apply -f - <<'EOF' |
等待資源就緒並確認 placement:
1 | kubectl wait --for=condition=available deployment/echo -n net-lab --timeout=120s |
預期可以看到:
echoPod 在k8s-net-lab-worker2。netshootPod 在k8s-net-lab-worker。echoService 有一個 ClusterIP。EndpointSlice的 endpoint address 是echoPod 的 Pod IP,且 ready condition 為true。
如果 Pod 是 Pending,先執行 kubectl describe pod -n net-lab <pod-name>;最常見的原因是 Node label 拼錯或對應的 worker 不存在。
3. 從 netshoot 觀察自己的 network namespace
1 | kubectl exec -n net-lab netshoot -- cat /etc/resolv.conf |
把三個輸出連起來看:
eth0上的位址是這個 Pod 的 IP。lo是 Pod 自己的 loopback,127.0.0.1不會指向其他 Pod。- default route 的 gateway 與 Pod CIDR route 由 CNI 設定,實際數值依 kind 與 CNI 而異。
/etc/resolv.conf的 nameserver 是叢集 DNS 入口;它不是echoService 的 ClusterIP。
4. 證明同一 Pod 內的 localhost
shared-net 裡的 web container 監聽 port 80,debug container 使用同一個 Pod network namespace。因此 debug 可以透過 127.0.0.1:80 呼叫 web:
1 | kubectl exec -n net-lab shared-net -c debug -- \ |
這個 localhost 指向 shared-net 這個 Pod 的 network namespace;它不是 netshoot Pod,也不是所在 Node 的其他 Pod。
5. 分別測試 Pod IP 與 Service
先從 API 取得動態位址,再從 netshoot Pod 發出請求:
1 | POD_IP=$(kubectl get pod -n net-lab -l app=echo \ |
對這三個測試應該這樣解讀:
- Pod IP 測試成功,表示目前 Pod IP、port、跨 Node 路徑與 nginx 至少可用。
- ClusterIP 測試成功,額外表示 Service selector、EndpointSlice 與 Service dataplane 能把 VIP 導到 endpoint。
- DNS 名稱測試成功,額外表示 Pod 可以連到 CoreDNS 並取得正確的 Service record;CoreDNS 回答後不會代理後面的 HTTP response。
若要觀察多個 endpoint 的 Service 選擇,可以把 echo Deployment 擴成多個 replicas,再查看 EndpointSlice;不要用少量短連線結果宣稱固定的 Round Robin:
1 | kubectl scale deployment/echo -n net-lab --replicas=2 |
6. 清理實驗環境
確認不再需要實驗叢集後:
1 | kind delete cluster --name k8s-net-lab |
用分層方式排查網路問題
不要一開始就把所有問題都叫做「Kubernetes 網路壞了」。先問哪一條路徑失敗,再逐層縮小範圍:
| 現象 | 優先懷疑的層 | 建議檢查 |
|---|---|---|
同 Pod 的 localhost 成功,但 Pod IP 失敗 |
程式 bind address、Pod port、policy | 程式是否只監聽 127.0.0.1、ss -lntp、NetworkPolicy |
| Pod IP 成功,但 Service VIP 失敗 | selector、EndpointSlice、Service port、dataplane | kubectl get svc、kubectl get endpointslice、targetPort、kube-proxy/CNI |
| Service IP 成功,但 Service DNS 失敗 | /etc/resolv.conf、CoreDNS、DNS egress |
dig、CoreDNS Pod/Service、UDP/TCP 53 policy |
| 同 Node 成功,跨 Node 失敗 | CNI、Node route、overlay/VPC、MTU、Node firewall | kubectl get pod -o wide、ip route、CNI logs、封裝與 MTU |
| DNS 能解析,但 HTTP timeout | DNS 之外的資料面 | Endpoint readiness、port、policy、return route、應用程式 logs |
| Service 存在但 EndpointSlice 沒 endpoint | 控制面條件尚未滿足 | selector label、Pod Ready condition、namespace、port |
一個實用的排查順序是:
1 | 1. 先在來源 Pod 看 /etc/resolv.conf、ip addr、ip route |
這個順序的價值在於把「名稱解析」、「VIP 選擇」、「Pod 路由」和「應用程式監聽」分開測。每一層都成功,才代表整條路徑成功。
本篇刻意不深入的主題
為了把 Pod-to-Pod 與 Service VIP 講完整,本篇刻意縮短以下內容:
- L4/L7 Load Balancing:TCP/UDP 與 HTTP/gRPC 在 NodePort、LoadBalancer、Ingress、Gateway 的差異。
- 外部入口:雲端外部 LB 如何把流量送到 Node,以及
externalTrafficPolicy、source IP、TLS termination。 - 完整 Service Discovery:CoreDNS plugin、搜尋網域、
ndots、SRV、Headless Service、StatefulSet hostname 與 DNS cache。 - 生產級 NetworkPolicy:不同 CNI 的 enforcement、觀測工具、policy rollout 與跨 namespace 的治理。
這些內容會分別放到後續 Part 7(Service Discovery)與 Part 8(L4/L7 與外部流量),避免在一篇文章裡把每個名詞都講半段。
本篇重點回顧
- 同一 Pod 的 containers 共享 network namespace,所以可以透過
localhost溝通,但也共享 IP 與 port 空間。 - Pod-to-Pod 直連使用 Pod IP;同節點通常經過 Node 內的介面與路由,跨節點則需要 CNI 與底層網路提供可達性。
- CNI 是網路插件介面,不代表所有叢集都使用相同的 bridge、overlay、VPC route 或 eBPF 實作。
- Service 的控制面透過 selector 維護 EndpointSlice;資料面再由 kube-proxy 或 CNI dataplane 將 ClusterIP 導向 ready endpoint。
- CoreDNS 處理 DNS lookup,不會代理後續的 HTTP/TCP 流量。
- NetworkPolicy 以 ingress/egress 與 Pod/namespace selector 表達限制;default deny egress 時,別忘了允許 DNS 的 UDP/TCP 53。
- 看到 DNS 成功不代表服務可達;Pod IP、ClusterIP 與 Service DNS 應該分層測試。
系列文章導覽
- Part 1:建立 K8s 心智模型 - 從 kubectl apply 到 Pod 啟動
- Part 2:Workload 實戰入門 - 從 Pod 到 Job
- Part 3:Pod 網路如何實際運作(本篇)
- Part 4:配置與儲存篇 - ConfigMap、Secret 與 Volume
- Part 5:進階篇 - 資源管理與自動擴展
- Part 6:實戰篇 - 部署完整微服務應用
- Part 7:服務發現實戰 - CoreDNS、Service 與 EndpointSlice
- Part 8:L4/L7 負載平衡實戰 - 從 ClusterIP 到 Gateway API
- Part 9:故障排除篇 - 從症狀到根因的實戰診斷
- Part 10:Production Baseline - 把測試叢集變成可運維環境
- Part 11:資源治理與排程 - Requests、QoS、PDB 與 Topology
- Part 12:Autoscaling 與容量規劃 - HPA、VPA 與 Node Autoscaler
- Part 13:Release Engineering - 安全發布、回滾與漸進式交付
- Part 14:Observability 實戰 - Metrics、Logs、Traces 與 SLO
- 進階網路:從 CNI、IPAM、Cloud VPC 到 Production 封包排錯
參考資源
- Cluster Networking - Kubernetes Documentation
- Network Plugins - Kubernetes Documentation
- Services - Kubernetes Documentation
- EndpointSlices - Kubernetes Documentation
- Virtual IPs and Service Proxies - Kubernetes Documentation
- DNS for Services and Pods - Kubernetes Documentation
- Network Policies - Kubernetes Documentation
- kind Quick Start
- netshoot - Kubernetes network troubleshooting container











