Mạng Linux & Container/Pod networking — mỗi pod một IP, mô hình phẳng
16/21
Bài 16 / 21~16 phútKubernetes Networking (nhập môn)Miễn phí lượt xem

Pod networking — mỗi pod một IP, mô hình phẳng

Pod network model: mọi pod thấy nhau không NAT, container trong pod chung netns, pause container, liên hệ veth/bridge đã học.

TL;DR: Kubernetes gán mỗi pod một IP cluster-wide và coi pod như “máy logic” trên mạng phẳng: mọi pod nói chuyện với mọi pod (cùng node hay khác node) không NAT theo network model — trừ khi bạn cố ý phân đoạn bằng NetworkPolicy. Các container trong cùng pod chia sẻ một network namespace (IP, port space, localhost). Runtime thường giữ netns bằng pause/infra container “ngủ” trong khi app container join cùng ns. Dưới hood: CNI (bài sau) vẫn dựng veth + bridge hoặc route như Module 1–2 — Kubernetes không invent TCP stack mới. Pitfall số một: nhầm pod IP (ephemeral, gắn workload instance) với Service IP (VIP ổn định, kube-proxy rewrite — bài 03).

Code review: junior dán log curl 10.96.0.1:443 từ laptop “thử ClusterIP”, timeout; đồng thời bảo “pod IP 10.244.1.7 thì ping OK trong cluster, vậy Service hỏng”. Bạn hỏi: “10.96.0.1 có process listen không?” Không — đó là VIP. kubectl get pod -o wide thấy pod IP; kubectl get svc thấy ClusterIP. Hai dải, hai vai trò. Junior đang debug Service bằng thói quen “một IP = một container eth0” từ Docker. Contract K8s khác: pod IP là endpoint thật; Service IP là địa chỉ ảo được DNAT/IPVS.

Module 2 đã cho bạn netns + veth + bridge trên một host. Bài này nâng contract lên cluster: mô hình phẳng pod-to-pod, shared netns trong pod, pause container, và ranh giới với Service IP — trước khi CNI gắn mạng.

1. Analogy — Mỗi căn hộ một số máy; chung tủ điện trong căn

Nhớ M2 — Network namespaces: mỗi netns là một căn hộ với tủ mạng riêng. Kubernetes đổi đơn vị deploy:

  1. Một pod = một căn hộ (một netns mạng), không phải “mỗi container một căn” như Docker bridge mode cổ điển.
  2. Số máy căn = pod IP — unique trong cluster, gán khi pod schedule.
  3. Nhiều người trong cùng căn (app + sidecar + init) dùng cùng tủ điện — cùng IP, tranh port với nhau, gọi nhau bằng localhost.
  4. Toà nhà nhiều tầng (nhiều node): model K8s hứa mọi căn gọi được mọi căn bằng số máy thật, không phải “gọi qua số tổng đài NAT” giữa pod (NAT nằm ở cửa Service/egress — chỗ khác).
  5. Người gác cửa VIP (Service IP) là số ảo: chuông kêu, lính gác (kube-proxy) chuyển hướng tới căn thật — đừng tưởng VIP là eth0 của ai đó.
Đời thườngKubernetes
Căn hộPod (một network namespace)
Số máy cănPod IP (pod.status.podIP)
Người trong cănContainers (app, sidecar, init…)
Chuông trong cănlocalhost / 127.0.0.1 giữa container cùng pod
Đường nối các căn (toà + giữa toà)Pod network / CNI
Số tổng đài ảoService ClusterIP (bài 03)
💡 Cách nhớ

Pod = netns + IP. Container trong pod không có eth0 riêng trong model chuẩn — chúng share. Docker “mỗi container một IP trên bridge” ≠ default pod model.

2. Network model Kubernetes — ba lời hứa

Tài liệu chính thức gom networking thành vài bài toán: container-to-container trong pod, pod-to-pod, pod-to-Service, external-to-Service. Bài này khóa pod model (hai mảnh đầu).

