理解OpenShift(2):网络之 DNS(域名服务)
理解OpenShift(1):网络之 Router 和 Route
理解OpenShift(5):从 Docker Volume 到 OpenShift Persistent Volume
** 本文基于 OpenShift 3.11,Kubernetes 1.11 进行测试 ***
OpenShift 集群中,至少有三个地方需要用到 DNS:
- 一是Pod 中的应用通过域名访问外网的时候,需要DNS来解析外网的域名
- 二是在集群内部(pod 中或者宿主机上)通过服务的域名来访问集群内服务的时候,这也是通常所说的服务发现功能,需要通过服务域名来先发现(获取其IP地址)再使用该服务
- 三是从集群外部通过域名访问部署在OpenShift pod 中的服务的时候,需要DNS来解析服务的外网域名
本文就从这三点出发,解释 OpenShift 是如何实现这三种DNS功能的。
1. OpenShift 中的DNS 相关组件及其配置
1.1 Pod 中的 DNS 配置
在Linux 系统上,当一个应用通过域名连接远端主机时,DNS 解析会通过系统调用来进行,比如 getaddrinfo()。
和任何Linux 操作系统一样,Pod 的 DNS 定义在 resolv.conf 文件中,其示例如下:
sh-4.2$ cat /etc/resolv.conf
nameserver 172.22.122.9
search dev.svc.cluster.local svc.cluster.local cluster.local exampleos.com
options ndots:
其中,
- nameserver 字段是 pod 所在的宿主机的主网卡的IP 地址。也就是说 pod 中发起的所有DNS 查询请求都会被转发到运行在宿主机的 53 端口上的DNS服务器上。
- search 字段指定当解析一个非FQDN域名时被附加的搜索域(search domain)列表。其解释如下:
- options ndots:5
默认地,许多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 来解析。
1.2 Pod 所在宿主机上的 DNS 配置及服务
1.2.1 resolv.conf 文件
[root@node2 cloud-user]# cat /etc/resolv.conf
# nameserver updated by /etc/NetworkManager/dispatcher.d/-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 服务启动时,该文件会被运行。它的任务包括:
- 创建 dnsmasq 配置文件 :
- node-dnsmasq.conf (在我的 3.11 版本环境上没有创建该文件,见下文分析)
- origin-dns.conf
- origin-upstream-dns.conf
- 当 NetworkManager 服务启动时启动 dnsmasq 服务
- 设置宿主机的所有默认路由 IP 为 Dnsmasq 的侦听IP
- 修改 /etc/resolv.conf,设置搜索域,以及将宿主机的默认 IP 作为 nameserver
- 创建 /etc/origin/node/resolv.conf
也就是说,宿主机上的 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 tun0
172.17.0.0 0.0.0.0 255.255.0.0 U docker0
172.22.122.0 0.0.0.0 255.255.255.0 U eth0
172.30.0.0 0.0.0.0 255.255.0.0 U tun0
dnsmasq 服务的配置目录为 /etc/dnsmasq.d。其中有两个配置文件(具体含义请查阅有关文档):
[root@node2 dnsmasq.d]# cat origin-dns.conf
no-resolv
domain-needed
no-negcache
max-cache-ttl=
enable-dbus
dns-forward-max=
cache-size=
bind-dynamic
min-port=
except-interface=lo
# End of config
文件 origin-upstream-dns.conf 中定义了上游(upstream) DNS 名字服务器:
[root@node2 dnsmasq.d]# cat origin-upstream-dns.conf
server=172.22.122.3
server=172.22.122.2
server=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.1
server=/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 在启动时会自动添加这两条记录:

