Sau bài này bạn làm được: chọn đúng Service type cho từng tình huống; debug “traffic không tới pod” bằng kiểm tra endpoints; hiểu kube-proxy làm gì ở tầng kernel.
Vấn đề: Pod IP không ổn định
Pod restart → IP mới. Scale up/down → IP thay đổi. Bạn không thể hardcode IP của pod vào config.
Service giải quyết bằng:
- VIP (ClusterIP): IP ảo ổn định không đổi suốt vòng đời Service.
- DNS:
<service>.<namespace>.svc.cluster.local. - Load balancing: forward tới nhiều pod backend.
Client pod
│
▼
Service (ClusterIP: 10.96.0.15)
│ kube-proxy / eBPF forward
├──► Pod 1 (10.244.0.5:8080)
├──► Pod 2 (10.244.1.3:8080)
└──► Pod 3 (10.244.2.7:8080)
Service spec cơ bản
apiVersion: v1
kind: Service
metadata:
name: my-api
namespace: default
spec:
type: ClusterIP # Mặc định
selector:
app: my-api # Chọn pod có label app=my-api
ports:
- name: http
port: 80 # Port của Service
targetPort: 8080 # Port container đang listen
protocol: TCP
selector → Endpoints
Service selector chọn pod → Kubernetes tự tạo Endpoints object chứa danh sách pod IP:
kubectl get endpoints my-api
# NAME ENDPOINTS AGE
# my-api 10.244.0.5:8080,10.244.1.3:8080,10.244.2.7:8080 5m
Endpoints trống = traffic không đi đâu. Nguyên nhân: label selector không match pod, pod không Ready.
# Debug: kiểm tra selector match
kubectl get pods -l app=my-api
kubectl get endpoints my-api
# Nếu endpoints trống:
# 1. Pod có label đúng không? kubectl get pods --show-labels
# 2. Pod có Ready không? kubectl get pods (READY 1/1?)
# 3. readinessProbe fail? kubectl describe pod <name>
Các loại Service
ClusterIP (mặc định)
- Chỉ truy cập trong cluster.
- Dùng cho: service-to-service communication (API gọi DB, frontend gọi backend).
NodePort
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # Mặc định: random trong 30000-32767
- Mở port trên tất cả node (NodeIP:NodePort).
- Traffic:
NodeIP:30080 → Service → Pod. - Dùng cho: test nhanh, bare-metal không có cloud LB.
- Không dùng production trên cloud — có LoadBalancer type.
LoadBalancer
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
- Cloud controller tạo external load balancer (AWS ELB/NLB, GCP LB, Azure LB).
- Được gán external IP.
- Traffic:
External IP:80 → LB → NodePort → Service → Pod. - Dùng cho: expose service ra internet (hoặc qua Ingress).
ExternalName
spec:
type: ExternalName
externalName: db.external.example.com
- Không tạo proxy/VIP. Chỉ trả CNAME record trong DNS.
- Dùng cho: alias service bên ngoài cluster bằng tên K8s.
Headless Service
spec:
clusterIP: None # ← Headless
selector:
app: my-db
ports:
- port: 5432
- Không có VIP. DNS trả trực tiếp danh sách Pod IP.
- Client chọn pod nào connect (client-side load balancing).
- Dùng cho: StatefulSet (cần address từng pod), service discovery custom.
# DNS lookup headless service
nslookup my-db.default.svc.cluster.local
# Address: 10.244.0.5
# Address: 10.244.1.3
# Address: 10.244.2.7
kube-proxy — ai forward traffic
kube-proxy chạy trên mỗi node, watch Service + Endpoints, cập nhật network rules:
| Mode | Cơ chế | Khi nào |
|---|---|---|
| iptables | NAT rules (DNAT + random probability) | Mặc định hầu hết cluster |
| IPVS | Kernel IPVS module (round-robin, least-conn…) | Cluster > 1000 Service |
| eBPF (Cilium) | Thay thế hoàn toàn kube-proxy | Cilium kubeProxyReplacement=true |
# Xem mode kube-proxy đang dùng
kubectl -n kube-system get cm kube-proxy -o yaml | grep mode
# Xem iptables rules (trên node)
iptables -t nat -L KUBE-SERVICES | head -20
kube-proxy không proxy traffic qua process — nó chỉ cấu hình kernel để kernel forward.
EndpointSlice (K8s 1.21+ GA)
Cluster lớn (hàng nghìn pod) → Endpoints object quá lớn → mỗi thay đổi phải broadcast full list. EndpointSlice chia nhỏ (mỗi slice max 100 endpoints mặc định), chỉ broadcast slice thay đổi.
kubectl get endpointslice -l kubernetes.io/service-name=my-api
Bạn không cần tạo EndpointSlice — K8s tự quản lý. Chỉ cần biết khi debug ở cluster lớn.
Service without selector
Khi bạn muốn Service trỏ tới IP bên ngoài cluster (legacy DB, external API):
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
ports:
- port: 5432
---
apiVersion: v1
kind: Endpoints
metadata:
name: external-db # Cùng tên với Service
subsets:
- addresses:
- ip: 192.168.1.100 # IP bên ngoài
ports:
- port: 5432
Pod trong cluster gọi external-db:5432 → forward tới 192.168.1.100:5432.
Multi-port Service
spec:
ports:
- name: http # Bắt buộc name khi > 1 port
port: 80
targetPort: 8080
- name: grpc
port: 9090
targetPort: 9090
Session affinity
Mặc định: kube-proxy round-robin. Nếu cần sticky session:
spec:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 3 giờ
Tóm tắt
- Service = VIP + DNS + load balancing cho pod. Pod IP thay đổi, Service IP ổn định.
- ClusterIP cho internal, NodePort cho test, LoadBalancer cho production cloud.
- Headless (
clusterIP: None) khi cần DNS trả pod IP trực tiếp. - Endpoints trống = lỗi #1: label selector không match hoặc pod không Ready.
- kube-proxy cấu hình kernel rules, không proxy traffic qua userspace.
Câu hỏi hay gặp
Service port và targetPort khác nhau — dùng port nào khi gọi?
Trả lời: Client gọi <service-name>:<port> (port của Service). Kubernetes forward tới <pod-ip>:<targetPort>. Ví dụ: Service port 80, targetPort 8080 → client gọi my-api:80, pod listen :8080.
Pod đã Running nhưng endpoints vẫn trống?
Trả lời: Kiểm tra: (1) Pod label có match Service selector không (kubectl get pods --show-labels); (2) Pod có Ready không (readiness probe pass); (3) Pod cùng namespace với Service. Ba điều kiện phải đúng hết.
Khi nào dùng Ingress thay vì LoadBalancer Service?
Trả lời: LoadBalancer tạo một LB per Service → tốn tiền khi có nhiều service. Ingress dùng một LB cho nhiều service, route theo host/path. Chi tiết → Bài 09.
Bài tiếp theo (Giai đoạn III): DNS, CoreDNS và service discovery — resolve tên service thành IP.