在 Kubernetes (K8s) 中,标准的 Service (如 ClusterIP 或 NodePort) 通常用于在 Pod 集合前提供一个稳定的、负载均衡的虚拟 IP。然而,对于需要感知集群内所有成员状态的分布式有状态应用(如 Cassandra, ZooKeeper, 或etcd),这种虚拟IP模型是不适用的。这些应用需要能够发现集群中所有独立的 Pod 实例的 IP 地址。
Headless Service 就是为解决这个问题而设计的。通过将 Service 的 clusterIP 字段设置为 None,K8s 不会分配一个 Cluster IP,也不会执行负载均衡。相反,它会配置 DNS 服务器,使得查询该 Service 时,返回所有关联 Pod 的 IP 地址列表。
关键概念:Headless Service vs Standard Service
| 特性 | 标准 Service (ClusterIP) | Headless Service |
|---|---|---|
| Cluster IP | 有,提供稳定的虚拟 IP | 无 (clusterIP: None) |
| 负载均衡 | Kube-proxy 执行 L4 负载均衡 | 无,由客户端自行处理 |
| DNS 查询结果 | 返回单个 A 记录指向 Service IP | 返回多个 A 记录,指向所有 Pod IP |
操作步骤:配置 Headless Service
我们将创建一个简单的 Deployment 和一个 Headless Service 来演示成员发现。
步骤 1: 部署目标应用 (Deployment)
首先,定义一个简单的 Deployment,我们希望通过 Service 发现其所有副本。
创建 app-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: discovery-app
labels:
app: member-discovery
spec:
replicas: 3
selector:
matchLabels:
app: member-discovery
template:
metadata:
labels:
app: member-discovery
spec:
containers:
- name: busybox-container
image: busybox
command: ["sh", "-c", "echo 'Running member' && sleep 3600"]
执行部署:
kubectl apply -f app-deployment.yaml
kubectl get pods
# 确认有三个 Pod 在运行
步骤 2: 定义 Headless Service
关键在于设置 clusterIP: None,并将 selector 指向我们的 Pod。
创建 headless-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: member-service
labels:
app: member-discovery
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: member-discovery
# 关键配置:设置 clusterIP 为 None
clusterIP: None
执行 Service 创建:
kubectl apply -f headless-service.yaml
kubectl get svc member-service
# 确认 CLUSTER-IP 字段显示为 <None>
步骤 3: 验证成员发现 (DNS 查询)
现在,我们从集群内部运行一个临时的 Pod 来执行 DNS 查询,验证 Headless Service 是否返回了所有后端 Pod 的 IP 地址。
使用 nslookup 查询 Service 的 FQDN (Fully Qualified Domain Name):servicename.namespace.svc.cluster.local。
# 临时启动一个 busybox 容器并执行 nslookup
kubectl run -it --rm --restart=Never dns-test --image=busybox -- sh
# 在容器内部执行查询 (假设命名空间为 default)
/ # nslookup member-service.default.svc.cluster.local
# 预期的输出结果将包含所有三个 Pod 的独立 IP 地址:
Server: 10.96.0.10
Address 1: 10.96.0.10#53
Name: member-service.default.svc.cluster.local
Address 1: 10.244.0.15 # Pod 1 IP
Address 2: 10.244.0.16 # Pod 2 IP
Address 3: 10.244.0.17 # Pod 3 IP
# 退出容器
/ # exit
总结
通过配置 clusterIP: None 的 Headless Service,我们成功地将 DNS 记录从单个 Service IP 切换到了所有后端 Pod 的 IP 列表。这使得分布式集群应用能够在启动时查询这个 DNS 记录,获取到所有同伴节点的精确网络地址,从而实现高效的集群成员发现和拓扑管理。Headless Service 是构建高性能、高可用性有状态应用集群的基础组件之一。
汤不热吧