Mạng Linux & Container/Service & kube-proxy — traffic tới pod thế nào
18/21
Bài 18 / 21~14 phútKubernetes Networking (nhập môn)Miễn phí lượt xem

Service & kube-proxy — traffic tới pod thế nào

Trace client tới pod: ClusterIP VIP, Endpoints, kube-proxy iptables/IPVS, map DNAT M2, pitfall session affinity và stale endpoints.

TL;DR: Pod IP ephemeral — rolling update đổi IP, hardcode là tự bắn chân. Service cung cấp VIP ổn định (thường ClusterIP) + DNS; Endpoints / EndpointSlice liệt kê pod IP:port “còn sống”. kube-proxy (hoặc dataplane thay thế) rewrite gói VIP → pod IP:port — mode phổ biến iptables (nhiều rule DNAT/probability) hoặc IPVS (virtual server, scale tốt hơn). Map thẳng M2 publish DNAT: đổi đích, không “process listen trên ClusterIP”. Pitfall: sessionAffinity dính client; stale endpoint sau pod die; curl ClusterIP từ ngoài cluster (VIP không route WAN).

On-call UAT: junior paste curl http://10.96.42.17:8080 từ laptop VPN — timeout. Trong pod sidecar curl cùng URL lại 200. kubectl get svc thấy ClusterIP 10.96.42.17; kubectl get endpoints có pod 10.244.1.7:8080. Junior kết luận “Service hỏng / firewall VPN”. Bạn hỏi: “Laptop có route Service CIDR không? ClusterIP có process listen eth0 không?” Không — VIP chỉ “sống” trong đường kube-proxy trên node. Cùng class bẫy pod IP vs Service IP: hai dải, hai vai trò; debug phải đứng trong cluster (hoặc NodePort/LB/Ingress — bài 04).

Bài 01 chốt pod IP; 02 chốt path L3 tới pod. Bài này trace VIP → endpointmap DNAT đã học.

Bài faded: path ClusterIP worked; bạn tự điền map rule/chain khi đọc iptables dump, và tự chọn iptables vs IPVS trước khi đối chiếu.

1. Analogy — Số tổng đài ảo, lính gác chuyển hướng

Nhớ căn hộ = pod netnschuông ngoài toà = host publish DNAT:

  1. Cư dân đổi phòng liên tục (pod recreate) — số máy căn không đưa vào danh bạ khách hàng.
  2. Số tổng đài ảo (ClusterIP / Service DNS) cố định theo “dịch vụ payment-api”.
  3. Sổ Endpoints = danh sách căn đang sẵn sàng nhận cuộc gọi (ready pods).
  4. Lính gác (kube-proxy) trên mỗi tầng (node): nghe chuông VIP, đổi đích sang một căn thật — không ngồi trả lời thay app.
  5. Khách trong toà quay số tổng đài; khách ngoài đường cần cửa NodePort / LoadBalancer / Ingress (bài 04) — không giả số nội bộ route ra Internet.
Đời thườngKubernetes
Số máy căn (đổi khi dọn)Pod IP
Số tổng đài ảo cố địnhService ClusterIP / DNS name
Sổ “ai đang trực”Endpoints / EndpointSlice
Lính gác mỗi tầngkube-proxy (iptables/IPVS/…)
Đổi đích trên sổDNAT / IPVS tới pod:port
Chuông mặt tiền ngoài đườngNodePort / LB / Ingress (bài 04)
💡 Cách nhớ

Pod IP = endpoint thật. ClusterIP = VIP. Không ss -lntp thấy process bind ClusterIP — rewrite ở dataplane node.

2. Vì sao cần Service khi pod ephemeral?

2.1 Pod không phải identity ổn định

Từ model bài 01:

  • Rolling update / crash / scale → pod mới, IP mới (thường).
  • Client hardcode 10.244.x.y → sau deploy: timeout, partial outage, “đôi khi được”.
  • Scale 3 replica: client cần một tên ổn định + phân tải, không ba IP copy-paste.

