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 → endpoint và map 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 netns và chuông ngoài toà = host publish DNAT:
- 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.
- Số tổng đài ảo (ClusterIP / Service DNS) cố định theo “dịch vụ payment-api”.
- Sổ Endpoints = danh sách căn đang sẵn sàng nhận cuộc gọi (ready pods).
- 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.
- 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ường | Kubernetes |
|---|---|
| Số máy căn (đổi khi dọn) | Pod IP |
| Số tổng đài ảo cố định | Service ClusterIP / DNS name |
| Sổ “ai đang trực” | Endpoints / EndpointSlice |
| Lính gác mỗi tầng | kube-proxy (iptables/IPVS/…) |
| Đổi đích trên sổ | DNAT / IPVS tới pod:port |
| Chuông mặt tiền ngoài đường | NodePort / LB / Ingress (bài 04) |
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:
- Ổn định identity: ClusterIP (và/hoặc DNS
svc.namespace.svc.cluster.local) sống theo object Service, không theo một pod. - Selector → backends: label selector (hoặc endpoint thủ công) gắn tập pod.
- Port abstraction:
port(mặt Service) vstargetPort(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
| Object | Câu hỏi |
|---|---|
| Service | VIP / type / port / selector đúng chưa? |
| Endpoints / EndpointSlice | Có backend? Ready? Đúng port? |
| Pod | Listen 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
eth0pod 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
| Field | Ai nhìn |
|---|---|
port | Client gọi Service (ClusterIP:port / DNS:port) |
targetPort | Port trong pod (số hoặc tên port trên Pod spec) |
nodePort | Chỉ khi type NodePort/LB — bài 04 |
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):
- Xem Service
selector. - Tìm Pod matching và (thường) Ready theo readiness.
- Ghi địa chỉ
podIP:targetPortvà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-*):
- Gói dst = ClusterIP:port match rule Service.
- Statistic probability / chain per-backend chọn một SEP (service endpoint).
- DNAT →
podIP:targetPort. - Routing/forward trên node → CNI path vào netns pod (bài 01–02).
- 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 mode | IPVS mode | |
|---|---|---|
| Cơ chế | netfilter rules + DNAT | IPVS virtual server |
| Scale nhiều Service | Rule bùng nổ | Thường tốt hơn |
| Session affinity | Rule/options K8s | IPVS persistence tương ứng |
| Debug entry | iptables-save / iptables -t nat -L | ipvsadm -Ln |
| Map M2 | Rấ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 DNAT | Service ClusterIP |
|---|---|
| Client gõ host:hostPort | Client gõ ClusterIP:port (hoặc DNS) |
| DNAT đổi destination → container IP:port | DNAT/IPVS đổi destination → pod IP:targetPort |
| Rule trên host netns | Rule trên mỗi node (kube-proxy) |
| Một publish cố định | N endpoint, LB giữa pod Ready |
| Conntrack reverse | Conntrack / IPVS connection table |
| Không process listen publish IP “ảo” thuần | Khô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:
- DNS →
10.96.42.17. - Client netns gửi TCP dst=10.96.42.17:80, src=client pod IP.
- Gói ra veth → node (CNI). Trên node (hoặc netns path tùy CNI), gói vào netfilter — match KUBE-SERVICES.
- Chọn backend (vd
10.244.1.7:8080) — DNAT đổi dst. - 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.
- App trong netns pod accept
:8080. - 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
Bạn iptables-save -t nat (lab) thấy comment/service name payment-api và hai rule DNAT tới 10.244.1.7:8080 và 10.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:
- Destination (DNAT) — src thường giữ pod client (east-west); đừng nhầm SNAT egress M2.
- Đổ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”.
- 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
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ứng | Hướng |
|---|---|
| VIP OK, 50% request fail | Một endpoint pod chết nhưng slice chậm / probe sai |
| 100% fail, DNS resolve | 0 ready endpoint |
| Pod Running nhưng endpoint thiếu | Readiness fail; label selector |
| Vừa xoá pod, vài giây lỗi | Cử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
Spec / docs:
- Service — types, virtual IP, proxy modes, session affinity.
- Services, load balancing, networking — chỗ Service trong model.
- EndpointSlices — backend scale-out API.
- kube-proxy — flags mode iptables/ipvs.
Nội bộ OLHub:
- 01 — Pod networking — pod IP ≠ ClusterIP.
- M2 — Publish DNAT — map đổi đích.
- M1 — iptables/netfilter — table nat, chain, conntrack.
- 04 — Ingress & LB — đưa traffic từ ngoài vào Service.
Liên hệ các bài khác
- 01 — Pod networking — endpoint thật; shared netns listen port.
- 02 — CNI — sau DNAT, gói vẫn cần path L3 tới pod (cùng/khác node).
- 04 — Ingress & load balancing — ClusterIP vs NodePort vs LB vs Ingress L7.
- 05 — NetworkPolicy — policy theo pod IP path; hiểu VIP≠pod khi viết rule.
- M2 — DNAT publish — isomorphism DNAT.
- 00 — Tổng quan M3 — chỗ Service trên pipeline capstone.
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
Q1Vì 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)?▸
Q2ClusterIP 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?▸
Q3Trace: 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?▸
Q4Map 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?▸
Q5So 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?▸
Q6sessionAffinity ClientIP gây class triệu chứng gì khi rolling update? Khi nào nên tránh mặc định bật?▸
Q7kubectl 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'?▸
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
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