1. Network có sẵn mà chúng ta có thể sử dụng
Mọi ứng dụng microservices đều cần giao tiếp với nhau. Trong video này, mình sẽ chỉ cho các bạn cách kết nối hai deployment trên Kubernetes, và điều này sẽ giúp các dịch vụ của bạn có thể trò chuyện và làm việc cùng nhau như một nhóm
2. Tạo 2 Deployment đơn giản (ví dụ nginx và curl client)
Tạo namespace:
kubectl create ns nginx
Ví dụ file nginx-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
Ví dụ file curl-client-deployment.yaml:
tạo namespace:
kubectl create ns curl
apiVersion: apps/v1
kind: Deployment
metadata:
name: curl-client
namespace: curl
spec:
replicas: 1
selector:
matchLabels:
app: curl-client
template:
metadata:
labels:
app: curl-client
spec:
containers:
- name: curl
image: appropriate/curl
command: ["sleep", "3600"]
3. Tạo Service cho Deployment nginx để curl-client có thể gọi tới
Ví dụ file nginx-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: nginx
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
4. Triển khai các resource lên cluster
kubectl apply -f nginx-deployment.yaml
kubectl apply -f curl-client-deployment.yaml
kubectl apply -f nginx-service.yaml
5. Kiểm tra kết nối giữa 2 Deployment
-
Lấy tên Pod của curl-client:
kubectl get pods -l app=curl-client -n curl
-
Vào Pod curl-client để thử gọi nginx-service:
kubectl exec -it <curl-client-pod-name> -- sh
curl http://nginx-service.nginx
Ở đây, nginx-service là tên Service và nginx là namespace, Kubernetes sẽ tự động resolve DNS nội bộ và chuyển tiếp request tới các Pod của nginx.
KubeDNS trong Kubernetes
KubeDNS là công cụ giúp các pod và service trong Kubernetes “nói chuyện” với nhau qua tên thay vì IP. Điều này cực kỳ quan trọng vì các pod trong Kubernetes thường có IP động, mà không phải lúc nào bạn cũng muốn nhớ hết chúng.
-
Khi bạn tạo một service trong Kubernetes, nó sẽ tự động có một tên DNS, ví dụ:
nginx-service.default.svc.cluster.local. -
Các pod trong cluster có thể dùng tên này để giao tiếp mà không cần phải lo về IP.
Ví dụ:
Giả sử có một service tên nginx-service, để gọi tới, bạn chỉ cần dùng:
curl nginx-service.default.svc.cluster.local
Cân Bằng Tải trong Kubernetes (Load Balancing)
Kubernetes cung cấp nhiều cách để phân phối lưu lượng giữa các pod thông qua Service.
ClusterIP – Cân bằng tải nội bộ:
-
Đây là kiểu service mặc định, dùng để phân phối yêu cầu từ pod này sang pod khác trong cluster.
-
Kubernetes tự động chia đều lưu lượng giữa các pod thông qua Kube Proxy (chạy bằng iptables hoặc ipvs).
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web-service
ports:
- protocol: TCP
port: 80
targetPort: 80
NodePort – Cân bằng tải cho truy cập ngoài cluster:
-
Nếu bạn muốn truy cập dịch vụ từ bên ngoài Kubernetes, dùng kiểu
NodePort. -
Kubernetes sẽ mở một port trên mỗi node, và khi truy cập vào port đó, yêu cầu sẽ được chuyển tới các pod trong service.
- Kubernetes sẽ tự động chọn một cổng trong phạm vi 30000 – 32767 khi bạn tạo service với kiểu
NodePort
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web-service
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30080 (optional)
type: NodePort
LoadBalancer – Cân bằng tải qua External Load Balancer (cho Cloud):
-
Khi bạn triển khai trên cloud, kiểu
LoadBalancersẽ tự động tạo một external load balancer. -
Lưu lượng truy cập sẽ được chuyển qua load balancer và Kubernetes sẽ phân phối đến các pod.
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web-service
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
(Tham khảo thêm)
Quy trình giao tiếp từ Pod đến Pod qua Service và vai trò của kube-proxy
Khi một Pod (ví dụ: Pod A) cần giao tiếp với một Pod khác (ví dụ: Pod B) thông qua một Service, luồng xử lý sẽ có sự tham gia của CoreDNS và kube-proxy như sau:
1. Phân giải tên miền (DNS Resolution)
Đầu tiên, Pod A sẽ sử dụng tên DNS của Service (ví dụ: my-service.default.svc.cluster.local) để bắt đầu kết nối. Yêu cầu này được gửi đến CoreDNS, là máy chủ DNS mặc định trong cụm Kubernetes. CoreDNS sẽ phân giải tên miền này thành địa chỉ IP ảo ổn định của Service, được gọi là ClusterIP.
https://kubernetes.io/docs/tutorials/services/connect-applications-service/
2. Vai trò của kube-proxy
Trên mỗi node trong cụm Kubernetes, có một agent tên là kube-proxy chạy dưới dạng DaemonSet. Nhiệm vụ chính của kube-proxy là theo dõi các thay đổi về đối tượng Service và EndpointSlices (danh sách các Pod đang hoạt động phía sau một Service) từ API Server.
https://kodekloud.com/blog/kube-proxy/
https://kubernetes.io/docs/reference/networking/virtual-ips/
3. Thiết lập quy tắc mạng (iptables/IPVS)
Khi một Service được tạo hoặc cập nhật, kube-proxy trên mỗi node sẽ nhận thông tin này. Dựa trên thông tin đó, kube-proxy sẽ thiết lập các quy tắc chuyển tiếp mạng trên chính node đó. Có một số chế độ hoạt động cho kube-proxy, nhưng phổ biến nhất là iptables và IPVS.
-
Chế độ
iptables(mặc định):kube-proxysẽ tạo ra các quy tắciptablestrong kernel của Linux. Các quy tắc này có nhiệm vụ theo dõi và xử lý các gói tin mạng.
4. Chặn và chuyển hướng lưu lượng (Traffic Interception & NAT)
Khi Pod A gửi một gói tin đến địa chỉ ClusterIP của Service, các quy tắc iptables do kube-proxy tạo ra trên node sẽ khớp với gói tin này và chặn nó lại.
Lúc này, quy tắc iptables sẽ thực hiện Destination Network Address Translation (DNAT). Nó sẽ thay đổi địa chỉ IP đích của gói tin từ ClusterIP (là một IP ảo) thành địa chỉ IP thực của một trong các Pod đích (Pod B) đang hoạt động phía sau Service đó. kube-proxy sẽ chọn một Pod đích một cách ngẫu nhiên (hoặc theo thuật toán cân bằng tải khác) từ danh sách các endpoint hợp lệ.
5. Chuyển tiếp đến Pod đích
Sau khi địa chỉ IP đích đã được thay đổi thành IP của Pod B, gói tin sẽ được hệ điều hành của node chuyển tiếp trực tiếp đến Pod B.
Như vậy, kube-proxy không thực sự “proxy” lưu lượng theo nghĩa truyền thống (tức là không đứng giữa luồng dữ liệu). Thay vào đó, nó hoạt động như một bộ điều khiển, thiết lập các quy tắc mạng ở tầng kernel để đảm bảo lưu lượng được định tuyến một cách chính xác và hiệu quả từ một IP ảo của Service đến IP thật của Pod.