2.1 Mỗi pod một IP cluster-wide

  • Scheduler đặt pod lên node → runtime + CNI gán IP từ dải pod (CIDR do cluster/plugin cấu hình).
  • IP xuất hiện ở pod.status.podIP / podIPs (dual-stack nếu bật).
  • IP theo lifecycle pod: pod chết/recreate → IP mới (trừ pattern đặc biệt như một số StatefulSet + policy — đừng giả định IP cố định cho app stateless).

2.2 Pod-to-pod: flat, không NAT (model)

Theo Kubernetes network model:

  • Mọi pod có thể nói chuyện với mọi pod khác (cùng node hoặc khác node).
  • Giao tiếp trực tiếp, không dùng proxy hay address translation (NAT) giữa pod IP nguồn và pod IP đích — barring intentional network segmentation (NetworkPolicy, mesh, v.v.).
  • Agent trên node (kubelet, daemon) nói chuyện được với pod trên cùng node.

Vì sao thiết kế vậy? Tránh “dynamic host port map” kiểu Docker publish cho mọi hop nội bộ: app bind port cố định trong pod; service discovery và config dùng IP/DNS ổn định hơn ở tầng Service, không ép mọi binary đọc “host port ngẫu nhiên”.

flowchart LR
    subgraph N1["Node A"]
      P1["Pod 10.244.1.7"]
      P2["Pod 10.244.1.9"]
    end
    subgraph N2["Node B"]
      P3["Pod 10.244.2.4"]
    end
    P1 -->|"L3 to pod IP no NAT"| P2
    P1 -->|"L3 to pod IP no NAT"| P3
    P2 -->|"L3 to pod IP no NAT"| P3

Model ≠ “firewall luôn mở.” Default-allow full mesh cho đến khi NetworkPolicy (bài 05) hoặc plugin enforce segmentation. “Không NAT” mô tả địa chỉ (src/dst pod IP end-to-end trong path pod-to-pod), không mô tả “không có filter”.

2.3 Shared network namespace trong pod

Mỗi pod có private network namespace dùng chung bởi mọi container trong pod:

  • Cùng IP (các address family được gán).
  • Cùng port space — hai container không bind trùng :8080.
  • Trong pod, container gọi nhau qua localhost.
  • Hostname hệ thống trong pod thường khớp tên pod.

Đây là lời giải cho “highly-coupled container-to-container”: sidecar proxy, log shipper, auth helper… cùng logical host.

📌 Không nhầm multi-container với multi-pod

Scale out = nhiều pod (Deployment replicas), mỗi pod IP riêng. Sidecar = thêm container trong cùng pod, share netns. Nhầm hai pattern → port conflict hoặc “localhost không thấy service kia” (service kia đang ở pod khác).

3. Pause / infra container — ai giữ netns?

Trên Linux, network namespace gắn với process còn sống (và mount ns file). Runtime (containerd/CRI-O + kubelet) cần một process giữ netns của pod trong suốt đời pod, kể cả khi app container restart.

Pause container (hay gọi infra / sandbox container tùy runtime):

  1. Image tối giản (pause / tương đương): process “ngủ” (pause() / sleep vòng), không chạy business logic.
  2. Runtime tạo sandbox (netns + thường IPC/UTS share theo Pod) gắn process pause.
  3. App / init / sidecar container join cùng network namespace của sandbox.
  4. Pod delete → sandbox teardown → netns + veth/CNI resources được dọn (CNI DEL — bài 02).
flowchart TB
    subgraph POD["Pod sandbox = one netns"]
      PAUSE["pause / infra process"]
      APP["app container"]
      SIDE["sidecar container"]
      ETH["eth0 + lo in netns"]
      PAUSE --- ETH
      APP --- ETH
      SIDE --- ETH
    end
    VETH["veth peer on host"] --- ETH

