+2

Hiểu và thực hành Networking trong Kubernetes với ví dụ BE + FE

1. Giới thiệu

Sau bài Triển khai ứng dụng full-stack với k8s cho người mới. Trong đó, mình đã giới thiệu và thực hành với các resource của k8s như Deployment, Service, StatefulSet,..., giúp mọi người có thể thực hành và dựng được 1 app cơ bản, hoàn chỉnh chạy trong cluster từ minikube. Tuy nhiên ở bài viết đó thì chưa có giải thích, đào sâu vào các câu hỏi như là các thành phần giao tiếp với nhau như nào, tại sao chỗ này phải dùng service, tại sao trong service, chỗ dùng NodePort, chỗ dùng ClusterIP,... rất nhiều câu hỏi về kiến trúc, về network,.. mà trong bài trên chưa đào sâu vào để hiểu. Do vậy, trong bài này sẽ tập trung vào các ví dụ đơn giản để làm rõ và hiểu hơn về Networking trong k8s, từ khái niệm đến các thực hành, để bạn có một hình dung trực quan về cách các resource giao tiếp với nhau trong cluster.

2. Pod IP & vì sao phải cần Service

Khi chạy app trong Kubernetes, mỗi Pod được gán một IP riêng. Nhưng IP này không cố định: khi Pod chết/rollback/scale là IP của Pod đổi ngay. Vì thế, nếu FE hoặc Pod khác gọi trực tiếp vào IP cũ Pod → rất dễ “toang” sau một lần redeploy/restart.

Dưới đây là 1 ví dụ, mà bạn có thể làm theo để thấy được vấn đề và cách giải quyết vấn đề bằng Service

2.1. Thử gọi trực tiếp Pod IP (và lý do thất bại)

  • Bước 1: Tạo một Pod backend đơn giản (chưa có Service)

Ở đây mình dùng image nginxdemos/hello:plain-text là một demo image do team NGINX cung cấp, dùng để test nhanh NGINX và môi trường container/k8s. Nó đơn giản là nhận 1 request và trả về response dạng text.

Các bạn nhớ bật minikube để có thể thực hành ở local nhé. Bật terminal lên chạy kubectl apply -f be-pod.yaml Đợi 1 lúc cho pod khởi chạy xong thì chạy lệnh kubectl get pod be-pod -o wide để thấy được IP của Pod be-pod. (ví dụ: 10.244.0.209)

  • Bước 2: Tạo tạm một Pod “client” để test call pod be-pod bằng curl

kubectl run curl --image=curlimages/curl -it --rm --restart=Never -- sh

Lệnh này dùng image curlimages/curl chưa duy nhất curl để test HTTP request từ trong cluster và mở shell sau khi pod được tạo thành công

Khi vào shell của pod curl này thì gõ lệnh curl -s http://10.244.0.209

Bạn sẽ thấy 1 response trên terminal:

image.png

Vậy là từ Pod curl đã call thành công sang be-pod khi biết được IP của pod be-pod

  • Bước 3: Mô phỏng Pod restart => IP đổi

Tuy nhiên chúng ta sẽ mô phỏng tình huống pod bị kill, restart, redeploy,... Chúng ta chạy các lệnh sau:

kubectl delete pod be-pod
kubectl apply -f be-pod.yaml
kubectl get pod be-pod -o wide

Sau khi chạy lại pod và check ip pod, chúng ta thấy ip của pod đã thay đổi. Khi này, thực hiện lại bước 2 với ip pod cũ thì sẽ bị lỗi. Đây là lý do không bao giờ nên setup FE gọi trực tiếp Pod IP trong K8s

2.2. Dùng Service (ClusterIP) để có định danh ổn định + load balancing

Trong bài viết trên, chúng ta thấy có sự xuất hiện của service. Service sẽ cũng cấp 1 định danh ổn định đại diện cho các Pod trong 1 nhóm nào đó (chọn qua selector)

  • Bước 4: Tạo Service cho backend

Ở đây, selector.app phải trỏ đúng vào metadata.labels.app trong be-pod.yaml để service biết rằng nó cần quản lý các pod có nhãn là be

Trong terminal, chúng ta chạy: kubectl apply -f be-svc.yaml

Sau khi apply thành công thì chạy kubectl get svc backend để xem thông tin service của be-pod

image.png

