理解 OpenShift 网络:Router vs Route

OpenShift 集群中,至少有三个地方需要用到 DNS:

本文就从这三点出发,解释 OpenShift 是如何实现这三种DNS功能的。

一、OpenShift 中的DNS 相关组件及其配置

openshift服务目录(理解OpenShift网络之)(1)

1.1 Pod 中的 DNS 配置

在Linux 系统上,当一个应用通过域名连接远端主机时,DNS 解析会通过系统调用来进行,比如 getaddrinfo()。和任何Linux 操作系统一样,Pod 的 DNS 定义在 resolv.conf 文件中,其示例如下:

sh-4.2$ cat /etc/resolv.conf nameserver 172.22.122.9search dev.svc.cluster.local svc.cluster.local cluster.local exampleos.com options ndots:5

其中,

域名(Domain Name)分为两种,一种是绝对域名(Absolute Domain Name,也称为 Fully-Qualified Domain Name,简称 FQDN),另一种是相对域名(Relative Domain Name,也称为 Partially Qualified Domain Name,简称PQDN)。FQDN 是完整域名,它能够唯一地在DNS名字空间中确定一个记录。比如最高级别的域名A包括子域名B它又包括子域名C,那么FQDN 是 C.B.A.,比如cs.widgetopia.edu.。 有时候我们也会使用PQDN,它是不完全的、模糊的。

FQDN 能被直接到 DNS 名字服务器中查询;而 PQDN 需要先转化为FQDN 再进行查询。其做法是将 PQDN 附加一个搜索域名(search domain)来生成一个 FQDN。在域名系统中,域名结尾是否是『.』被用来区分 FQDN 和 PQDN。比如 apple.com. 表示一个Apple公司的 FQDN,而 apple 则表示一个 PQDN,它的FQDN 可能是 apple.cs.widgetopia.edu.;apple.com 仍然是一个 PQDN,它的FQDN 可能是 apple.com.cs.widgetopia.edu.。

默认地,许多DNS 解析器如果发现被解析的域名中有任何的点(.)就把它当做一个 FQDN 来解析;如果域名中没有任何点,就把它当做 PQDN 来处理,并且会加上系统的默认domain name 和最后的点,来组成 FQDN。如果没有指定默认的 domain name (通过 domain 字段)或查询失败,则会将 search 字段的第一个值当做默认domain name,如果解析不成功,则依次往下试,直到有一个成功或者全部失败为止。

这个行为是通过 options ndots 来指定的,其默认值为1,这意味着只要被解析域名中有任何一个点(.),那么它就会被当做 FQDN,而不会附加任何 search domain,直接用来查询。OpenShift 环境中,这个值被设置为 5。这意味着,只要被解析域名中包含不超过五个点,该域名就会被当做PQDN,然后挨个使用 search domain,来组装成 FQDN 来做DNS查询。如果全部不成功过,则会尝试将它直接作为 FQDN 来解析。

因此,这某些场景中,pod 中的DNS 查询速度会降低应用的性能。解决方法主要有两种,要么直接使用 FQDN,要么减小 ndots 的值,具体请查看 Kubernetes 和 DNS 的有关文档。

1.2 Pod 所在宿主机上的 DNS 配置及服务

1.2.1 resolv.conf 文件

[root@node2 cloud-user]# cat /etc/resolv.conf # nameserver updated by /etc/NetworkManager/dispatcher.d/99-origin-dns.sh # Generated by NetworkManager search cluster.local exampleos.com nameserver 172.22.122.9

在部署环境时,会在每个节点上部署 /etc/NetworkManager/dispatcher.d/99-origin-dns.sh 文件。每当节点上的 NetworkManager 服务启动时,该文件会被运行。它的任务包括:

也就是说,宿主机上的 DNS 请求也会转到本机上的 53 端口。

1.2.2 dnsmasq 及其配置

宿主机上的 53 端口上,dnsmasq 服务在route 默认路由的所有IP的53端口上侦听。其中一个负责接受并处理宿主机上所有pod 中以及宿主机上的所有DNS查询服务。

