Kubernetes 系列【18】服务:基于 DNS 的服务发现机制
2026/7/15 19:31:52 网站建设 项目流程

文章目录

  • 1. Pod 之间互相通信
    • 1.1 四种服务的访问入口
    • 1.2 标准选型:ClusterIP 服务
    • 1.3 硬编码虚拟 IP 的致命缺点
    • 1.4 生产最佳实践
  • 2. 服务发现机制
    • 2.1 环境变量服务发现
      • 2.1.1 变量命名规则
      • 2.1.2 致命缺陷(生产禁用)
      • 2.1.3 实操演示:环境变量服务发现
    • 2.2 DNS 服务发现(生产标准推荐)
      • 2.2.1 DNS 基础
      • 2.2.2 域名访问规则
      • 2.2.3 DNS SRV 记录
      • 2.2.3 ExternalName 服务特殊说明
  • 3. 案例演示
    • 3.1 创建客户端 Pod
    • 3.2 创建服务端 Pod
    • 3.3 测试验证
      • 3.3.1 进入 Pod 内部
      • 3.3.2 使用虚拟 IP 访问
      • 3.3.3 使用短服务名访问
      • 2.2.5 使用 DNS 服务域名访问
      • 2.2.6 使用 DNS SRV 记录访问
    • 3.4 访问方式总结:

1. Pod 之间互相通信

1.1 四种服务的访问入口

Kubernetes中,不同类型的Service对应不同的访问入口与流量场景。我们先回顾四种Service类型的默认访问入口:

Service 类型默认访问入口
ClusterIP(默认)集群内部虚拟 ClusterIP
NodePortClusterIP + 节点宿主机端口
LoadBalancerClusterIP + 云厂商负载均衡公网 IP
ExternalName无 ClusterIP,CNAME映射外部域名

1.2 标准选型:ClusterIP 服务

集群内部业务Pod主动发起请求、调用另一业务Pod,属于服务内网互相调用场景,也是Kubernetes最核心、最普遍的通信方式。

该场景仅存在集群内部流量、无需外网暴露,因此生产环境统一首选ClusterIP类型Service

典型适用场景:

  • 前后端业务微服务互相调用
  • 业务服务访问集群内部中间件(MySQLRedisKafka
  • gRPC微服务内网互通、多端口精细化调用

ClusterIP只会生成集群内网虚拟IP,外网无法直接接入,天然具备良好的隔离性与安全性,是内网服务的标准选型。

1.3 硬编码虚拟 IP 的致命缺点

如果Pod之间使用ClusterIP中的虚拟IP进行通信的话,直接在代码 / 配置里写死ClusterIP地址访问服务,存在以下致命问题:

  1. ClusterIP 不固定Service删除重建后IP会重新分配,一旦变更,所有硬编码IP的业务直接访问失败。
  2. 可读性差、不易维护:纯数字IP无语义,无法直观对应业务服务名称,排查问题成本高。
  3. 无法使用 SRV 记录能力:多端口Service场景下,不能借助命名端口自动获取端口信息,需要手动维护多组地址端口。
  4. 多环境迁移繁琐:开发、测试、生产集群内同一服务的ClusterIP各不相同,硬编码需要维护多套配置。
  5. 违背 Service 抽象设计理念Service提供稳定域名层屏蔽底层变化,直连IP等于舍弃这一层抽象。
  6. 不利于后续架构演进:服务网格(Istio)、灰度发布、流量管控等组件大多基于服务域名配置策略,静态IP难以适配。
  7. 故障定位难度提升:日志只打印IP,需要额外对照表才能关联到具体Service

1.4 生产最佳实践

生产环境严格禁止硬编码ClusterIP虚拟IP,统一使用Service域名 +DNS服务发现实现内网通信,依托K8s原生DNS能力,实现服务无感变更、稳定寻址、可扩展、可治理的云原生通信架构。

2. 服务发现机制

Kubernetes集群中,Pod想要主动访问Service服务,必须依靠服务发现机制获取Service地址与端口。K8s原生提供两种服务发现方式:环境变量服务发现、DNS服务发现,其中DNS为生产唯一推荐方案。

服务发现方式优点致命缺点生产适用性
环境变量无需DNS组件、解析速度快强时序依赖、无法动态更新、跨NS访问繁琐❌ 完全禁用
DNS动态更新、无时序限制、支持跨NS、可读性强依赖CoreDNS组件✅ 全局唯一推荐

2.1 环境变量服务发现

环境变量是K8s最基础、最原始的服务发现方式。Pod启动初始化阶段,节点上的kubelet组件会自动扫描当前命名空间下所有已存在的活跃 Service,并将Service的集群IP、端口等信息注入为Pod环境变量。

2.1.1 变量命名规则

环境变量遵循固定大写命名规则:

  • 服务名_SERVICE_HOST(存储Service集群IP
  • 服务名_SERVICE_PORT(存储Service业务端口)。

示例:命名空间下存在名为redis-primaryService,集群IP10.0.0.11、端口6379Pod内会自动注入以下环境变量:

REDIS_PRIMARY_SERVICE_HOST=10.0.0.11REDIS_PRIMARY_SERVICE_PORT=6379

2.1.2 致命缺陷(生产禁用)

该机制存在无法规避的时序漏洞:Service 必须提前于 Pod 创建完成。如果先启动Pod、后创建Servicekubelet已完成Pod初始化,不会二次注入环境变量,Pod将永远无法识别该Service

同时该方式不支持动态更新、不支持跨命名空间便捷访问、无域名可读性,完全无法适配动态扩缩容、滚动更新的云原生场景。

结论:仅作原理了解,生产环境完全弃用

2.1.3 实操演示:环境变量服务发现

这里我们实操验证环境变量注入的时序 BUG,彻底看懂为什么不能生产用。

1)先创建Service,再启动Pod(能正常注入)

# 先创建服务kubectl expose deployment nginx-demo--port=80--name=svc-env-demo# 临时启动测试Podkubectl run test-env1--image=busybox--rm-it--sh

Pod内查看环境变量:

env|grepSVC_ENV_DEMO

结果:可以正常看到SVC_ENV_DEMO_SERVICE_HOSTSVC_ENV_DEMO_SERVICE_PORT

2)复现致命问题:先启动Pod,后创建Service

