深入挖掘 OpenShift 内部 DNS

Jul 4, 2019 00:30 · 977 words · 2 minute read OpenShift Kubernetes DNS

OpenShift 容器云集群中有两种 DNS:

  • 外部 DNS(DNS 服务商或自己搭建的 DNS 服务器)用于解析集群中主机的 IP
  • 内部 DNS 用于内部服务之间的通讯

DNS 配置

在部署 OpenShift 时我们会为所有的节点上都安装好 Dnsmasq(监听着53端口)。

$ cat /etc/resolv.conf
# nameserver updated by /etc/NetworkManager/dispatcher.d/99-origin-dns.sh
search cluster.local paas.99cloud.home
nameserver 172.16.60.83

主机的 DNS 指向了本机上运行的 Dnsmasq。

$ cat /etc/dnsmasq.d/origin-upstream-dns.conf
server=172.16.60.90

Dnsmasq 的上游服务器指向了外部的 DNS 服务器(DNS 服务商或使用 CoreDNS 为集群提供域名解析服务)。

$ cat /etc/origin/node/resolv.conf
nameserver 172.16.60.90

而内部的 SkyDNS 也指向了外部的 DNS 服务器作为默认 DNS 域名服务器。

DNS 查询流程

OpenShift 内部使用 SkyDNS,数据存储在 etcd 中。

我们首先看一下主节点上所有监听了53端口的进程:

$ netstat -tunlp | grep 53
tcp        0      0 172.16.60.83:53         0.0.0.0:*               LISTEN      33210/dnsmasq
tcp        0      0 172.17.0.1:53           0.0.0.0:*               LISTEN      33210/dnsmasq
tcp        0      0 127.0.0.1:53            0.0.0.0:*               LISTEN      12277/openshift
tcp        0      0 10.128.0.1:53           0.0.0.0:*               LISTEN      62167/dnsmasq
tcp        0      0 0.0.0.0:8053            0.0.0.0:*               LISTEN      6392/openshift

127.0.0.1:53 就是真正的 OpenShift 内部 DNS。

Kubernetes 集群内部发布的 Service 对应的域名可以按照 service_name.name_space.svc 这个规则拼接起来。

$ curl -k https://apiserver.kube-service-catalog.svc/healthz
ok

$ oc get svc -n kube-service-catalog
NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
apiserver            ClusterIP   172.30.36.133   <none>        443/TCP   7h
controller-manager   ClusterIP   172.30.189.41   <none>        443/TCP   7h

$ nslookup apiserver.kube-service-catalog.svc
Server:		172.16.60.83
Address:	172.16.60.83#53

Name:	apiserver.kube-service-catalog.svc.cluster.local
Address: 172.30.36.133

$ nslookup apiserver.kube-service-catalog.svc 172.16.60.83
Server:		172.16.60.83
Address:	172.16.60.83#53

Name:	apiserver.kube-service-catalog.svc.cluster.local
Address: 172.30.36.133

$ nslookup apiserver.kube-service-catalog.svc 127.0.0.1
Server:		127.0.0.1
Address:	127.0.0.1#53

Name:	apiserver.kube-service-catalog.svc.cluster.local
Address: 172.30.36.133

对照着流程图,我们可以还原一个完整的 DNS 解析过程:

  1. 当访问 api server 服务健康状态的 API 时首先会查询 apiserver.kube-service-catalog.svc 这个(内部)域名对应的 IP 地址
  2. DNS 查询请求先来到主机已经配置好的 DNS 服务器,也就是 Dnsmasq
  3. Dnsmasq 随后会将查询请求向上递交给 SkyDNS,也就是集群内部的 DNS 服务器,这里能查到正确的 IP 地址

我们在管理 OpenShift 网络的 Pod 内部来查询一下 apiserver.kube-service-catalog.svc.cluster.local

$ oc get pods -n openshift-sdn
NAME        READY     STATUS    RESTARTS   AGE
ovs-7kpnv   1/1       Running   0          11h
ovs-7p75w   1/1       Running   0          11h
ovs-m6clw   1/1       Running   0          11h
sdn-7fz45   1/1       Running   0          11h
sdn-l9cq7   1/1       Running   0          11h
sdn-rmqvd   1/1       Running   0          11h

$ oc exec -it sdn-7fz45 -n openshift-sdn /bin/bash
[root@ocp-infra-1 origin]# dig apiserver.kube-service-catalog.svc.cluster.local

; <<>> DiG 9.9.4-RedHat-9.9.4-74.el7_6.1 <<>> apiserver.kube-service-catalog.svc.cluster.local
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39256
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;apiserver.kube-service-catalog.svc.cluster.local. IN A

;; ANSWER SECTION:
apiserver.kube-service-catalog.svc.cluster.local. 30 IN	A 172.30.36.133

;; Query time: 1 msec
;; SERVER: 172.16.60.83#53(172.16.60.83)
;; WHEN: Wed Jul 03 16:17:59 UTC 2019
;; MSG SIZE  rcvd: 82

为什么需要内部 DNS?

以往虚拟机中的应用程序之间通讯所使用的宿主机 IP 通常不会轻易变化。但是在容器环境中,容器启动时会被分配新的 IP 地址。编排容器的 Kubernetes 需要静态 IP,提供服务 IP 的 Service 对象就是为了实现这个目的而创建的。Service 对象的域名是不会变化的(拼接规则),借助这个内部的域名,可以实现服务之间的稳定通讯而不用管背后 Pod 本身的 IP 是否被改变。

由于 Pod 的 IP 会变化,如果我们在应用程序或者配置中指定了 IP 地址,Pod 被重新创建时应用程序并不会被主动通知 IP 已经改变。出于这些原因,我们通常使用域名来避免在应用程序中写死 IP 地址。OpenShift 的内部 DNS 使用动态 DNS,无论何时 Pod 被重建,DNS 都将使用新纪录进行更新。