tcp 0 0 10.128.2.1:53 0.0.0.0:* LISTEN 906/dnsmasq tcp 0 0 172.17.0.1:53 0.0.0.0:* LISTEN 906/dnsmasq tcp 0 0 172.22.122.9:53 0.0.0.0:* LISTEN 906/dnsmasq

这些 IP 地址和默认路由IP 地址是符合的:

10.128.0.0 0.0.0.0 255.252.0.0 U 0 0 0 tun0 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 172.22.122.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0 172.30.0.0 0.0.0.0 255.255.0.0 U 0 0 0 tun0

dnsmasq 服务的配置目录为 /etc/dnsmasq.d。其中有两个配置文件(具体含义请查阅有关文档):

[root@node2 dnsmasq.d]# cat origin-dns.conf no-resolv domain-needed no-negcache max-cache-ttl=1enable-dbus dns-forward-max=10000cache-size=10000bind-dynamic min-port=1024except-interface=lo # End of config

文件 origin-upstream-dns.conf 中定义了上游(upstream) DNS 名字服务器:

[root@node2 dnsmasq.d]# cat origin-upstream-dns.conf server=172.22.122.3server=172.22.122.2server=172.22.122.4

这些上游服务器的地址是从 DHCP 服务器中获取到的(我的OpenShift 环境搭建在OpenStack虚拟机中。前两个地址是OpenStack neutron 网络的 DNSmasq 地址,最后一个是单独搭建的 bind9 DNS 服务器地址)。

在早期版本中(我的OpenShift版本是 3.11),还有一个配置文件 node-dnsmasq.conf :

server=/in-addr.arpa/127.0.0.1server=/cluster.local/127.0.0.1

这意味着所有以 cluster.local 和 in-addr.arpa 结尾的域名,都会被转到 127.0.0.1:53 上被解析。而其它的解析请求,会被转到在 origin-upstream-dns.conf 中定义的上游 DNS 服务器。

我的3.11版本环境中并没有生成该文件。

从代码 https://github.com/openshift/origin/blob/master/pkg/dns/dnsmasq.go 看,OpenShift 中的 dnsmasq 在启动时会自动添加这两条记录:

openshift服务目录(理解OpenShift网络之)(2)

而 dnsIP 和 dnsDomain 应该是在 /etc/origin/node/node-config.yaml 中的如下配置:

dnsBindAddress: 127.0.0.1:53dnsDomain: cluster.local

从 dnsmasq 日志中也能看到相关记录:

Dec 3 14:10:57 dnsmasq[29595]: using nameserver 127.0.0.1#53 for domain in-addr.arpa Dec 3 14:10:57 dnsmasq[29595]: using nameserver 127.0.0.1#53 for domain cluster.local

从上面的分析可见,在 node 节点上的 dnsmasq,其实只是一个DNS 查询转发器(转到上游DNS 服务器或者本机上的 SkyDns)和结果缓存器,它本身并不保存域名的原始记录。

1.2.3 SkyDNS 及其配置

关于 SkyDNS:它是一个开源的构建在 etcd 之上的分布式服务宣告(announcement)和发现(discovery)服务。利用它,可以通过 DNS 查询来发现可用的服务。其开源社区的地址是 https://github.com/skynetservices/skydns。社区版本的 SkyDns 将记录保存在 etcd 中,在做查询时从etcd 获取数据并封装成 DNS 结果格式给客户端。

SkyDNS 的 server 部分支持被作为库文件使用,此时可以为其实现其它后端。在OpenShift 中并没有采用默认的 etcd 后端,而是基于 OpenShift API 服务实现了新的后端,其代码在https://github.com/openshift/origin/blob/master/pkg/dns/ 。SkyDns 调用 OpenShift API 服务来获取主机名、IP地址等信息,然后封装成标准 DNS 记录并返回给查询客户端。

在 127.0.0.1:53 上,包装在 openshift 进程中的 SkyDNS 在侦听。

tcp 0 0 127.0.0.1:53 0.0.0.0:* LISTEN 17182/openshift

