Trong Kubernetes, Headless Service là một loại Service đặc biệt cho phép các ứng dụng tự quản lý việc cân bằng tải thay vì dựa vào Service để thực hiện điều này. Khác với các Service thông thường có ClusterIP được Kubernetes tự động gán, Headless Service được cấu hình với clusterIP: None. Điều này dẫn đến việc DNS của Kubernetes không trả về một địa chỉ IP duy nhất cho Service, mà thay vào đó, nó sẽ trả về danh sách các địa chỉ IP của tất cả các Pod đang chạy thuộc Service đó.
Việc không có ClusterIP đồng nghĩa với việc kube-proxy không can thiệp vào việc định tuyến hoặc cân bằng tải cho Headless Service. Các yêu cầu đến Service sẽ được phân giải trực tiếp thành địa chỉ IP của các Pod backend. Điều này mang lại lợi ích về hiệu suất do bỏ qua một lớp trung gian, đồng thời cho phép các nhà phát triển xây dựng các giải pháp cân bằng tải tùy chỉnh dựa trên danh sách IP Pod trả về.
Lưu ý quan trọng:
- Cả Headless Service và ClusterIP Service đều có thể được phân giải tên từ bên trong các container cùng namespace.
- Headless Service phân giải trực tiếp ra địa chỉ IP của Pod. Bạn có thể ping tên Service và nhận được phản hồi từ các IP Pod.
- ClusterIP Service phân giải ra địa chỉ
ClusterIPđược gán. Việc ping trực tiếp tên Service sẽ không thành công. - Khi truy cập một Headless Service, bạn sẽ kết nối trực tiếp đến cổng của Pod. Các thuộc tính
portvàtargetPorttrong định nghĩa Service ít quan trọng hơn trong trường hợp này. - Khi truy cập ClusterIP Service, bạn cần chỉ định
targetPort(cổng trên Pod) vàport(cổng của Service để truy cập).
Tạo Headless Service
Để tạo một Headless Service, bạn chỉ cần đặt giá trị của trường clusterIP trong phần spec của tài nguyên Service thành None.
Ví dụ 1: Deployment và Headless Service `app-one`
File cấu hình app-one-headless.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: dev
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-one-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: app-one
template:
metadata:
labels:
app: app-one
spec:
containers:
- name: app-one-container
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: app-one-svc
namespace: dev
spec:
selector:
app: app-one
clusterIP: None # Đây là điểm mấu chốt để tạo Headless Service
ports:
- protocol: TCP
port: 80
targetPort: 80
Ví dụ 2: Deployment và Headless Service `app-two`
File cấu hình app-two-headless.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-two-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: app-two
template:
metadata:
labels:
app: app-two
spec:
containers:
- name: app-two-container
image: httpd:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: app-two-svc
namespace: dev
spec:
selector:
app: app-two
clusterIP: None
ports:
- protocol: TCP
port: 80
targetPort: 80
Kiểm tra tài nguyên Kubernetes
Sau khi áp dụng các tệp cấu hình trên, bạn có thể kiểm tra các Pod, Service và Deployment:
kubectl get pods,svc,deploy -o wide -n dev
Kết quả sẽ hiển thị các Pod đang chạy, Service với CLUSTER-IP là None và các Deployment tương ứng.
Truy cập Headless Service
Vì Headless Service không có ClusterIP, bạn không thể truy cập nó bằng địa chỉ IP. Thay vào đó, bạn phải sử dụng tên DNS của Service. Việc phân giải tên Service này chỉ hoạt động hiệu quả khi được thực hiện từ bên trong một container trong cluster.
Định dạng tên DNS chuẩn là:
[tên-service].[namespace].svc.cluster.local
Thực hiện truy vấn DNS từ bên trong container
Để kiểm tra, bạn có thể truy cập vào một Pod và thực hiện các lệnh dig hoặc nslookup.
# Truy cập vào một Pod của app-one
kubectl exec -it app-one-deployment- -- bash -n dev
# Kiểm tra tệp resolv.conf để xem cấu hình DNS
cat /etc/resolv.conf
# Sử dụng dig để phân giải tên Service
dig app-one-svc.dev.svc.cluster.local
Lệnh dig sẽ trả về danh sách các địa chỉ IP của tất cả các Pod thuộc app-one-svc.
# Ví dụ kết quả dig:
;; ANSWER SECTION:
app-one-svc.dev.svc.cluster.local. 30 IN A 10.244.1.9
app-one-svc.dev.svc.cluster.local. 30 IN A 10.244.2.8
app-one-svc.dev.svc.cluster.local. 30 IN A 10.244.0.8
Bạn cũng có thể sử dụng nslookup:
nslookup app-one-svc 10.96.0.10 # Giả sử 10.96.0.10 là IP của CoreDNS
Kết quả sẽ tương tự, liệt kê các IP của Pod backend.
Truy cập dịch vụ khác bằng tên
Từ bên trong một Pod, bạn có thể thử truy cập các dịch vụ khác bằng tên DNS của chúng:
# Thử kết nối tới app-two-svc bằng telnet (hoặc ping)
telnet app-two-svc 80
ping app-two-svc -c 3
Các lệnh này sẽ tương tác với một trong các Pod thuộc app-two-svc dựa trên kết quả phân giải DNS.
Cấu hình chi tiết
Dưới đây là một ví dụ chi tiết về cách định nghĩa một Headless Service cùng với Deployment của nó, bao gồm cả các cấu hình tài nguyên và probe:
apiVersion: v1
kind: Service
metadata:
name: web-app-headless
namespace: dev
spec:
clusterIP: None
ports:
- name: web-app-port
port: 8080 # Cổng mà Service sẽ lắng nghe
protocol: TCP
targetPort: 80 # Cổng trên container của Pod
selector:
app: web-app
type: ClusterIP # Mặc định là ClusterIP, nhưng clusterIP: None sẽ ghi đè
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app-deployment
namespace: dev
labels:
app: web-app
spec:
replicas: 1
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web-app-container
image: my-custom-webapp:v1.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: http
protocol: TCP
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 10
timeoutSeconds: 3
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 5
timeoutSeconds: 2
periodSeconds: 5
successThreshold: 1
failureThreshold: 3