Bạn hiếm khi kubectl exec vào pause — nó không phải workload. kubectl exec -c <app> chạy trong cùng netns: ip addr, ss -lntp thấy bind của mọi container trong pod (cùng view mạng).

Quan sátÝ nghĩa
kubectl get pod -o wide → một IPIP của pod netns, không “IP từng container”
Hai container cùng pod, một bind :80Cái kia không bind :80 được
curl localhost:8080 từ sidecarĐến process app cùng pod nếu app listen 8080
App container restartNetns/IP thường giữ (sandbox còn); connection cũ drop
Thử đoán

Pod hai container: app listen 0.0.0.0:8080, sidecar curl -sS 127.0.0.1:8080. Cùng node, pod khác cũng có app listen 8080. Sidecar chạm nhầm pod kia không? Viết dự đoán — rồi đối chiếu mục 2.3.

Đáp án kỳ vọng: Không. 127.0.0.1 chỉ trong netns pod hiện tại. Pod kia có IP riêng; muốn gọi phải dùng pod IP (hoặc Service), không phải localhost.

4. Map về veth / bridge / route (recall M1–M2)

Kubernetes không thay softirq / sk_buff / netfilter. Trên một node, path quen thuộc Module 2:

4.1 Cùng node (điển hình bridge hoặc equivalent)

  1. Process app trong pod netns gửi gói từ eth0 (thường là đầu veth trong ns).
  2. Peer veth nằm host/init netns (hoặc gắn bridge cni0 / flannel.* / OVS / … tùy plugin).
  3. L2/L3 trên node: bridge learning, hoặc route tới subnet pod local.
  4. Đích cùng node: gói vào veth peer của pod B → netns B.

Đây là nối netns bằng veth + bridge from scratch — chỉ ai gọi ip link đổi: Docker daemon vs CNI plugin do kubelet/runtime kích.

4.2 Khác node

Model vẫn: src = pod IP A, dst = pod IP B, không NAT đổi thành node IP giữa chừng (trong path chuẩn pod-to-pod).

Cách thực hiện do CNI:

  • Routed: route/BGP quảng bá “subnet pod node X qua node X”; gói L3 thuần qua underlay.
  • Overlay (vd VXLAN): gói pod bọc outer header giữa node; bên trong vẫn giữ pod IP — encapsulation ≠ NAT rewrite pod address.

Chi tiết trade-off: bài 02 — CNI.

flowchart TB
    subgraph SRC["Pod A netns"]
      AETH["eth0 veth"]
    end
    subgraph HOSTA["Node A host netns"]
      AV["veth peer"]
      BR["bridge or route/CNI"]
    end
    subgraph HOSTB["Node B host netns"]
      BR2["bridge or route/CNI"]
      BV["veth peer"]
    end
    subgraph DST["Pod B netns"]
      BETH["eth0 veth"]
    end
    AETH --> AV --> BR
    BR -->|"same node L2/L3 or underlay/overlay"| BR2
    BR2 --> BV --> BETH

4.3 So với Docker bridge “cổ điển”

Docker bridge (lab M2)Kubernetes pod model
Đơn vị IPThường mỗi container một IP trên bridgeMỗi pod một IP
Multi-processNhiều container = nhiều netns (trừ network_mode: container:…)Nhiều container một netns
Ra InternetHost SNAT/MASQUERADE (M2 bài 04)Tương tự ở node; policy/CNI có thể siết
Publish portDNAT host port (M2 bài 05)Service/NodePort/Ingress — không map từng pod thủ công làm default
Cross-hostKhông “free”; cần overlay/user-definedContract cluster: pod network xuyên node

5. Worked walkthrough — đọc IP và netns (optional cluster)

Không bắt buộc cluster để hiểu model; nếu có kind / minikube:

# 1) See pod IPs (cluster-wide addresses on the pod network)
kubectl get pods -A -o wide

