诊断 Service DNS 访问

KubernetesBeginner
立即练习

介绍

上一个实验展示了 Service 如何为 Pod 提供稳定的集群内入口点。Kubernetes 还会为每个 Service 分配一个 DNS 名称。同一命名空间内的 Pod 通常可以使用简短的 Service 名称,但不同命名空间中的 Pod 通常需要使用包含 Service 命名空间的完整名称。

在本实验中,你将创建一个 Web Service,测试同命名空间内的 DNS 访问,重现跨命名空间短名称解析失败的问题,通过 Service 的完全限定域名(FQDN)修复请求,并检查相关对象,以诊断 DNS 问题究竟是源于 Service、端点(Endpoint)还是 CoreDNS 本身。

准备 Web Service

在此步骤中,你将创建目标 Service,供后续的 DNS 测试进行解析。由于本实验的重点是 DNS 命名而非应用程序部署,因此工作负载非常简单。

从 /home/labex/project 开始:

mkdir -p /home/labex/project
cd /home/labex/project

检查 Kubernetes API 服务器是否可达。如果虚拟机刚启动且节点尚未就绪,请等待几秒钟后再运行该命令。

kubectl get nodes --request-timeout=5s

预期输出包含:

NAME         STATUS   ROLES           AGE   VERSION
labex-v135   Ready    control-plane   ...   v1.35...

等待集群网络就绪。本实验涉及 Pod 之间的流量传输,因此在进行 DNS 访问测试前,必须确保 Pod 网络运行正常。

kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s

预期输出:

pod/calico-node-... condition met

创建一个名为 ckad-prep-dns 的命名空间,并将其设置为当前命名空间:

kubectl create namespace ckad-prep-dns
kubectl config set-context --current --namespace=ckad-prep-dns

创建一个 Deployment 并通过名为 dns-web 的 Service 将其暴露出来:

kubectl create deployment dns-web --image=nginx:1.25 --port=80
kubectl expose deployment dns-web --port=80 --target-port=80

等待 Web Pod 可用:

kubectl rollout status deployment/dns-web --timeout=120s

检查 Service 及其 EndpointSlice。Service 是稳定的 DNS 目标;EndpointSlice 显示了当前由 Service 选择的后端 Pod 地址。

kubectl get service dns-web
kubectl get endpointslice -l kubernetes.io/service-name=dns-web

预期输出包含:

NAME      TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
dns-web   ClusterIP   ...          <none>        80/TCP    ...

NAME          ADDRESSTYPE   PORTS   ENDPOINTS   AGE
dns-web-...   IPv4          80      ...         ...

解析同一命名空间内的 Service

在此步骤中,你将在与 Service 相同的命名空间中创建一个客户端 Pod,并使用短 Service 名称。这是最简单的 Service DNS 情况。

创建一个持续运行的客户端 Pod:

kubectl run same-ns-client --image=busybox:1.36 --restart=Never --command -- sleep 3600

等待客户端 Pod 就绪:

kubectl wait --for=condition=Ready pod/same-ns-client --timeout=120s

预期输出:

pod/same-ns-client condition met

在客户端 Pod 内部使用 nslookup 解析短 Service 名称 dns-web。由于客户端 Pod 和 Service 处于同一命名空间,短名称在此处有效。BusyBox 的 nslookup 可能会针对其他搜索后缀打印额外的 NXDOMAIN 行,并在显示正确地址后返回非零退出码,因此命令中使用了 || true,你应该关注 Name 和 Address 行。

kubectl exec same-ns-client -- sh -c 'nslookup dns-web || true'

预期输出包含:

Name:      dns-web.ckad-prep-dns.svc.cluster.local
Address: ...

现在向该 Service 的短名称发送 HTTP 请求:

kubectl exec same-ns-client -- wget -qO- http://dns-web | grep '<title>Welcome to nginx'

预期输出:

<title>Welcome to nginx!</title>