Node 节点上的 SkyDN 要么从cache 中直接回答 DNS 查询,要么调用 OpenShift API 服务来获取数据并返回。

1.3 Master 节点上的 DNS 服务

resolv.conf 文件同Node 节点上的:

[root@master1 cloud-user]# cat /etc/resolv.conf # nameserver updated by /etc/NetworkManager/dispatcher.d/99-origin-dns.sh # Generated by NetworkManager search cluster.local haihangyun.cn exampleos.com nameserver 172.22.122.5

dnsmasq 在多个IP 地址的 53 端口上侦听,为本机上的以及本机上Pod 中的DNS查询服务:

udp 0 0 10.128.0.1:53 0.0.0.0:* 866/dnsmasq udp 0 0 172.17.0.1:53 0.0.0.0:* 866/dnsmasq udp 0 0 172.22.122.5:53 0.0.0.0:* 866/dnsmasq

和 Node 节点不同,Master 节点上有两个SkyDns 进程。一个在 127.0.0.1:53 侦听,负责本机上的集群内服务的DNS查询,因为 Master 节点同时承担 node 节点的角色:

udp 0 0 127.0.0.1:53 0.0.0.0:* 11700/openshift Dec 3 14:50:41 dnsmasq[10607]: using nameserver 127.0.0.1#53 for domain cluster.local Dec 3 14:50:41 dnsmasq[10607]: using nameserver 127.0.0.1#53 for domain in-addr.arpa

另一个是在所有网卡的 8053 端口上侦听,这是因为Master 还具有 master api 角色:

udp 0 0 0.0.0.0:8053 0.0.0.0:* 15096/openshift

对于这个 SkyDns 进程的作用尚不清楚,还需进一步研究。从已有资料上看看,所有节点上都需要安装 SkyDns,并组成一个分布式集群。因为 Master 节点上的 53 端口被另一个 SkyDns 进程占用,因此换到了端口8053。

二、DNS 查询流程

2.1 pod 内的应用通过域名访问外网服务器的DNS查询流程

流程示意图如最上面图中的 1 和 2.1 部分所示。

dnsmasq 日志:

Nov 21 11:03:44 dnsmasq[17788]: using nameserver 172.22.122.3#53 Nov 21 11:03:44 dnsmasq[17788]: using nameserver 172.22.122.2#53 Nov 21 11:03:44 dnsmasq[17788]: using nameserver 172.22.122.4#53 Nov 21 11:03:49 dnsmasq[17788]: query[A] www.sina.com from 172.22.122.13 Nov 21 11:03:49 dnsmasq[17788]: forwarded www.sina.com to 172.22.122.4 Nov 21 11:03:49 dnsmasq[17788]: forwarded www.sina.com to 172.22.122.2 Nov 21 11:03:49 dnsmasq[17788]: forwarded www.sina.com to 172.22.122.3 Nov 21 11:03:49 dnsmasq[17788]: reply spool.grid.sinaedge.com is 124.228.42.248

能看到 node 上的 dnsmasq 直接将查询请求转发给上游 DNS 名字服务器。因为存在多个名字服务器,所以是依次查询,直到成功为止。从日志看,其查询顺序和配置文件中的顺序是相反的。

2.2 Pod 内应用通过服务域名查找其IP 地址

流程示意图如上图中的 1 2.2 3 部分所示。

日志实例:

(1)从一个 pod 中 ping registry-console服务的域名 registry-console.default.svc.cluster.local。

(2)Node宿主机(IP 地址为 172.22.122.13)上的 dnsmasq 收到该查询。

(3)dnsmasq 将查询转到 127.0.0.1:53 上的 SkyDns 服务。

(4)SkyDNS 做查询。SkyDNS 能接收的域名格式:<prefix>.<service_name>.<namespace>.(svc|endpoints|pod).<base>,这意味着它支持查询服务(svc)、端点(endpoints)和 pod 的 DNS信息。

查询结果:

[root@node2 cloud-user]# nsenter -t 4216 -n dig mybank.dev.svc.cluster.local ;; QUESTION SECTION: ;mybank.dev.svc.cluster.local. IN A ;; ANSWER SECTION: mybank.dev.svc.cluster.local. 30 IN A 172.30.162.172;; Query time: 1 msec ;; SERVER: 172.22.122.9#53(172.22.122.9) ;; WHEN: Mon Dec 03 11:43:01 CST 2018;; MSG SIZE rcvd: 62

dnsmasq 日志:

Dec 3 14:19:44 dnsmasq[29595]: query[A] mybank.dev.svc.cluster.local from 10.128.2.128 Dec 3 14:19:44 dnsmasq[29595]: forwarded mybank.dev.svc.cluster.local to 127.0.0.1 Dec 3 14:19:44 dnsmasq[29595]: reply mybank.dev.svc.cluster.local is 172.30.162.172

(5)其它实验:查询服务的所有端点

查询结果:

[root@node2 cloud-user]# nsenter -t 4216 -n dig jenkins.dev.endpoints.cluster.local ;; QUESTION SECTION: ;jenkins.dev.endpoints.cluster.local. IN A ;; ANSWER SECTION:jenkins.dev.endpoints.cluster.local. 30 IN A 10.128.2.81 jenkins.dev.endpoints.cluster.local. 30 IN A 10.131.1.70

dnsmasq 日志:

Dec 3 14:20:48 dnsmasq[29595]: query[A] jenkins.dev.endpoints.cluster.local from 10.128.2.128 Dec 3 14:20:48 dnsmasq[29595]: forwarded jenkins.dev.endpoints.cluster.local to 127.0.0.1 Dec 3 14:20:48 dnsmasq[29595]: reply jenkins.dev.endpoints.cluster.local is 10.128.2.81 Dec 3 14:20:48 dnsmasq[29595]: reply jenkins.dev.endpoints.cluster.local is 10.131.1.70

(6)查询 pod

待查询的pod域名的格式为 <IP_with_dashes>.<namespace>.pod.<base>,SkyDns 会返回其IP 地址,但我没明白这么做的场景和价值,也许是确认pod是否存在?

查询结果:

[root@node2 cloud-user]# nsenter -t 4216 -n dig 172-30-162-172.dev .pod.cluster.local;; QUESTION SECTION: ;172-30-162-172.dev.pod.cluster.local. IN A ;; ANSWER SECTION: 172-30-162-172.dev.pod.cluster.local. 30 IN A 172.30.162.172;; Query time: 1 msec ;; SERVER: 172.22.122.9#53(172.22.122.9) ;; WHEN: Mon Dec 03 13:32:05 CST 2018;; MSG SIZE rcvd: 70

dnsmasq 日志:

Dec 3 14:22:24 dnsmasq[29595]: query[A] 172-30-162-172.dev.pod.cluster.local from 10.128.2.128 Dec 3 14:22:24 dnsmasq[29595]: forwarded 172-30-162-172.dev.pod.cluster.local to 127.0.0.1 Dec 3 14:22:24 dnsmasq[29595]: reply 172-30-162-172.dev.pod.cluster.local is 172.30.162.172

(7)对比 FQDN 和 PQDN

这个 PQDN 被加上了搜索域名再进行查询,能返回正确的IP地址:

[root@node2 cloud-user]# nsenter -t 4216 -n ping mybank.dev.svc PING mybank.dev.svc.cluster.local (172.30.162.172) 56(84) bytes of data.

而这个 FQDN 被直接做DNS查询,结果查询失败,未能获取IP地址:

[root@node2 cloud-user]# nsenter -t 4216 -n ping mybank.dev.svc.ping: mybank.dev.svc.: Name or service not known

2.3 从外网通过服务域名访问pod 中运行的服务

openshift服务目录(理解OpenShift网络之)(3)

可以看出,该过程中只涉及到外部DNS将服务的公共域名解析为 OpenShift Router 所在节点的公网地址,后面 HAProxy 作为代理,直接通过 IP 访问pod,并将结果返回客户端。

参考文档:

作者:Sammy Liu

,