# 2) Pick a workload pod name/namespace from the table, then:
kubectl exec -n <ns> <pod> -- ip -br addr
kubectl exec -n <ns> <pod> -- ip route

# 3) Same view from another container in a multi-container pod (if any):
# kubectl exec -n <ns> <pod> -c <other> -- ip -br addr
# Expect the same pod IP on eth0.

# 4) Contrast Service VIP (no listener inside a random pod for that IP):
kubectl get svc -A
# Note ClusterIP range vs pod CIDR — different roles.

Kỳ vọng:

LệnhThấy gì
get pods -o wideCột IP = pod IP trên node đã schedule
ip -br addr trong podlo + eth0 (tên có thể khác) mang pod IP
Hai container cùng podCùng địa chỉ trên iface pod
get svc ClusterIPDải khác pod CIDR; không phải eth0 pod

Trên node (lab advanced, cần shell node):

# Illustrative only — names vary by CNI
ip -br link | head
bridge link 2>/dev/null || true
# Often: vethXXXX@ifN pairs; one end in pod netns (not visible here without nsenter)

Map debug Module 2: nsenter / tcpdump đúng netns — khi “pod không ping pod”, vào đúng ns hoặc kubectl exec, đừng chỉ tcpdump eth0 host rồi kết luận app chết.

6. Vì sao nhầm pod IP với Service IP?

Pod IPService ClusterIP
Là gì?Địa chỉ thật của netns podVIP ảo trong dải service CIDR
Ai “listen”?Process trong pod bind portKhông có process listen VIP; kube-proxy/dataplane rewrite
Ổn định?Đổi khi pod recreateỔn định theo object Service (trong đời Service)
Dùng khi debug app bindkubectl exec + ss trong podget endpointslices / endpoints — backend pod IP:port
Client ngoài clusterThường không route thẳng (tuỳ network)Cũng không — cần NodePort/LB/Ingress

Triệu chứng nhầm:

  1. Hardcode pod IP trong config microservice → sau deploy, 50% call fail (pod mới IP mới).
  2. curl <ClusterIP> từ máy laptop không có route/kube-proxy → timeout → “K8s hỏng” trong khi trong-cluster DNS Service vẫn ổn.
  3. NetworkPolicy chọn nhầm: filter theo pod label/IP path, không “magically” hiểu tên Service nếu rule viết sai layer (chi tiết bài 05).
flowchart LR
    C["Client in cluster"] --> VIP["Service ClusterIP VIP"]
    VIP -->|"kube-proxy DNAT/IPVS"| PIP["Pod IP:targetPort"]
    PIP --> APP["app in pod netns"]

Bài 03 — Service & kube-proxy mổ rewrite; ở đây chỉ cần tách khái niệm: model phẳng gắn pod IP; ổn định discovery gắn Service.

⚠️ hostNetwork: true

Pod hostNetwork: true share netns host — không còn “một IP pod riêng” theo model chuẩn; port bind thẳng host, collision với daemon node. Dùng có chủ đích (một số agent); đừng lấy lab hostNetwork làm hình mẫu app thường.

📚 Deep Dive — tài liệu gốc

📚 Deep Dive — tài liệu gốc

Spec / docs:

Nội bộ OLHub (plumbing):

Liên hệ các bài khác

Tóm tắt

  • Pod networking model: mỗi pod một IP; pod-to-pod L3 trực tiếp không NAT (model); segmentation là lớp thêm.
  • Shared netns: mọi container trong pod chung IP/port/localhost; scale = nhiều pod, không phải nhiều IP trong một pod.
  • Pause/infra: giữ sandbox/netns; app join ns — IP gắn pod, không gắn “từng container image”.
  • Plumbing: veth + bridge/route (cùng node) và CNI path (khác node) — compose M1–M2, không stack user-space thay kernel.
  • Pitfall: pod IP ephemeral endpoint; Service ClusterIP là VIP — đừng curl nhầm dual và đừng hardcode pod IP.