诊断跨命名空间短名称解析失败

在此步骤中,你将在不同的命名空间中创建一个客户端 Pod,并重现一个常见的 DNS 错误。短 Service 名称首先会相对于客户端 Pod 的命名空间进行解析,因此当客户端在另一个命名空间中运行时,dns-web 将无法在 ckad-prep-dns 中找到该 Service。

创建第二个命名空间并在其中创建一个客户端 Pod:

kubectl create namespace ckad-prep-dns-client
kubectl run remote-client --namespace ckad-prep-dns-client --image=busybox:1.36 --restart=Never --command -- sleep 3600
kubectl wait --namespace ckad-prep-dns-client --for=condition=Ready pod/remote-client --timeout=120s

预期输出包含:

namespace/ckad-prep-dns-client created
pod/remote-client created
pod/remote-client condition met

尝试从远程命名空间解析短名称。使用 || true 可以让实验继续进行,因为这种失败是预期的。

kubectl exec --namespace ckad-prep-dns-client remote-client -- nslookup dns-web || true

预期输出包含类似以下的失败信息:

** server can't find dns-web...

现在解析完全限定的 Service 名称。完整模式为 <service>.<namespace>.svc.cluster.local。

kubectl exec --namespace ckad-prep-dns-client remote-client -- nslookup dns-web.ckad-prep-dns.svc.cluster.local

预期输出包含:

Name:      dns-web.ckad-prep-dns.svc.cluster.local
Address 1: ...

使用相同的完全限定名称进行 HTTP 访问:

kubectl exec --namespace ckad-prep-dns-client remote-client -- wget -qO- http://dns-web.ckad-prep-dns.svc.cluster.local | grep '<title>Welcome to nginx'

预期输出:

<title>Welcome to nginx!</title>

检查 DNS 故障排查路径

在此步骤中,你将检查有助于区分 DNS 命名问题与 Service 或后端问题的对象。实用的排查顺序是:确认 Service 名称和命名空间,确认其拥有端点,然后确认 CoreDNS 是否可用。

首先,检查其所在命名空间中的 Service:

kubectl get service dns-web --namespace ckad-prep-dns

预期输出包含:

NAME      TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
dns-web   ClusterIP   ...          <none>        80/TCP    ...

接下来,检查 EndpointSlice。如果 DNS 解析成功但 HTTP 流量失败,缺少端点是一个常见原因。

kubectl get endpointslice --namespace ckad-prep-dns -l kubernetes.io/service-name=dns-web

预期输出包含至少一个端点:

NAME          ADDRESSTYPE   PORTS   ENDPOINTS   AGE
dns-web-...   IPv4          80      ...         ...

检查 CoreDNS Pod。CoreDNS 是响应 Pod DNS 查询的集群 DNS 服务。

kubectl get pods --namespace kube-system -l k8s-app=kube-dns

预期输出包含正在运行的 CoreDNS Pod:

NAME                       READY   STATUS    RESTARTS   AGE
coredns-...                1/1     Running   ...        ...

最后,检查远程客户端的 DNS 搜索配置。search 行解释了为什么在你使用完整名称之前,短名称 dns-web 会在客户端命名空间中进行搜索。

kubectl exec --namespace ckad-prep-dns-client remote-client -- cat /etc/resolv.conf

预期输出包含类似于以下的搜索路径:

search ckad-prep-dns-client.svc.cluster.local svc.cluster.local cluster.local
nameserver ...

总结

你通过一个适合初学者的序列练习了诊断 Kubernetes Service DNS 访问的方法。你创建了一个 Service,确认了同命名空间客户端可以使用短 Service 名称,重现了跨命名空间短名称解析失败的问题,通过完整的 Service DNS 名称修复了该问题,并检查了 Service、EndpointSlice、CoreDNS Pod 以及客户端 DNS 搜索路径,从而理解了 DNS 访问问题的来源。