Cách setup service (dùng ClusterIP) sẽ cung cấp 1 IP cho service này, và chúng ta có thể thấy None ở ExternalIP, tức là không thể gọi đến service này từ ngoài cluster.

  • Bước 5: Gọi qua DNS Service (ổn định, không sợ Pod đổi IP)

Chúng ta sẽ lại dùng pod curl để call thử đến be-pod thông qua service

image.png

Lúc này chúng ta hoàn toàn ko cần quan tâm đến Pod IP, ngay cả khi restart lại be-pod thì nhờ service, chúng ta vẫn có thể thực hiện các request call đến be-pod

2.3. Thêm Deployment + scale để thấy Service load balance

Trong thực tế, có thể sẽ có nhiều tình huống crash pod, (ở local thì thường chạy với 1 pod để phù hợp với sự hạn chế tài nguyên của máy tính). Do vậy thay vì Pod đơn lẻ, ta demo với Deployment (2 replicas) để scale và test với load balancing ( 1 cơ chế cân bằng tải của service )

File deployment.yaml

kubectl apply -f be-deploy.yaml
kubectl get pods -l app=be -o wide

Tiếp tục dùng pod curl để chạy thử lệnh: for i in 1 2 3 4 5; do curl -s http://backend | grep 'Server name'; done

image.png

Bạn sẽ thấy “Server name” thay đổi qua lại giữa 2 Pod khác nhau → chứng tỏ Service đang cân bằng tải cho bạn. Còn về các cách, hay là các thuật toán cân bằng tải sẽ được trình bày trong 1 bài viết trong tương lai gần. Đến đây, chúng ta đã có thể hiểu phần nào tại sao không dùng pod IP trong thực tế, cách giao tiếp trong cluster, cách từ 1 FE sẽ call đến BE trong môi trường k8s (bằng cách set biến env trong FE, ví dụ API_BASE_URL=http://backend:80)

3. Các loại Service trong Kubernetes

Trong phần trước, bạn đã thấy ClusterIP giải quyết bài toán Pod IP “ngắn hạn”. Tuy nhiên, đôi khi chúng ta cần mở Service cho người dùng bên ngoài cluster truy cập (ví dụ: frontend app, public API). Đây là lúc cần tìm hiểu kỹ hơn các loại Service khác

3.1. ClusterIP (mặc định)

  • Là gì?
    • Đây là Service mặc định trong Kubernetes.
    • Chỉ cho phép truy cập bên trong cluster.
    • DNS: <service-name>.<namespace>.svc.cluster.local
  • Use case:
    • BE ↔ DB (nội bộ)
    • FE ↔ BE (nếu cả hai đều nằm trong cluster)

3.2. NodePort

  • Là gì?
    • Mở một port cố định (30000–32767) trên mỗi Node
    • Bạn có thể truy cập Service từ ngoài cluster thông qua: http://<NodeIP>:<NodePort> (NodeIP lấy bằng minikube ip)
  • Use case:
    • Demo local với Minikube/K3s
    • Khi chưa có Ingress/LoadBalancer
  • Yaml mẫu:

3.3. LoadBalancer

  • Là gì?
    • Khi bạn dùng Kubernetes trên cloud (AWS/GCP/Azure), Service type LoadBalancer sẽ nhờ cloud cấp IP public/Load Balancer thật
    • Người dùng chỉ cần truy cập IP public đó
  • Use case:
    • Production trên cloud.
    • Khi cần expose trực tiếp service ra Internet.
  • Yaml mẫu:

Truy cập thông qua: http://<cloud-public-ip>. Và chỉ hoạt động nếu cluster chạy trên cloud provider có hỗ trợ (EKS, GKE, AKS…)

4. Kinh nghiệm & Best Practice khi làm việc với Networking trong Kubernetes

Sau khi thực hành qua ClusterIP, NodePort, LoadBalancer, mình rút ra được một số kinh nghiệm nho nhỏ. Nếu bạn cũng mới bắt đầu, hy vọng sẽ giúp tiết kiệm kha khá thời gian.

  • Luôn dùng Service name thay vì Pod IP
  • Chọn đúng loại Service cho đúng mục đích
  • NodePort – đừng hardcode nếu không cần

Tài liệu tham khảo

  1. https://kubernetes.io/docs/concepts/cluster-administration/networking/
  2. https://www.vmware.com/topics/kubernetes-networking

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí