Trong bài viết này chúng ta sẽ tìm hiểu về các định nghĩa loại Kubernetes Service và ứng dụng của chúng nhé.
Các ứng dụng triển khai trên các pod sử dụng deployment có thể sẽ cần được truy cập nội bộ trong cụm hoặc truy cập bởi các dịch vụ từ bên ngoài cụm.
Tính năng giúp ta có thể làm việc này là Service trong Kubernetes.
Service cung cấp một IP và DNS name, cho phép giao tiếp hiệu quả và cân bằng tải giữa các pod mà không ảnh hưởng bởi sự thay đổi vòng đời của pod. Việc này đảm bảo kết nối tới các ứng dụng một cách ổn định và tin cậy.
Có 4 loại service chính với ClusterIP là service gốc.
Hãy tưởng tượng rằng nếu ta tạo một NodePort thì ta cũng đã tự động tạo một ClusterIP. Nếu ta tạo một LoadBalancer thì nó cũng đã tự động tạo một NodePort và tự động tạo một ClusterIP. Các service đều được tạo nên dựa trên ClusterIP. Ta sẽ đi sâu hơn vào vấn đề này nhé.
Services và Pods
Service sẽ trỏ vào các pod. Các service không trỏ vào deployment hay replicaset. Service trỏ trực tiếp vào pod sử dụng các label. Điều này cung cấp tính linh hoạt vì service sẽ trỏ chính xác tới pod cần thiết không quan tâm pod đó đã được tạo ra như thế nào.
Ta sẽ cùng xem qua một ví dụ cơ bản nhé:
Không sử dụng service
Ta có 2 node, 1 pod. Các node có IP bên ngoài (4.4.4.1, 4.4.4.2) và IP nội bộ (1.1.1.1, 1.1.1.2). Pod-python chỉ có 1 IP nội bộ.
Giờ ta sẽ thêm pod thứ hai tên là pod-nginx ở trên node-1. Trong Kubernetes, mọi pod có thể kết nối tới nhau thông qua IP nội bộ không quan trọng chúng đang chạy trên node nào.
Tức là pod-nginx có thể ping và kết nối tới pod-python sử dụng IP nội bộ 1.1.1.3 của pod-python.
Giờ nếu pod-python lỗi và kube controller yêu cầu tạo lại một pod mới. Khi này pod-nginx không thể truy cập 1.1.1.3 được nữa và từ đây các pod sẽ không còn có thể giao tiếp được nữa!
ClusterIP
Same scenario, but we configured a ClusterIP service. A service is not scheduled on a specific node like pods. For this article it is enough to assume a service is just available in memory inside the whole cluster.
Ta sẽ cấu hình thêm một ClusterIP service cho cụm. Service sẽ không được lập lịch trên một node cụ thể như các pod. Để cho đơn giản hãy tưởng tượng service chỉ tồn tại trong bộ nhớ bên trong cụm.
Pod-nginx luôn luôn có thể kết nối tới 1.1.10.1 một cách an toàn hoặc sử dụng DNS service-python
và ClusterIP sẽ redirect nó tới pod-python.
We extend the example, spin up 3 instances of python and we now display the ports of the internal IP addresses of all pods and services. Ta sẽ mở rộng ví dụ trên bằng cách thêm 2 instance của pod-python.
Mọi pod trong cụm có thể kết nối tới pod-python trên port 443 thông qua http://1.1.10.1:3000
hoặc http://service-python:3000
. ClusterIP có nhiệm vụ phân bổ các request dưa trên random hoặc round-robin. Tóm lại ClusterIP cung cấp tên và IP để các pod có thể kết nối tới nhau trong cụm.
service-python
có thể được tạo thông qua manifest sau:
apiVersion: v1
kind: Service
metadata:
name: service-python
spec:
ports:
- port: 3000
protocol: TCP
targetPort: 443
selector:
run: pod-python
type: ClusterIP
Chạy câu lệnh kubectl get svc
:
NodePort
Giờ giả sử ta muốn ClusterIP service khả dụng bên ngoài cụm, khi này ta chuyển nó thành một NodePort. Trong ví dụ của chúng ta ta chuyển service-python
thành NodePort như sau:
apiVersion: v1
kind: Service
metadata:
name: service-python
spec:
ports:
- port: 3000
protocol: TCP
targetPort: 443
nodePort: 30080
selector:
run: pod-python
type: NodePort
NodePort cho phép service-python
có thể truy cập từ các node nội bộ và IP ngoài trên port 30080.
Pod trong cụm cũng có thể kết nối tới node nội bộ trên port 30080
NodePort hoạt động giống như ClusterIP. Ta có thể tưởng tượng NodePort tạo ra ClusterIP để dễ hiểu hơn mặc dù thực ra không có ClusterIP nào mới được tạo ra thêm.
LoadBalancer
Ta dùng LoadBalancer nếu ta cần một IP phân phối các request (sử dụng các kĩ thuật ví dụ như round robin) đến các IP bên ngoài của các node. Vì vậy, LoadBalancer được xây dựng dựa trên môt NodePort:
Tưởng tượng LoadBalancer tạo một NodePort và NodePort tạo ra ClusterIP. Ta có thể thay đổi manifest để sử dụng LoadBalancer:
apiVersion: v1
kind: Service
metadata:
name: service-python
spec:
ports:
- port: 3000
protocol: TCP
targetPort: 443
nodePort: 30080
selector:
run: pod-python
type: LoadBalancer
Tất cả công việc của LoadBalancer là tạo một NodePort. Thêm vào đó nó gửi một thông điệp cho provider host Kubernetes để yêu cầu một loadbalancer trỏ vào mọi IP bên ngoài của cụm và các NodePort cụ thể. Nếu provider không hỗ trợ thông điệp đã gửi, không có gì sẽ xảy ra và LoadBalancer sẽ trở thành một NodePort.
LoadBalancer vẫn sẽ mở port 30080 trên các node như trước.
ExternalName
Đơn giản thì nó sẽ tạo một dịch vụ nội bộ với một endpoint trỏ vào một DNS name.
Giả sử pod-nginx đã được triển khai trên cụm nhưng python api ở bên ngoài:
pod-nginx cần kết nối tới http://remote.server.url.com
để giap tiếp với pod-python, điều này sẽ hoạt động tuy nhiên khi ta cần tích hợp python api đó vào cụm thì ta cần ExternalName:
Manifest như sau:
kind: Service
apiVersion: v1
metadata:
name: service-python
spec:
ports:
- port: 3000
protocol: TCP
targetPort: 443
type: ExternalName
externalName: remote.server.url.com
Giờ pod-nginx có thể kết nối tới http://service-python:3000
giống như khi sử dụng ClusterIP. Khi ta quyết định chuyển python api về cụm, ta chỉ cần thay đổi service thành ClusterIP là được:
Điểm mạnh khi dùng ExternalName là bạn vẫn có thể tạo hạ tầng Kubernetes hoàn chỉnh và triển khai các rule, restriction dựa trên các service và IP mặc dù một vài service không ở nội bộ cụm.