文章目录
- 问题背景
- 1、什么是Headless Service
- 1.2 为什么 Zookeeper 使用 Headless Service?
- 1.2 Headless Service 的 DNS 行为
- 1.3 验证示例
- 1.4 如何创建 Headless Service?
- 2. zk-0.zookeeper.default.svc.cluster.local 域名是如何创建出来的? 是无头服务自动映射的吗
问题背景
zookeeper ClusterIP None 这中svc作用是什么,为什么没有cluster ip
1、什么是Headless Service
在 Kubernetes 中,ClusterIP 类型的 Service 被设置为 None(即 ClusterIP: None)时,表示这是一个 Headless Service(无头服务)。这种设计有特定的用途和行为模式:
Headless Service 的核心作用:
-
直接访问 Pod IP,跳过 Service 代理
-
普通 Service:会分配一个虚拟 ClusterIP,流量通过 kube-proxy 负载均衡到后端 Pod。
-
Headless Service:没有 ClusterIP,DNS 查询直接返回所有后端 Pod 的 IP 地址(而非 Service 的虚拟 IP)。
-
-
适用场景
-
有状态应用(如 Zookeeper、Kafka、MongoDB):需要直接访问特定 Pod(如通过 Pod 域名)。
-
自定义服务发现:应用需要自行处理负载均衡或直接连接 Pod。
-
StatefulSet 配套使用:StatefulSet 的 Pod 具有稳定的域名(如
pod-name.svc-name.namespace.svc.cluster.local
)。
1.2 为什么 Zookeeper 使用 Headless Service?
简单来说,当一个pod 是sts
类型,每个副本的名称,都是固定的(固定名称是前提,不像deployment生成的pod都会带随机字符串 ),如 zk-0、zk-1,此时需要无头服务,被访问时,都是指定访问,不能直接负载均衡随机调度。
Zookeeper 是一个有状态分布式协调服务,它的典型配置需求:
支持应用层的负载均衡或服务发现:
- 客户端应用(或应用内置的逻辑)可以获取到所有后端 Pod 的 IP 列表,然后自己决定如何连接(例如,轮询、根据角色选择 leader 等)。
- 对于像 ZooKeeper 这样的集群,成员之间需要互相知道彼此的地址来形成集群,Headless Service 提供了完美的解决方案。
支持直接的 Pod-to-Pod 通信:
- 当你需要直接连接到某个特定的 Pod 实例时(比如访问主节点进行写操作,或进行健康检查),可以通过其唯一的 DNS 名称直接访问。:
StatefulSet + Headless Service 能为每个 Pod 提供固定域名
。
1.2 Headless Service 的 DNS 行为
对于 Service zookeeper:
-
普通查询(非 Headless):
nslookup zookeeper.mano-2.svc.cluster.local
返回一个虚拟 ClusterIP。
-
Headless 查询:
nslookup zookeeper.mano-2.svc.cluster.local
返回所有后端 Pod 的 IP 地址(例如):
Name: zookeeper.mano-2.svc.cluster.local Address: 10.244.1.10 # zk-0 的 IP Address: 10.244.2.20 # zk-1 的 IP Address: 10.244.3.30 # zk-2 的 IP
如果您的 ZooKeeper StatefulSet 有 3 个副本,名为 zk-0, zk-1, zk-2,那么 DNS 会解析:
- zookeeper.default.svc.cluster.local -> 返回 zk-0, zk-1, zk-2 的 IP 地址列表(A 记录)。
- zk-0.zookeeper.default.svc.cluster.local -> 返回 zk-0 Pod 的 IP 地址。
- zk-1.zookeeper.default.svc.cluster.local -> 返回 zk-1 Pod 的 IP 地址。
- zk-2.zookeeper.default.svc.cluster.local -> 返回 zk-2 Pod 的 IP 地址。
1.3 验证示例
1、查看 Service 定义
kubectl get svc zookeeper -o yaml
输出关键字段:
spec:clusterIP: None # 明确标记为 Headlessports:- port: 2181targetPort: 2181selector:app: zookeeper
2、通过 DNS 直接访问 Pod
每个 StatefulSet 的 Pod 会获得独立域名:
# 查询单个 Pod 的 DNS
nslookup zk-0.zookeeper.mano-2.svc.cluster.local# 客户端连接示例(Zookeeper 客户端)
zkCli.sh -server zk-0.zookeeper:2181,zk-1.zookeeper:2181,zk-2.zookeeper:2181
与普通 Service 的对比:
特性 | Headless Service | 普通 Service |
---|---|---|
ClusterIP | None | 自动分配虚拟 IP (如 10.96.x.x ) |
DNS 解析结果 | 返回所有 Pod IP | 返回 Service 的 ClusterIP |
负载均衡 | 由客户端或应用层实现 | 由 kube-proxy 实现(iptables/IPVS) |
典型应用场景 | 有状态服务(如 Zookeeper、MySQL) | 无状态服务(如 Nginx、微服务) |
1.4 如何创建 Headless Service?
Headless 示例:
apiVersion: v1
kind: Service
metadata:name: zookeepernamespace: default
spec:clusterIP: None # 这是关键,指定为 Noneports:- port: 2181name: client- port: 2888name: server- port: 3888name: leader-electionselector:app: zookeeper # 选择后端 Pod 的标签# 注意:Headless Service 通常不指定 type,默认就是 ClusterIP
- clusterIP: None:这不是“不想要 IP”,而是明确地、主动地要求创建一个 Headless Service(无头服务)。它告诉 Kubernetes:“我不要你为这个服务分配一个虚拟的 ClusterIP,我要直接访问后端的 Pod”。
- 自动分配 ClusterIP:这是 Kubernetes Service 的默认行为。你不需要做任何特殊配置来“开启”自动分配。
有头示例:
apiVersion: v1
kind: Service
metadata:name: my-web-service
spec:# 注意:这里完全不写 clusterIP 字段ports:- port: 80targetPort: 8080protocol: TCPname: httpselector:app: my-web-app# type: ClusterIP # type 字段也可以省略,默认就是 ClusterIP
当你创建这个 Service 时,Kubernetes 会:
- 自动从集群的 Service IP 池(由
--service-cluster-ip-range
参数定义)中选择一个可用的 IP 地址。 - 将这个 IP 地址分配给该 Service。
- 你可以在 kubectl get svc 的输出中看到这个分配的 IP(不再是 None)。
2. zk-0.zookeeper.default.svc.cluster.local 域名是如何创建出来的? 是无头服务自动映射的吗
是的,zk-0.zookeeper.default.svc.cluster.local 这个域名是由 Kubernetes 的 Headless Service(无头服务)机制自动创建的,其核心原理依赖于 Kubernetes DNS 系统(如 CoreDNS)的自动服务发现功能。
下面简述其工作原理:
1、 前提条件:Headless Service + StatefulSet
这个域名的自动生成,通常发生在以下组合中:
- 一个 Headless Service:clusterIP: None
- 一个 StatefulSet:管理有状态应用的 Pod,如 zk-0, zk-1, zk-2
2、DNS 自动映射原理
当满足上述条件时,Kubernetes DNS 组件(如 CoreDNS)会根据以下规则自动为每个 Pod 生成稳定的 DNS 记录:
规则一:Pod 主机名 DNS 记录
对于每一个由 StatefulSet 创建的 Pod(如 zk-0),DNS 会生成一条 A 记录:
<pod-name>.<service-name>.<namespace>.svc.cluster.local --> <pod-ip>
在你的例子中:
pod-name = zk-0
service-name = zookeeper
namespace = default
cluster.local = 集群默认域名