Service (API v1/Service) giải:

  1. Ổn định identity: ClusterIP (và/hoặc DNS svc.namespace.svc.cluster.local) sống theo object Service, không theo một pod.
  2. Selector → backends: label selector (hoặc endpoint thủ công) gắn tập pod.
  3. Port abstraction: port (mặt Service) vs targetPort (port trong pod) — client không cần biết containerPort đổi tên.
flowchart LR
    CLI["Client pod"] --> DNS["kube-dns / CoreDNS"]
    DNS --> VIP["Service ClusterIP"]
    VIP --> KP["kube-proxy on node"]
    KP --> E1["Pod A IP"]
    KP --> E2["Pod B IP"]
    KP --> E3["Pod C IP"]

2.2 Ba object phải khớp khi debug

ObjectCâu hỏi
ServiceVIP / type / port / selector đúng chưa?
Endpoints / EndpointSliceCó backend? Ready? Đúng port?
PodListen targetPort? Ready probe pass? Network path CNI OK?

kubectl get svc,endpoints,pods -o wide — ba cột trước khi “restart kube-proxy mù”.

3. ClusterIP là VIP thế nào?

3.1 ClusterIP là gì?

  • Type mặc định của Service: ClusterIP.
  • IP lấy từ Service CIDR cluster (vd 10.96.0.0/12 — phụ thuộc cài đặt), khác Pod CIDR (10.244.0.0/16…).
  • Không gán lên eth0 pod hay node như secondary IP “thật” trong model cổ điển — là địa chỉ ảo dataplane hiểu.
  • Traffic trong cluster (pod → Service, node component theo config) dùng VIP; ngoài cluster mặc định không route Service CIDR.

3.2 Port mapping

# Illustrative — not a full deploy
apiVersion: v1
kind: Service
metadata:
  name: payment-api
spec:
  type: ClusterIP
  selector:
    app: payment
  ports:
    - name: http
      port: 80          # clients use :80 on ClusterIP / DNS
      targetPort: 8080  # pods listen 8080
      protocol: TCP
FieldAi nhìn
portClient gọi Service (ClusterIP:port / DNS:port)
targetPortPort trong pod (số hoặc tên port trên Pod spec)
nodePortChỉ khi type NodePort/LB — bài 04
⚠️ port vs targetPort vs containerPort

Client timeout dù pod ss thấy :8080: Service port: 80 nhưng targetPort nhầm 80 trong khi container chỉ listen 8080. Endpoints sẽ trỏ sai port → connection refuse/reset. Đọc Endpoints port, không chỉ YAML cảm tính.

3.3 DNS trong cluster

CoreDNS (hoặc tương đương) resolve:

  • payment-api (cùng namespace)
  • payment-api.default.svc.cluster.local (FQDN)

A record → ClusterIP (headless Service khác: trả pod IP — ngoài scope sâu bài này; chỉ cần biết pattern tồn tại).

4. Endpoints & EndpointSlice — sổ backend

4.1 Ai điền sổ?

Control plane (endpoint controller / endpointslice controller):

  1. Xem Service selector.
  2. Tìm Pod matching (thường) Ready theo readiness.
  3. Ghi địa chỉ podIP:targetPort vào Endpoints (legacy) và/hoặc EndpointSlice (scale tốt hơn, mặc định hiện đại).

4.2 Empty endpoints = VIP “ma”

Service object tồn tại, ClusterIP cấp, DNS resolve — nhưng 0 backend:

  • Selector label lệch Deployment.
  • Mọi pod NotReady (probe fail).
  • Sai namespace.

Triệu chứng: curl VIP hang/refuse tùy dataplane; kubectl get endpoints payment-api trống hoặc không có addresses.

kubectl get svc payment-api -o wide
kubectl get endpoints payment-api -o yaml
# Prefer slices on modern clusters:
kubectl get endpointslice -l kubernetes.io/service-name=payment-api
kubectl get pods -l app=payment -o wide

4.3 Headless & manual endpoints (biết tên, không đào sâu)

  • Headless (clusterIP: None): DNS trả pod IP — discovery trực tiếp, không VIP load-balance kiểu ClusterIP.
  • Không selector: Endpoints do người/controller khác điền (externalName-ish patterns, multi-cluster…) — debug “selector trống” khác “selector sai”.

