服务发现与 CoreDNS
本教程共 65 篇 · 第 27 篇 · 更新于 2026-08-14 · 约 13 分钟阅读
本节目标:理解集群内为什么要用 DNS 做服务发现,CoreDNS 怎么为每个 Service 和 Pod 生成记录,以及跨命名空间访问时 DNS 名字怎么写。
前面两章讲了 Service 用虚拟 IP 兜住一组 Pod。但还有个问题:前端怎么知道这个 Service 的 IP 是多少?总不能写死在代码里吧。
Kubernetes 给的答案很干脆:用 DNS 名字,别用 IP。
27-1 服务发现的两种老办法
在 Kubernetes 里,集群内的客户端发现 Service 主要有两条路:环境变量和 DNS。
环境变量是早期方案。当 Pod 被调度到节点上时,kubelet 会往 Pod 里塞一组环境变量,比如有个叫 redis-primary 的服务,Pod 里就能看到:
REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
REDIS_PRIMARY_SERVICE_PORT=6379
但环境变量有个硬伤:必须先创建 Service,客户端 Pod 才读得到。客户端后于 Service 启动才管用,顺序反了就什么都没有。
DNS 就没这问题。Service 一创建,DNS 记录就生成,什么时候查都行。所以现在几乎都走 DNS。
Tip新应用一律用 DNS 做服务发现,别依赖环境变量。环境变量的顺序依赖坑过不少人。
27-2 CoreDNS 在背后干了啥
CoreDNS 是 Kubernetes 集群默认的 DNS 服务(插件形式安装)。它干的事很单纯:盯着 API 服务器里新增、变更的 Service 和 Pod,然后给每个对象生成对应的 DNS 记录。
只要集群开了 DNS(现在基本都开),所有 Pod 天生就能用名字解析 Service。你不用操心 CoreDNS 怎么配,它和 kubelet 配合,把 Pod 的 /etc/resolv.conf 写好。
一个典型 Pod 里的 DNS 配置长这样:
nameserver 10.32.0.10
search <namespace>.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
10.32.0.10 就是 CoreDNS 自己的集群 IP。search 那行决定了你只写短名字时,系统会帮你在哪些域里补全查找。
27-3 Service 的 DNS 名字怎么拼
普通(非无头)Service 会被分配一条 A 记录(或 AAAA 记录,对应 IPv6),名字格式是:
my-svc.my-namespace.svc.cluster-domain.example
它解析到的就是 Service 的集群 IP。
举个具体例子。你在 my-ns 命名空间里有个 my-service,那它的完整域名就是 my-service.my-ns.svc.cluster.local。
27-4 在同命名空间 vs 跨命名空间
DNS 查询的结果,取决于发起查询的 Pod 在哪个命名空间。默认情况下,不写命名空间的话,查询被限制在 Pod 自己所在的命名空间。
假设 test 命名空间有个 Pod,想访问 prod 命名空间里的 data 服务:
- 只查
data→ 查的是test命名空间,找不到,返回空。 - 查
data.prod→ 指定了命名空间,能找到。
所以跨命名空间访问,要把命名空间带上。同命名空间内可以偷懒只写服务名,因为 search 列表会帮你补成 data.test.svc.cluster.local。
Note默认集群域名是
cluster.local,所以标准后缀是svc.cluster.local。有些发行版会改这个域,看到不一样的别慌。
27-5 无头服务的 DNS 不一样
前面提过无头服务(clusterIP: None)没有虚拟 IP。它的 DNS A 记录不解析到单个 IP,而是解析到所有后端 Pod 的 IP 集合。
客户端拿到这一组 IP,自己挑一个连(或者按标准轮询)。这给有状态集这类需要直连特定 Pod 的场景开了口子。
27-6 Pod 自己也有 DNS 名字
不光 Service,Pod 也能有 DNS 名字。默认情况下,Pod 的 FQDN 形如:
<pod-ip-with-dashes>.<namespace>.pod.cluster.local
比如 IP 是 172.17.0.3、在 default 命名空间,名字就是 172-17-0-3.default.pod.cluster.local。
你也可以给 Pod 指定 hostname 和 subdomain 字段,让它拥有更好认的主机名。比如一个 Pod 的 hostname 是 busybox-1、subdomain 是 busybox-subdomain,再加一个同名的无头服务,它就有了一个体面的名字:
busybox-1.busybox-subdomain.my-namespace.svc.cluster.local
27-7 命名端口还有 SRV 记录
Service 里给端口起了名字(比如 http),CoreDNS 还会生成 SRV 记录。你可以用 DNS SRV 查询 _http._tcp.my-service.my-ns,一次性拿到端口号和 IP。
这对服务想”自报家门”说”我监听哪个端口”很有用,调用方不用硬编码端口号。
27-8 每个 Pod 的 DNS 策略
Pod 的 DNS 行为由 dnsPolicy 字段控制,支持四种:
Default:从所在节点继承 DNS 配置(注意,它不是默认值)。ClusterFirst:集群域名之外的查询转发给上游 DNS。这是默认策略。ClusterFirstWithHostNet:用 hostNetwork 跑的 Pod 必须设这个,否则会回退成 Default。None:完全忽略集群预设,DNS 全靠你写的dnsConfig提供。
Warning没显式写
dnsPolicy时,走的是ClusterFirst而不是Default。别望文生义,这俩名字容易误导。
27-9 自定义 DNS 配置
当 dnsPolicy: None 时,你必须在 dnsConfig 里把 DNS 服务器、搜索域、选项配齐。就算不是 None,也可以叠加自定义:
apiVersion: v1
kind: Pod
metadata:
name: dns-example
spec:
dnsPolicy: None
dnsConfig:
nameservers:
- 192.0.2.1
searches:
- ns1.svc.cluster.local
- my.dns.search.suffix
options:
- name: ndots
value: "2"
- name: edns0
containers:
- name: test
image: nginx
Kubernetes 本身对 DNS 配置的限制是:搜索域最多 32 个、总长度不超过 2048 字符。超出会出问题。
小结
服务发现的核心是:别记 IP,记名字。CoreDNS 在后台监听 Service 变化,自动维护 DNS 记录。同命名空间用短名字,跨命名空间带上命名空间前缀。
无头服务把 DNS 指向一组 Pod IP,给有状态场景留了门。Pod 自身也能有 DNS 名字,命名端口还有 SRV 记录可用。
下章进入七层路由:用 Ingress 把集群外的 HTTP/HTTPS 流量,按域名和路径精准分发到内部 Service。