Kubernetes 系列【18】服务:基于 DNS 的服务发现机制
文章目录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 创建客户端 Pod3.2 创建服务端 Pod3.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默认集群内部虚拟 ClusterIPNodePortClusterIP 节点宿主机端口LoadBalancerClusterIP 云厂商负载均衡公网 IPExternalName无 ClusterIPCNAME映射外部域名1.2 标准选型ClusterIP 服务集群内部业务Pod主动发起请求、调用另一业务Pod属于服务内网互相调用场景也是Kubernetes最核心、最普遍的通信方式。该场景仅存在集群内部流量、无需外网暴露因此生产环境统一首选ClusterIP类型Service。典型适用场景前后端业务微服务互相调用业务服务访问集群内部中间件MySQL、Redis、KafkagRPC微服务内网互通、多端口精细化调用ClusterIP只会生成集群内网虚拟IP外网无法直接接入天然具备良好的隔离性与安全性是内网服务的标准选型。1.3 硬编码虚拟 IP 的致命缺点如果Pod之间使用ClusterIP中的虚拟IP进行通信的话直接在代码 / 配置里写死ClusterIP地址访问服务存在以下致命问题ClusterIP 不固定Service删除重建后IP会重新分配一旦变更所有硬编码IP的业务直接访问失败。可读性差、不易维护纯数字IP无语义无法直观对应业务服务名称排查问题成本高。无法使用 SRV 记录能力多端口Service场景下不能借助命名端口自动获取端口信息需要手动维护多组地址端口。多环境迁移繁琐开发、测试、生产集群内同一服务的ClusterIP各不相同硬编码需要维护多套配置。违背 Service 抽象设计理念Service提供稳定域名层屏蔽底层变化直连IP等于舍弃这一层抽象。不利于后续架构演进服务网格(Istio)、灰度发布、流量管控等组件大多基于服务域名配置策略静态IP难以适配。故障定位难度提升日志只打印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-primary的Service集群IP为10.0.0.11、端口6379Pod内会自动注入以下环境变量REDIS_PRIMARY_SERVICE_HOST10.0.0.11REDIS_PRIMARY_SERVICE_PORT63792.1.2 致命缺陷生产禁用该机制存在无法规避的时序漏洞Service 必须提前于 Pod 创建完成。如果先启动Pod、后创建Servicekubelet已完成Pod初始化不会二次注入环境变量Pod将永远无法识别该Service。同时该方式不支持动态更新、不支持跨命名空间便捷访问、无域名可读性完全无法适配动态扩缩容、滚动更新的云原生场景。结论仅作原理了解生产环境完全弃用。2.1.3 实操演示环境变量服务发现这里我们实操验证环境变量注入的时序 BUG彻底看懂为什么不能生产用。1先创建Service再启动Pod能正常注入# 先创建服务kubectl expose deployment nginx-demo--port80--namesvc-env-demo# 临时启动测试Podkubectl run test-env1--imagebusybox--rm-it--sh在Pod内查看环境变量env|grepSVC_ENV_DEMO结果可以正常看到SVC_ENV_DEMO_SERVICE_HOST、SVC_ENV_DEMO_SERVICE_PORT。2复现致命问题先启动Pod后创建Service# 先启动Podkubectl run test-env2--imagebusybox--rm-it--sh# 新开终端、后创建服务kubectl expose deployment nginx-demo--port80--namesvc-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地址。举个外网例子你在浏览器输入www.baidu.comDNS查询翻译成180.101.49.11客户端使用IP建立网络连接DNS服务器保存一条条映射数据每一条称为一条DNS标准记录。常用标准记录清单记录类型作用说明关键特点 补充信息示例A 记录最常见域名解析到 IPv4 地址仅返回IPv4地址baidu.com → 180.101.49.11AAAA 记录域名解析到 IPv6 地址仅返回IPv6地址-CNAME 别名记录将一个域名指向另一个域名不能直接指向IP域名跳转目标只能填写域名shturl.cc/n → shturl.cc/GyF8SRV 记录重点服务定位记录通过DNS查找服务地址端口标准RFC 2782非K8s自定义返回内容优先级、权重、端口号、目标域名A记录只返回IPSRV附带端口与调度权重信息用于发现服务-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协议protocolmy-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类型Service无ClusterIP、无后端端点、无流量转发能力不支持环境变量发现、不支持四层流量转发仅能通过 DNS 服务发现访问完全依赖CoreDNS的CNAME域名映射能力实现服务调用。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:latestimagePullPolicy:Alwaysname:nginxports:-containerPort:80protocol:TCP3.2 创建服务端 Pod基于之前 Kubernetes 系列【16】服务ClusterIP 核心原理与演示案例 中部署的服务模拟被其他Pod调用。查看服务信息[rootiZ2ze60mcbemirrq6qZ ~]# kubectl get svc nginx-svc -o wideNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR nginx-svc ClusterIP 10.8.173.136 none8080/TCP 23h appnginx-web3.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 是集群私有域名仅集群 DNSCoreDNS能够识别3.3.1 进入 Pod 内部方式1直接进入Deployment下任意Podkubectlexec-itdeploy/nginx-client --sh方式2先查Pod名称精准进入单个Pod# 列出podkubectl get pods# 进入指定podkubectlexec-itnginx-client-xxxxxx-xxxxx --sh3.3.2 使用虚拟 IP 访问可直接通过虚拟IP访问/# curl 10.8.173.136:8080!DOCTYPE htmlhtmlheadtitleWelcome to nginx!/titlestylehtml{color-scheme: light dark;}body{width: 35em;margin:0auto;font-family: Tahoma, Verdana, Arial, sans-serif;}/style3.3.3 使用短服务名访问在同一命名空间Pod内部不需要填写完整长域名可以直接使用Service名称即可访问服务依托CoreDNSPod DNS搜索域 共同实现。查看/etc/resolv.confnameserver10.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 htmlhtmlheadtitleWelcome to nginx!/titlestyle2.2.5 使用 DNS 服务域名访问核心特性通用不受命名空间限制不管客户端Pod运行在哪一个命名空间都可以使用这条完整域名访问服务。查询的是A记录CoreDNS返回IP地址。所有常规客户端天然兼容curl、RestTemplate、WebClient、OpenFeign、Java程序均可直接使用底层只依赖标准A记录解析不需要支持SRV。DNS服务域名完整格式service-name.namespace.svc.cluster.local分段拆解片段含义说明service-nameService 的资源名称kubectl get svc展示的名字本例nginx-svcnamespace命名空间名称服务所在ns本例defaultsvc固定关键字代表这条域名对应Service 资源cluster.local集群域名后缀K8s集群默认根域名全局固定访问示例# Pod 内任意命名空间均可执行/# curl nginx-svc.default.svc.cluster.local:8080!DOCTYPE htmlhtmlheadtitleWelcome to nginx!/titlestyle2.2.6 使用 DNS SRV 记录访问前提条件Service端口必须配置ports.name否则CoreDNS不会生成SRV记录K8s SRV域名标准格式_端口名称._协议.service-name.ns.svc.cluster.local字段拆解片段说明_端口名称spec.ports[].name 自定义名称必须以下划线开头_协议_tcp / _udpservice-nameService名称ns命名空间svc.cluster.localK8s集群域名后缀用nslookup查询/# nslookup -typeSRV _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.localservice01008080nginx-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 -typeSRV _http-web._tcp.nginx-svc.default.svc.cluster.local❌不可直接curl _http-web._tcp.nginx-svc.default.svc.cluster.localSRV记录✅❌Service端口配置ports.nameCoreDNS生成SRV记录仅gRPC原生支持普通HTTP客户端需自行编码解析SRV仅为服务发现元数据携带优先级、权重、端口该域名无A记录JDK/操作系统标准解析器无法直接访问

相关新闻

最新新闻

日新闻

周新闻

月新闻