Services & NodePort
1. Vấn đề
- Pod chết thì IP đổi → ai đang gọi theo IP sẽ hỏng.
- Có nhiều Pod giống nhau → gọi Pod nào?
- Laptop của bạn (
192.168.81.1) không gọi thẳng được IP Pod10.244.x.x– đó là mạng ảo bên trong cluster.
Service là một object cho một nhóm Pod một địa chỉ cố định (tên + IP), và tự động chuyển request tới một Pod còn sống trong nhóm. Service cân bằng tải ở Layer 4 (TCP/UDP).
Service giúp các thành phần liên kết lỏng (loose coupling): frontend không cần biết backend có mấy Pod, ở đâu.
2. Các loại Service lồng nhau
| Loại | Gọi được từ | Thêm gì so với loại bên trong |
|---|---|---|
| ClusterIP (mặc định) | Chỉ trong cluster | IP ảo + tên DNS |
| NodePort | Ngoài cluster qua <IP-node>:30000-32767 | Mở 1 port trên mọi node |
| LoadBalancer | Internet / VPC | Cloud tạo LB trỏ vào các node |
| ExternalName | Trong cluster | Chỉ là bản ghi CNAME, không có Pod |
3. Các thành phần của Service
Tên & DNS
CoreDNS tự tạo bản ghi "tên → IP":
| Gọi từ đâu | Tên dùng được |
|---|---|
| Pod cùng namespace | myapp-service |
| Pod khác namespace | myapp-service.default |
| Tên đầy đủ (FQDN) | myapp-service.default.svc.cluster.local |
ClusterIP
apiserver cấp một IP trong dải Service (ví dụ 10.96.0.50). IP này không đổi cho tới khi xóa Service.
Ba loại port
| Field | Nghĩa | Ví dụ | Bắt buộc? |
|---|---|---|---|
port | Cửa của Service | 80 | ✅ Có |
targetPort | Cửa của container, nơi app thật sự nghe | 80 | Không, mặc định = port |
nodePort | Cửa trên node (NodePort/LoadBalancer) | 30008 | Không, tự cấp trong 30000–32767 |
protocol | TCP / UDP / SCTP | TCP | Không, mặc định TCP |
name | Tên cặp port | http | Khi có ≥ 2 port |
nodePort = cổng tòa nhà, port = số máy lẻ của hotline, targetPort = cửa căn hộ.
Selector
Service không ghi tên Pod, chỉ ghi điều kiện nhãn:
selector: # ✅ map phẳng
app: myapp
selector:
matchLabels: # ❌ của Deployment/ReplicaSet, KHÔNG dùng cho Service
app: myapp
Những gì Kubernetes tự dựng
| Thành phần | Làm gì | Xem bằng |
|---|---|---|
| EndpointSlice | Danh sách IP:port các Pod khớp selector và Ready | kubectl get endpoints myapp-service |
| kube-proxy | Trên mọi node, viết luật iptables: tới ClusterIP → đổi đích thành một Pod ngẫu nhiên | iptables-save -t nat |
| CoreDNS | Trả lời "tên → ClusterIP" | nslookup myapp-service |
kubectl get endpoints ra <none> → Service không có Pod nào để chuyển tới. Kiểm tra lại selector có khớp label Pod không, Pod có Ready không.
Khi có nhiều Pod, iptables chọn bằng xác suất ngẫu nhiên (ví dụ 3 Pod: 1/3, rồi 1/2, rồi 1) – mỗi kết nối mới có thể vào Pod khác.
4. NodePort
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: NodePort
ports:
- targetPort: 80 # cửa container
port: 80 # cửa Service (bắt buộc)
nodePort: 30008 # cửa trên node, 30000–32767
selector: # chép y nguyên labels của Pod
app: myapp
type: front-end
- Port
30008được mở trên tất cả node – kể cả master và node không chạy Pod nào. Gọi node nào cũng tới được Pod. externalTrafficPolicy: Local→ chỉ node có Pod mới nhận traffic (giữ IP client gốc, tránh nhảy thêm một hop).
kubectl create -f svc.yaml
kubectl get svc
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# myapp-service NodePort 10.96.0.50 <none> 80:30008/TCP 5s
curl http://192.168.81.102:30008 # từ laptop
Tạo nhanh bằng lệnh
| Lệnh | Đặc điểm |
|---|---|
kubectl expose deployment myapp --type=NodePort --port=80 --name=myapp-svc | Tự lấy selector từ Deployment; không chọn được nodePort |
kubectl create service nodeport myapp --tcp=80:80 --node-port=30008 | Chọn được nodePort, nhưng selector mặc định là app=myapp |
5. Headless Service và Service không selector
| Loại | Cách khai báo | Dùng khi |
|---|---|---|
| Headless | clusterIP: None | DNS trả về IP từng Pod thay vì một ClusterIP – dùng cho StatefulSet (pod-0.svc…) |
| Không selector | Bỏ selector, tự tạo EndpointSlice | Trỏ tới IP ngoài cluster (DB on-prem) |