# 先启动Podkubectl run test-env2--image=busybox--rm-it--sh# 新开终端、后创建服务kubectl expose deployment nginx-demo--port=80--name=svc-env-late

回到test-env2容器内查询:

env|grepSVC_ENV_LATE

结果为空,永远不会补发环境变量。

这就是生产绝对禁用的核心原因:服务动态创建、动态扩容场景完全不兼容

2.2 DNS 服务发现(生产标准推荐)

DNS服务发现是K8s官方标准、生产唯一通用的服务发现方案。集群部署CoreDNS组件后,所有Pod会默认使用集群内置DNS服务器,通过域名解析自动获取Service地址,全程动态感知资源变更,无时序限制、无配置硬编码。

2.2.1 DNS 基础

DNSDomain Name System,域名系统),可以理解成互联网/集群内部的「电话簿」。计算机网络通信只能识别一串数字IP地址,人类很难记忆大量IPDNS把好记的域名翻译成机器能识别的IP地址。

举个外网例子:

  1. 你在浏览器输入www.baidu.com
  2. DNS查询,翻译成180.101.49.11
  3. 客户端使用IP建立网络连接

DNS服务器保存一条条映射数据,每一条称为一条DNS标准记录。

常用标准记录清单:

记录类型作用说明关键特点 & 补充信息示例
A 记录(最常见)域名解析到 IPv4 地址仅返回IPv4地址baidu.com → 180.101.49.11
AAAA 记录域名解析到 IPv6 地址仅返回IPv6地址-
CNAME 别名记录将一个域名指向另一个域名(不能直接指向IP域名跳转,目标只能填写域名shturl.cc/n → shturl.cc/GyF8
SRV 记录(重点)服务定位记录,通过DNS查找服务地址+端口标准:RFC 2782,非K8s自定义
返回内容:优先级、权重、端口号、目标域名
A记录只返回IP;SRV附带端口与调度权重信息,用于发现服务
-
MX 记录邮件解析专用指定域名对应的邮件服务器地址-
TXT 记录存储自定义文本信息常用于域名所有权验证、DKIM邮件签名等-

Kubernetes集群内也运行DNS服务,默认组件为CoreDNS。集群内Pod不再需要硬编码各个服务的IP,只需要填写Service域名,请求发送给CoreDNS,解析得到Service 的ClusterIP,完成通信。

2.2.2 域名访问规则

K8s内置标准化域名体系,区分同命名空间、跨命名空间访问,适配所有业务场景:

  • 同命名空间访问:直接使用Service短名称,示例:my-service

  • 跨命名空间访问:服务名 + 命名空间,示例:my-service.my-ns

  • 完整 FQDN 域名(全局通用)my-service.my-ns.svc.cluster.local,集群内所有Pod均可解析,无命名空间限制

2.2.3 DNS SRV 记录

普通A/AAAA记录只能拿到IP,没有端口,很多服务不跑标准默认端口,光有IP无法连接。

SRVService Location Record服务定位记录,可以通过DNS,直接查到「某个服务 = 域名:端口」,同时携带调度策略。

针对配置了命名端口ServiceK8s会自动生成SRV解析记录,不仅可以解析服务IP,还能精准获取对应端口号,适配多端口Service精准访问场景。

SRV域名固定格式:

_{端口名称}._{协议}.{服务名}.{命名空间}

_http._tcp.my-service.my-ns解析:

  • _httpports里的name名称
  • _tcp:协议protocol
  • my-serviceService名称
  • my-ns:命名空间

假设业务Pod想要调用http接口:

  • 普通方式地址:my-service.my-ns.svc.cluster.local:9090(端口写死)
  • SRV方式:程序解析_http._tcp.my-service.my-ns,自动获取端口

多端口Service YAML示例:

apiVersion:v1kind:Servicemetadata:name:my-servicenamespace:my-nsspec:selector:app:my-appports:-name:http# ✅ 端口命名,生成SRV记录port:80targetPort:8080protocol:TCP-name:grpc# ✅ 第二个命名端口port:9090targetPort:9090protocol:TCP

解析格式示例:查询_http._tcp.my-service.my-ns,可同时返回服务IP、业务端口、协议类型,实现精细化服务发现。

2.2.3 ExternalName 服务特殊说明

ExternalName类型ServiceClusterIP、无后端端点、无流量转发能力,不支持环境变量发现、不支持四层流量转发,仅能通过 DNS 服务发现访问,完全依赖CoreDNSCNAME域名映射能力实现服务调用。

3. 案例演示

3.1 创建客户端 Pod

创建一个测试的nginx-client,模拟需要调用其他Pod进行业务操作:

apiVersion:apps/v1kind:Deploymentmetadata:name:nginx-clientnamespace:defaultspec:replicas:1selector:matchLabels:app:nginx-clienttemplate:metadata:labels:app:nginx-clientspec:containers:-image:'nginx:latest'imagePullPolicy:Alwaysname:nginxports:-containerPort:80protocol:TCP

3.2 创建服务端 Pod

基于之前 Kubernetes 系列【16】服务:ClusterIP 核心原理与演示案例 中部署的服务,模拟被其他Pod调用。

查看服务信息:

[root@iZ2ze60mcbemirrq6qZ ~]# kubectl get svc nginx-svc -o wideNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR nginx-svc ClusterIP 10.8.173.136 <none>8080/TCP 23h app=nginx-web

3.3 测试验证

提示:不要直接在宿主机服务器进行访问操作,K8s 集群内域名 *.svc.cluster.local 由 CoreDNS 负责解析,容器 /etc/resolv.conf 的 nameserver 指向集群内部 CoreDNS Service IP ,宿主机的 /etc/resolv.conf 是操作系统默认 DNS(运营商 DNS、阿里云 DNS 等),外部公共 DNS 完全不认识 svc.cluster.local 这个域名后缀
请求发出去后,外部 DNS 直接返回:域名不存在 ❌
简单一句话:
*.svc.cluster.local 是集群私有域名,仅集群 DNS(CoreDNS)能够识别

3.3.1 进入 Pod 内部

方式1直接进入Deployment下任意Pod

kubectlexec-itdeploy/nginx-client --sh

方式2先查Pod名称,精准进入单个Pod

# 列出podkubectl get pods# 进入指定podkubectlexec-itnginx-client-xxxxxx-xxxxx --sh

3.3.2 使用虚拟 IP 访问

可直接通过虚拟IP访问:

/# curl 10.8.173.136:8080<!DOCTYPE html><html><head><title>Welcome to nginx!</title><style>html{color-scheme: light dark;}body{width: 35em;margin:0auto;font-family: Tahoma, Verdana, Arial, sans-serif;}</style>

3.3.3 使用短服务名访问

在同一命名空间Pod内部,不需要填写完整长域名,可以直接使用Service名称即可访问服务,依托CoreDNS+Pod DNS搜索域 共同实现。

查看/etc/resolv.conf

nameserver10.96.0.10 search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5

关键字段

  • nameserver 10.96.0.10:指向集群CoreDNS的固定Service IP,所有域名请求发送给集群内部DNS
  • search搜索域列表:程序发起域名查询nginx-svc,操作系统DNS解析器会自动依次拼接搜索域尝试解析,拼接第一个nginx-svc.default.svc.cluster.local,命中,查询A记录,返回Service地址,终止后续搜索匹配
  • ndots:5:域名中点.数量≥5,直接当作完整域名查询,不追加搜索域,优化解析逻辑。

访问示例:

/# curl nginx-svc:8080<!DOCTYPE html><html><head><title>Welcome to nginx!</title><style>

2.2.5 使用 DNS 服务域名访问

核心特性:

  • 通用不受命名空间限制:不管客户端Pod运行在哪一个命名空间,都可以使用这条完整域名访问服务。
  • 查询的是A记录:CoreDNS返回IP地址。
  • 所有常规客户端天然兼容:curlRestTemplateWebClientOpenFeignJava程序均可直接使用,底层只依赖标准A记录解析,不需要支持SRV

DNS服务域名完整格式:

<service-name>.<namespace>.svc.cluster.local

分段拆解

片段含义说明
service-nameService 的资源名称kubectl get svc展示的名字,本例:nginx-svc
namespace命名空间名称服务所在ns,本例:default
svc固定关键字代表这条域名对应Service 资源
cluster.local集群域名后缀K8s集群默认根域名,全局固定

访问示例:

# Pod 内任意命名空间均可执行/# curl nginx-svc.default.svc.cluster.local:8080<!DOCTYPE html><html><head><title>Welcome to nginx!</title><style>

2.2.6 使用 DNS SRV 记录访问

前提条件:Service端口必须配置ports.name,否则CoreDNS不会生成SRV记录

K8s SRV域名标准格式:

_<端口名称>._<协议>.<service-name>.<ns>.svc.cluster.local

字段拆解

片段说明
_端口名称spec.ports[].name 自定义名称,必须以下划线开头
_协议_tcp / _udp
service-nameService名称
ns命名空间
svc.cluster.localK8s集群域名后缀

nslookup查询:

/# nslookup -type=SRV _http-web._tcp.nginx-svc.default.svc.cluster.localServer:10.96.0.10 Address:10.96.0.10:53 _http-web._tcp.nginx-svc.default.svc.cluster.localservice=01008080nginx-svc.default.svc.cluster.local

不能直接curl SRV域名:

# ❌ 错误写法,一定会报域名解析失败curl_http._tcp.nginx-svc.default.svc.cluster.local:8080

原因:

  • _http._tcp.xxx这条域名只有SRV记录,没有A记录。
  • 操作系统 /JDK默认域名解析只会查询A记录,找不到记录直接抛出UnknownHostException

3.4 访问方式总结:

访问方式访问示例DNS记录类型跨Namespace可用是否可直接curl访问核心前提适用客户端关键说明
1. ClusterIP虚拟IP访问curl 10.8.173.136:8080无DNS,直接IP知道Service固定ClusterIP全部客户端IP硬编码,不推荐生产;ClusterIP仅集群内部可达,集群重建IP会变化
2. 短服务名访问curl nginx-svc:8080A记录❌(仅限同namespace)Pod与Service同命名空间;依赖resolv.conf search搜索域全部客户端CoreDNS自动补齐完整域名;跨命名空间不能使用短名称
3. 标准完整DNS域名访问curl nginx-svc.default.svc.cluster.local:8080A记录Service资源正常存在,Endpoints不为空全部客户端(curl、RestTemplate、OpenFeign、WebClient等)生产微服务主流推荐方式,通用稳定,基于标准A记录解析
4. SRV记录查询查询:nslookup -type=SRV _http-web._tcp.nginx-svc.default.svc.cluster.local
❌不可直接curl _http-web._tcp.nginx-svc.default.svc.cluster.local
SRV记录Service端口配置ports.name;CoreDNS生成SRV记录仅gRPC原生支持;普通HTTP客户端需自行编码解析SRV仅为服务发现元数据(携带优先级、权重、端口);该域名无A记录,JDK/操作系统标准解析器无法直接访问

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询