而 dnsIP 和 dnsDomain 应该是在 /etc/origin/node/node-config.yaml 中的如下配置:
dnsBindAddress: 127.0.0.1:
dnsDomain: cluster.local
Dec :: dnsmasq[]: using nameserver 127.0.0.1# for domain in-addr.arpa
Dec :: dnsmasq[]: using nameserver 127.0.0.1# 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 记录并返回给查询客户端。
tcp 127.0.0.1: 0.0.0.0:* LISTEN /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/-origin-dns.sh
# Generated by NetworkManager
search cluster.local haihangyun.cn exampleos.com
nameserver 172.22.122.5
dnsmasq 在多个IP 地址的 53 端口上侦听,为本机上的以及本机上Pod 中的DNS查询服务:
udp 10.128.0.1: 0.0.0.0:* /dnsmasq
udp 172.17.0.1: 0.0.0.0:* /dnsmasq
udp 172.22.122.5: 0.0.0.0:* /dnsmasq
和 Node 节点不同,Master 节点上有两个SkyDns 进程。一个在 127.0.0.1:53 侦听,负责本机上的集群内服务的DNS查询,因为 Master 节点同时承担 node 节点的角色:
udp 127.0.0.1: 0.0.0.0:* /openshift
Dec :: dnsmasq[]: using nameserver 127.0.0.1# for domain cluster.local
Dec :: dnsmasq[]: using nameserver 127.0.0.1# for domain in-addr.arpa
另一个是在所有网卡的 8053 端口上侦听,这是因为Master 还具有 master api 角色:
udp 0.0.0.0: 0.0.0.0:* /openshift
对于这个 SkyDns 进程的作用尚不清楚,还需进一步研究。从已有资料上看看,所有节点上都需要安装 SkyDns,并组成一个分布式集群。因为 Master 节点上的 53 端口被另一个 SkyDns 进程占用,因此换到了端口8053。
2. DNS 查询流程
2.1 pod 内的应用通过域名访问外网服务器的DNS查询流程
流程示意图如最上面图中的 1 和 2.1 部分所示。
dnsmasq 日志:
Nov :: dnsmasq[]: using nameserver 172.22.122.3#
Nov :: dnsmasq[]: using nameserver 172.22.122.2#
Nov :: dnsmasq[]: using nameserver 172.22.122.4#
Nov :: dnsmasq[]: query[A] www.sina.com from 172.22.122.13
Nov :: dnsmasq[]: forwarded www.sina.com to 172.22.122.4
Nov :: dnsmasq[]: forwarded www.sina.com to 172.22.122.2
Nov :: dnsmasq[]: forwarded www.sina.com to 172.22.122.3
Nov :: dnsmasq[]: 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 -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: msec
;; SERVER: 172.22.122.9#(172.22.122.9)
;; WHEN: Mon Dec :: CST
;; MSG SIZE rcvd:
dnsmasq 日志:
Dec :: dnsmasq[]: query[A] mybank.dev.svc.cluster.local from 10.128.2.128
Dec :: dnsmasq[]: forwarded mybank.dev.svc.cluster.local to 127.0.0.1
Dec :: dnsmasq[]: reply mybank.dev.svc.cluster.local is 172.30.162.172
(5)其它实验:查询服务的所有端点
查询结果:
[root@node2 cloud-user]# nsenter -t -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 :: dnsmasq[]: query[A] jenkins.dev.endpoints.cluster.local from 10.128.2.128
Dec :: dnsmasq[]: forwarded jenkins.dev.endpoints.cluster.local to 127.0.0.1
Dec :: dnsmasq[]: reply jenkins.dev.endpoints.cluster.local is 10.128.2.81
Dec :: dnsmasq[]: 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 -n dig ---.dev.pod.cluster.local ;; QUESTION SECTION:
;---.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: msec
;; SERVER: 172.22.122.9#(172.22.122.9)
;; WHEN: Mon Dec :: CST
;; MSG SIZE rcvd:
dnsmasq 日志:
Dec :: dnsmasq[]: query[A] ---.dev.pod.cluster.local from 10.128.2.128
Dec :: dnsmasq[]: forwarded ---.dev.pod.cluster.local to 127.0.0.1
Dec :: dnsmasq[]: reply ---.dev.pod.cluster.local is 172.30.162.172
(7)对比 FQDN 和 PQDN
这个 PQDN 被加上了搜索域名再进行查询,能返回正确的IP地址:
[root@node2 cloud-user]# nsenter -t -n ping mybank.dev.svc
PING mybank.dev.svc.cluster.local (172.30.162.172) () bytes of data.
而这个 FQDN 被直接做DNS查询,结果查询失败,未能获取IP地址:
[root@node2 cloud-user]# nsenter -t -n ping mybank.dev.svc.
ping: mybank.dev.svc.: Name or service not known
2.3 从外网通过服务域名访问pod 中运行的服务
可以看出,该过程中只涉及到外部DNS将服务的公共域名解析为 OpenShift Router 所在节点的公网地址,后面 HAProxy 作为代理,直接通过 IP 访问pod,并将结果返回客户端。
参考文档:
- https://pracucci.com/kubernetes-dns-resolution-ndots-options-and-why-it-may-affect-application-performances.html
- https://www.redhat.com/en/blog/red-hat-openshift-container-platform-dns-deep-dive-dns-changes-red-hat-openshift-container-platform-36
感谢您的阅读,欢迎关注我的微信公众号:
理解OpenShift(2):网络之 DNS(域名服务)的更多相关文章
- 理解OpenShift(1):网络之 Router 和 Route
理解OpenShift(1):网络之 Router 和 Route 理解OpenShift(2):网络之 DNS(域名服务) 理解OpenShift(3):网络之 SDN 理解OpenShift(4) ...
- 理解OpenShift(3):网络之 SDN
理解OpenShift(1):网络之 Router 和 Route 理解OpenShift(2):网络之 DNS(域名服务) 理解OpenShift(3):网络之 SDN 理解OpenShift(4) ...
- 理解OpenShift(7):基于 Prometheus 的集群监控
理解OpenShift(1):网络之 Router 和 Route 理解OpenShift(2):网络之 DNS(域名服务) 理解OpenShift(3):网络之 SDN 理解OpenShift(4) ...
- 理解OpenShift(6):集中式日志处理
理解OpenShift(1):网络之 Router 和 Route 理解OpenShift(2):网络之 DNS(域名服务) 理解OpenShift(3):网络之 SDN 理解OpenShift(4) ...
- 理解OpenShift(5):从 Docker Volume 到 OpenShift Persistent Volume
理解OpenShift(1):网络之 Router 和 Route 理解OpenShift(2):网络之 DNS(域名服务) 理解OpenShift(3):网络之 SDN 理解OpenShift(4) ...
- 理解OpenShift(4):用户及权限管理
理解OpenShift(1):网络之 Router 和 Route 理解OpenShift(2):网络之 DNS(域名服务) 理解OpenShift(3):网络之 SDN 理解OpenShift(4) ...
- 转-深入理解VMware虚拟网络
原文出处:http://wangchunhai.blog.51cto.com/225186/381225 VMware Workstation是一款非常不错的虚拟机软件,许多爱好者用VMware Wo ...
- Linux安装Tomcat-Nginx-FastDFS-Redis-Solr-集群——【第五集之补充-转载“深入理解VMware虚拟网络”】
郑重声明,此文太好,按耐不住要保存起来好好研究研究,如果侵权,联系我. 转载自王春海的http://blog.51cto.com/wangchunhai/381225,有所更改. 同时可以参考:htt ...
- 深入理解 Neutron -- OpenStack 网络实现(4):网络名字空间
问题导读1.如何查看网络名字空间?2.网络名字空间开头的名字有什么规律?3.dhcp服务是如何实现的?4.router的实现是通过iptables进行的是否正确?5.SNAT和DNAT规则有什么作用? ...
随机推荐
- Matlab:五点差分方法求解椭圆方程非导数边值问题
差分格式脚本文件: tic; clear clc M=32;%x的步数 N=16;%y的步数 h1=1/M;%x的步长 h2=1/N;%y的步长 x=0:h1:1; y=0:h2:1; u=zeros ...
- 微信小程序常见问题
上拉加载与下拉刷新 https://blog.csdn.net/yelin042/article/details/71435628 微信小程序---报错:对应的服务器TLS为TLS 1.0,小程序要求 ...
- github命令总结
一.创建版本库 1.创建一个空目录 $ mkdir learngit $ cd learngit $ pwd //pwd命令用于显示当前目录 2.创建一个版本库(通过git init命令把这个目录变成 ...
- JavaScript下实现交换数组元素上下移动例子
// 交换数组元素 var swapItems = function(arr, index1, index2) { arr[index1] = arr.splice(index2, ...
- docker命令脚本
第一版: 1 #!/bin/bash #this is input docker continer shell! #this is -- # v1.1.2 read -p "请输入要执行do ...
- windows环境搭建Vue开发环境
一.安装node.js(https://nodejs.org/en/) 下载完毕后,可以安装node,建议不要安装在系统盘(如C:). 二.设置nodejs prefix(全局)和cache(缓存)路 ...
- springcloud Zuul学习笔记
SpringCloud Zull是一个基于NetflixZuul实现的API网关组件,它实现了请求路由,负载均衡,校验过滤等功能;本文主要记录springcloud zuul的入门级demo开发过程; ...
- pthon入门之strip()和split()函数简单区分
小白,分享记录学习新感悟 路飞的第一次作业写一个登录的程序,作业的升级需求中有个锁文件的需求,大致上如果用户数错了密码三次将用户写到黑名单上,下次登录锁定: ok基本的要求写完,我们上代码 usern ...
- 使用vue+koa实现一个简单的图书小程序(1)
这个系列的博客用来记录我开发时候遇到的问题以及学习到的知识 边做边学: 前后端分离,高内聚低耦合小程序端使用了mpvue 内部使用了vuejs的语法 来做整个小程序的渲染层 后端使用的是koa2搭建一 ...
- nodeJS网络操作
var http = require('http'); http.createServer(function (request, response) { response.writeHead(200, ...