5. kube-proxy rewrite VIP tới pod thế nào?

5.1 Vai trò

kube-proxy chạy (thường DaemonSet) trên mỗi node:

  • Watch Service + Endpoint(Slice).
  • Cài quy tắc local: “gói tới ClusterIP:port → chọn backend → đổi đích podIP:targetPort”.
  • Không terminate HTTP; không thay CNI path sau khi đã có pod IP đích.

Một số CNI/mesh (Cilium kube-proxy replacement, service mesh sidecar/iptables riêng…) thay hoặc bổ kube-proxy — mental model VIP→backend giữ; tool soi rule đổi (cilium, eBPF map…). Bài nhập môn bám iptables + IPVS vì còn map thẳng M1/M2.

5.2 Mode iptables (phổ biến lâu năm)

Tinh thần (chi tiết chain tên có thể KUBE-SERVICES, KUBE-SVC-*, KUBE-SEP-*):

  1. Gói dst = ClusterIP:port match rule Service.
  2. Statistic probability / chain per-backend chọn một SEP (service endpoint).
  3. DNATpodIP:targetPort.
  4. Routing/forward trên node → CNI path vào netns pod (bài 01–02).
  5. Conntrack giữ reverse path (như DNAT M2).
flowchart TB
    IN["Packet dst ClusterIP:80"] --> KS["KUBE-SERVICES"]
    KS --> SVC["KUBE-SVC-xxx"]
    SVC -->|"1/N probability"| SEP1["KUBE-SEP podA DNAT"]
    SVC -->|"1/N probability"| SEP2["KUBE-SEP podB DNAT"]
    SEP1 --> PODA["Forward to 10.244.1.7:8080"]
    SEP2 --> PODB["Forward to 10.244.1.9:8080"]

Scale note: mỗi Service × endpoint thêm rule — cluster rất nhiều Service+pod → iptables mode có thể chậm restore/lookup. Đó là động lực IPVS và eBPF replacement.

5.3 Mode IPVS

  • Kernel IPVS (L4 load balancer): VIP như virtual server, real server = pod endpoints.
  • Scheduler (rr, lc, …) thay dải probability iptables dài.
  • Vẫn L4, không hiểu Host HTTP header (Ingress L7 = bài 04).
  • Cần modules IPVS trên node; kube-proxy config mode: ipvs.
iptables modeIPVS mode
Cơ chếnetfilter rules + DNATIPVS virtual server
Scale nhiều ServiceRule bùng nổThường tốt hơn
Session affinityRule/options K8sIPVS persistence tương ứng
Debug entryiptables-save / iptables -t nat -Lipvsadm -Ln
Map M2Rất gần DNAT publish“LB kernel” + vẫn DNAT/masq cạnh

5.4 Map DNAT Module 2 (bắt buộc nối)

M2 docker -p / lab DNATService ClusterIP
Client gõ host:hostPortClient gõ ClusterIP:port (hoặc DNS)
DNAT đổi destination → container IP:portDNAT/IPVS đổi destinationpod IP:targetPort
Rule trên host netnsRule trên mỗi node (kube-proxy)
Một publish cố địnhN endpoint, LB giữa pod Ready
Conntrack reverseConntrack / IPVS connection table
Không process listen publish IP “ảo” thuầnKhông process listen ClusterIP

Khác quan trọng: Docker publish thường north-south vào một host. ClusterIP là east-west identity trong cluster; cửa ngoài là type khác + Ingress.

# On a node (lab kind/k3s — privileges vary)
# iptables mode sketch:
sudo iptables -t nat -L KUBE-SERVICES -n 2>/dev/null | head
# IPVS mode sketch:
sudo ipvsadm -Ln 2>/dev/null | head

6. Trace path và pitfall — client tới pod ra sao?

6.1 Worked path (cùng cluster)

Giả sử:

  • Client pod curl http://payment-api.default.svc:80/health
  • Service ClusterIP 10.96.42.17, port 80 → targetPort 8080
  • Endpoints: 10.244.1.7:8080, 10.244.1.9:8080
  • kube-proxy iptables mode

