在Kubernetes中,服务发现的核心目标是让集群内的Pod(服务实例)能够动态找到并通信,而无需手动管理IP地址(因为Pod的IP是动态变化的)。K8s通过Service资源和DNS系统实现了自动化的服务发现,具体机制和实操如下:
一、核心原理:Service + DNS 协同工作
Service资源:为一组具有相同标签的Pod提供固定的访问入口(ClusterIP),并自动维护Pod的动态变化(新增/删除/重启时自动更新关联关系)。DNS解析:通过CoreDNS(K8s默认DNS服务)将Service名称解析为对应的ClusterIP,使得Pod可以通过“Service名称”而非IP访问目标服务。
二、实操步骤:实现服务发现的完整流程
步骤1:部署目标服务(带标签的Pod)
首先需要部署一组提供服务的Pod,并为其添加统一标签(如),以便后续Service通过标签关联它们。
app: backend
示例:部署一个简单的后端服务(用nginx模拟):
# backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: 3 # 3个副本,模拟服务集群
selector:
matchLabels:
app: backend # 标签选择器
template:
metadata:
labels:
app: backend # Pod标签,与Service关联
spec:
containers:
- name: backend
image: nginx:1.25
ports:
- containerPort: 80 # 容器暴露的端口
部署并验证:
kubectl apply -f backend-deployment.yaml
kubectl get pods -l app=backend # 查看3个Pod是否运行(IP会动态变化)
步骤2:创建Service关联Pod
通过Service为上述Pod分配固定访问地址(ClusterIP),并通过标签选择器与Pod绑定。
示例:创建ClusterIP类型的Service(仅集群内部可访问):
# backend-service.yaml
apiVersion: v1
kind: Service
metadata:
name: backend-service # Service名称(后续用于DNS解析)
spec:
selector:
app: backend # 匹配标签为app=backend的Pod
ports:
- port: 8080 # Service暴露的端口(集群内访问时用)
targetPort: 80 # 转发到Pod的端口(需与Pod的containerPort一致)
type: ClusterIP # 默认类型,仅集群内部可见
创建并验证:
kubectl apply -f backend-service.yaml
kubectl get service backend-service # 查看Service的ClusterIP(如10.96.xx.xx)
此时,Service会自动关联所有的Pod,并维护一个Endpoint列表(记录Pod的IP和端口):
app=backend
kubectl describe service backend-service | grep Endpoints # 查看关联的Pod地址
步骤3:通过Service名称实现服务发现
其他Pod(如前端服务)可以直接通过“Service名称”访问后端服务,无需关心具体Pod的IP。
示例:部署一个前端Pod,验证能否访问后端服务:
# frontend-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: frontend
image: busybox:1.35
command: ["sh", "-c", "sleep 3600"] # 保持Pod运行
部署后,进入前端Pod内部测试访问:
kubectl apply -f frontend-pod.yaml
kubectl exec -it frontend -- sh # 进入前端Pod
# 在前端Pod内部,通过Service名称访问后端服务
wget -qO- backend-service:8080 # 成功返回nginx首页,说明服务发现生效
原理:CoreDNS会将解析为其ClusterIP,且解析记录会自动更新(当Service的ClusterIP变化时)。
backend-service
步骤4:跨命名空间的服务发现
如果服务部署在不同命名空间,访问时需要加上命名空间后缀:(完整域名)。
服务名称.命名空间.svc.cluster.local
示例:假设后端服务在命名空间,前端Pod在
backend-ns,则访问方式为:
frontend-ns
# 在前端Pod内部
wget -qO- backend-service.backend-ns:8080 # 简化写法(省略.svc.cluster.local)
三、特殊场景:无头服务(Headless Service)
对于有状态应用(如数据库集群、分布式系统),需要直接访问具体Pod(而非通过Service的ClusterIP负载均衡),此时可使用无头服务(不分配ClusterIP,DNS直接返回Pod的IP列表)。
示例:创建无头服务:
# headless-service.yaml
apiVersion: v1
kind: Service
metadata:
name: backend-headless
spec:
selector:
app: backend
ports:
- port: 8080
targetPort: 80
clusterIP: None # 关键:设置为None即无头服务
部署后,解析服务名称会返回所有关联Pod的IP:
# 在前端Pod内部执行DNS解析
nslookup backend-headless # 返回3个Pod的IP(因replicas=3)
四、核心组件与排查
CoreDNS:确保CoreDNS正常运行,否则无法解析Service名称:
kubectl get pods -n kube-system | grep coredns # 检查CoreDNS Pod状态
Service与Pod关联:若服务发现失败,先检查Service的标签选择器是否与Pod标签匹配:
kubectl describe service backend-service | grep Selector # 查看选择器
kubectl get pods --show-labels | grep backend # 查看Pod标签
Endpoint状态:若Service关联的Endpoint为空,说明标签匹配失败:
kubectl get endpoints backend-service # 正常应显示Pod的IP和端口
总结
K8s服务发现的核心是:
用Service为动态Pod提供固定访问点;用CoreDNS将Service名称解析为网络地址;通过标签选择器自动维护Service与Pod的关联。
无论是集群内、跨命名空间,还是有状态应用,都可以通过上述机制实现高效的服务发现。