7. Tự kiểm tra

Tự kiểm tra
Q1
Nêu ba đặc trưng cốt lõi của Kubernetes pod network model (IP, pod-to-pod, netns trong pod). Cái nào là 'model' chứ không phải 'mọi cluster luôn full-mesh không filter'?
(1) Mỗi pod một IP cluster-wide. (2) Pod-to-pod giao tiếp trực tiếp, không NAT giữa pod IP (model). (3) Container trong pod share một network namespace (IP/port/localhost). Full-mesh không filter là default khi chưa NetworkPolicy/enforce — segmentation có chủ đích vẫn nằm trong hệ; 'no NAT' nói về địa chỉ end-to-end, không cấm firewall.
Q2
Hai container trong cùng pod: một sidecar gọi http://127.0.0.1:8080, app listen 8080. Vì sao được? Nếu tách app sang pod khác trên cùng node, localhost còn đúng không — phải dùng gì?
Cùng pod = cùng netns = cùng lo và cùng port space; 127.0.0.1 tới process listen trong ns đó. Pod khác = netns khác; localhost của sidecar không thấy app. Phải gọi pod IP (hoặc Service DNS/ClusterIP), kể cả khi hai pod cùng node.
Q3
Pause/infra container đóng vai trò gì với network namespace? App container restart có nhất thiết đổi pod IP không (model thông thường)?
Pause/infra giữ sandbox/netns (và thường identity mạng của pod) sống suốt đời pod. App container join ns đó; restart app thường giữ netns/IP vì sandbox còn. Pod bị xoá/tạo lại mới teardown ns và (thường) nhận IP mới từ CNI.
Q4
Trace mental model: gói từ pod A tới pod B cùng node. Liệt kê object Linux lần lượt (netns/veth/bridge-or-route) — map về lab M2.
App trong netns A → eth0 (đầu veth trong ns) → peer veth host → bridge CNI hoặc route local trên node → peer veth pod B → eth0 trong netns B → stack B. Giống pipeline veth+bridge M2; runtime/CNI script thay Docker daemon.
Q5
Phân biệt pod IP và Service ClusterIP theo: có process listen không, ổn định ra sao, dùng khi debug bind app thế nào?
Pod IP: địa chỉ thật netns, process trong pod listen port trên IP đó; đổi khi pod recreate. ClusterIP: VIP, không process listen; kube-proxy/dataplane DNAT/IPVS tới endpoint pod IP:port; ổn định theo Service. Debug bind: exec vào pod xem ss/ip; debug Service: endpoints/EndpointSlice + kube-proxy, không giả VIP là eth0.
Q6
Junior hardcode 10.244.3.15 (pod IP lúc demo) vào config service khác. Sau rolling update, triệu chứng là gì và fix đúng hướng (không phải 'tăng timeout')?
Pod mới IP mới; client vẫn gọi IP cũ → connection fail/timeout một phần hoặc toàn bộ tuỳ replica còn sót. Fix: dùng Service DNS/ClusterIP (hoặc discovery API), không pin pod IP cho workload stateless. Timeout dài chỉ che race, không sửa identity sai.
Q7
Vì sao model 'no NAT pod-to-pod' vẫn tương thích với việc node SNAT khi pod ra Internet (egress)? Hai chiều traffic khác nhau chỗ nào?
No-NAT áp cho path giữa các pod trong cluster model (src/dst là pod IP). Egress ra ngoài cluster thường cần node SNAT/MASQUERADE (giống M2) vì Internet không route pod CIDR RFC1918. Khác chiều: east-west pod giữ địa chỉ; north-south ra WAN đổi source tại biên node/NAT gateway.

Bài tiếp theo: CNI là gì — interface gắn mạng cho pod

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên

Bài tiếp theo

CNI là gì — interface gắn mạng cho pod