Bước:

  1. DNS10.96.42.17.
  2. Client netns gửi TCP dst=10.96.42.17:80, src=client pod IP.
  3. Gói ra veth → node (CNI). Trên node (hoặc netns path tùy CNI), gói vào netfilter — match KUBE-SERVICES.
  4. Chọn backend (vd 10.244.1.7:8080) — DNAT đổi dst.
  5. Route tới pod IP (cùng/khác node) — CNI như bài 01–02; không NAT pod-to-pod model sau khi đích đã là pod IP.
  6. App trong netns pod accept :8080.
  7. Reply: conntrack un-DNAT — client vẫn thấy peer là VIP:80, không tự “biết” đã nói chuyện pod IP (L4 view).

6.2 Faded — tự điền map rule

Tự điền trước khi xem đáp án

Bạn iptables-save -t nat (lab) thấy comment/service name payment-api và hai rule DNAT tới 10.244.1.7:808010.244.1.9:8080.
(1) Field L3/L4 nào bị đổi — source hay destination?
(2) Chain/hook gần với bài M2 publish hơn: PREROUTING-ish hay POSTROUTING SNAT?
(3) Nếu Endpoints còn mỗi 10.244.1.7 sau khi pod B NotReady, rule DNAT pod B nên còn hay biến mất — vì sao?

Đáp án kỳ vọng:

  1. Destination (DNAT) — src thường giữ pod client (east-west); đừng nhầm SNAT egress M2.
  2. Đổi đích sớm trên path vào (họ PREROUTING/OUTPUT tùy local vs forward) — cùng tinh thần publish DNAT, không MASQUERADE POSTROUTING “ra Internet”.
  3. Biến mất / không chọn backend NotReady — controller cập nhật EndpointSlice → kube-proxy reconcile bỏ SEP chết; để stale = traffic đen.

6.3 Faded — chọn mode

Tự chọn

Cluster ~5 Service lab demo: iptables hay IPVS đủ? Cluster hàng nghìn Service, restore iptables hàng phút: nghi mode nào / hướng nào trước khi “mua mesh”?

Đáp án kỳ vọng: Lab nhỏ — iptables đủ, dễ soi. Scale rule nổ / latency proxy sync — xem IPVS hoặc kube-proxy replacement eBPF; đo trước khi đổ lỗi app.

6.4 Pitfall — session affinity, stale endpoint, nhầm lớp

sessionAffinity: ClientIP

spec:
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800
  • Cùng client IP dính một backend một cửa sổ thời gian.
  • Bất ngờ: scale/rolling thấy “mọi user dồn 1 pod”; debug cache “chỉ user A lỗi” vì sticky.
  • Stateful cần sticky thật → thường app-level / Ingress cookie / mesh — đừng mặc định bật ClientIP “cho chắc”.

Stale / rỗng endpoints

Triệu chứngHướng
VIP OK, 50% request failMột endpoint pod chết nhưng slice chậm / probe sai
100% fail, DNS resolve0 ready endpoint
Pod Running nhưng endpoint thiếuReadiness fail; label selector
Vừa xoá pod, vài giây lỗiCửa sổ propagate EndpointSlice + conntrack cũ

Curl ClusterIP từ ngoài

Laptop/VPN không có route Service CIDR → timeout. Không chứng minh Service hỏng. Test từ pod debug / kubectl run curl --rm -it --image=curlimages/curl.

CNI hỏng vs Service hỏng

  • Pod-to-pod IP fail → CNI/underlay (bài 02), Service rewrite đúng cũng bó tay.
  • Pod-to-pod IP OK, VIP fail → Service/endpoints/kube-proxy.
  • Tách hai class trước khi restart cả cụm.
flowchart TD
    Q["Traffic to Service fails"] --> A{"Pod IP to pod IP OK?"}
    A -->|No| CNI["Debug CNI / route / MTU"]
    A -->|Yes| B{"Endpoints non-empty Ready?"}
    B -->|No| EP["Selector probe labels"]
    B -->|Yes| C{"kube-proxy / dataplane rules?"}
    C --> KP["iptables IPVS or replacement"]

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

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

Spec / docs:

Nội bộ OLHub:

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

Tóm tắt

  • Service ổn định discovery khi pod IP ephemeral; ClusterIP là VIP, không eth0 app.
  • Endpoints/EndpointSlice = backend Ready; trống = traffic chết dù DNS xanh.
  • kube-proxy iptables (DNAT + probability) hoặc IPVS; rewrite destination → pod:targetPort.
  • Map M2: cùng họ DNAT publish; scope cluster + multi-backend.
  • Pitfall: affinity sticky, stale endpoint, curl VIP ngoài cluster, lẫn port Service/container.

7. Tự kiểm tra

Tự kiểm tra
Q1
Vì sao hardcode pod IP vào config client là anti-pattern với Deployment stateless? Service giải đúng pain nào (identity vs load vs port)?
Pod recreate/rolling đổi IP; client pin IP cũ fail. Service cho identity ổn định (ClusterIP/DNS), tập backend theo selector (LB giữa Ready pods), và map port→targetPort. Không chỉ 'một DNS record đẹp' — còn endpoint lifecycle.
Q2
ClusterIP có process listen trên node/pod không? ss -lntp trên node thấy gì khác với Endpoints? Debug curl VIP timeout từ laptop hướng nào trước?
Không process listen ClusterIP — VIP dataplane. ss có thể thấy kube-proxy/healthz, không phải app bind VIP. Endpoints liệt kê podIP:port thật. Timeout từ laptop: nghi không route Service CIDR / test từ trong cluster trước khi kết luận Service hỏng.
Q3
Trace: client pod → DNS → ClusterIP → kube-proxy iptables → pod. Bước nào đổi destination? Bước nào là CNI path đã học ở bài 01–02?
DNAT (hoặc IPVS) ở kube-proxy/dataplane đổi destination VIP:port → podIP:targetPort. Sau đó forward/route tới pod IP qua veth/bridge/overlay CNI — cùng path pod-to-pod model, không còn 'gói chỉ tới VIP'.
Q4
Map bảng M2 docker -p với ClusterIP: field đổi (src/dst), 'publish address' tương ứng object nào, multi-backend khác publish đơn thế nào?
Cả hai đổi destination (DNAT). Publish address host:hostPort ↔ ClusterIP:port. Docker publish thường 1 container; Service LB N pod Ready, rule/IPVS cập nhật theo EndpointSlice. Conntrack reverse giống tinh thần.
Q5
So sánh iptables mode và IPVS mode kube-proxy: cơ chế, khi lab nhỏ vs cluster nhiều Service, lệnh survey nhanh mỗi mode?
iptables: chains + probability DNAT, dễ nổ rule. IPVS: virtual server kernel, scale tốt hơn. Lab nhỏ iptables đủ; nhiều Service đo sync/latency → IPVS hoặc replacement. Survey: iptables-save/-L KUBE-*; ipvsadm -Ln.
Q6
sessionAffinity ClientIP gây class triệu chứng gì khi rolling update? Khi nào nên tránh mặc định bật?
Client dính một pod — lệch tải, user A luôn hit pod lỗi trong khi user B OK, cửa sổ timeout affinity. Tránh bật 'cho chắc' với app stateless; sticky thật thường cookie/L7 hoặc session store ngoài.
Q7
kubectl get svc có ClusterIP, DNS resolve, nhưng curl từ pod debug fail. Checklist thứ tự: Endpoints, readiness, kube-proxy, CNI — vì sao Endpoints trước 'restart node'?
(1) Endpoints/EndpointSlice non-empty đúng port. (2) Pod Ready/listen targetPort. (3) Rules kube-proxy/dataplane. (4) Nếu podIP↔podIP cũng fail mới CNI. Endpoints trống = control-plane/selector/probe — restart node không sửa label sai.

Bài tiếp theo: Ingress & load balancing — L4 vs L7 vào cluster

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

Ingress & load balancing — L4 vs L7 vào cluster