目录
 * 什么是服务发现? <https://www.cnblogs.com/lshare/p/11361687.html#什么是服务发现> 
 * 环境变量 <https://www.cnblogs.com/lshare/p/11361687.html#环境变量> 
 * DNS 服务 <https://www.cnblogs.com/lshare/p/11361687.html#dns-服务> 
 * Linux 中 DNS 查询原理 
<https://www.cnblogs.com/lshare/p/11361687.html#linux-中-dns-查询原理> 
 * Kubernetes 中 DNS 查询原理 
<https://www.cnblogs.com/lshare/p/11361687.html#kubernetes-中-dns-查询原理> 
 * 调试 DNS 服务 <https://www.cnblogs.com/lshare/p/11361687.html#调试-dns-服务> 
 * 存根域及上游 DNS <https://www.cnblogs.com/lshare/p/11361687.html#存根域及上游-dns> 
什么是服务发现?
服务发现就是一种提供服务发布和查找的服务,是基于服务架构(SOA)应用的核心服务,需具备以下关键特性:
 * 注册(Registration),新增服务到服务列表; 
 * 目录(Directory),即服务列表; 
 * 查找(Lookup),通过服务名找到服务。 
服务发现的关键在于服务元数据(metadata)的存储,包括服务名、服务 IP、服务端口等信息。
Kubernetes 支持两种服务发现方式,环境变量和 DNS。
环境变量
当 Pod 创建时,Kubernetes 会将每个活跃的 Service 的相关环境变量设置到 Pod 中。值得注意的是,这些环境变量不会因为相关 
Service 改变而改变(笔者亲手试验过)。
Kubernetes 会设置两类环境变量,分别是:
 * Kubernetes Service 环境变量 
 * Docker Link 环境变量 
Kubernetes Service 环境变量形如(假定服务名为 latte,且访问端口为 8080):
LATTE_SERVICE_HOST=10.100.251.57 LATTE_SERVICE_PORT=8080 
Docker Link 环境变量形如(假定服务名为 latte,且访问端口为 8080):
LATTE_PORT_8080_TCP_ADDR=10.100.251.57 LATTE_PORT_8080_TCP_PORT=8080 
LATTE_PORT_8080_TCP_PROTO=tcp LATTE_PORT=tcp://10.100.251.57:8080 
LATTE_PORT_8080_TCP=tcp://10.100.251.57:8080 
可以通过进入 Pod 的终端,执行 env 命令查看设置的环境变量验证。
kubectl exec -ti <pod-name> env --namespace=<my-namespace> 
此种方式的服务发现缺点很明显:
 * 依赖的服务必须先运行起来,否则 Pod 无法发现; 
 * 如依赖的服务宕机或绑定新地址,Pod 无法发现,仍然持有旧的地址。 
幸好,我们还有另一种服务发现机制。
DNS 服务
在讲述 Kubernetes 中使用 DNS 进行服务发现之前,我们不得不先了解下 Linux 中是如何进行 DNS 查询的。
Linux 中 DNS 查询原理
在 Linux 的 /etc/ 目录中,存在 3 
个我们需要关注的文件,分别是(参考:http://man7.org/linux/man-pages/man5/host.conf.5.html):
 * /etc/host.conf:DNS 解析器配置,包含 trim、multi、order、reorder 和 nospoof 等等配置。 
 * /etc/hosts:本地 hosts 数据库,存放本地的域名到 IP 的配置。 
 * /etc/resolv.conf:DNS 解析器配置,包含 nameserver、domain、search、sortlist 和 options 
等配置。 
下面是一台 Linux 服务器中 3 个相关文件的内容:
# /etc/host.conf multi on # /etc/hosts 127.0.0.1 localhost 
localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost6 
localhost6.localdomain6 # /etc/resolv.conf ; generated by 
/usr/sbin/dhclient-script search us-west-2.compute.internal options timeout:2 
attempts:5 nameserver 192.168.0.2 
/etc/resolv.conf 配置 <http://man7.org/linux/man-pages/man5/resolv.conf.5.html>
解释如下:
配置项 功能 备注 
nameserver DNS 服务器 值必须是 IP 地址 
domain 本地域名 域中的查询可以使用相对于本地域名的短名称 
search 主机名查询列表 默认只包含本地域名。阈值为 6 个域名,256 个字符。 
options 选项 修改内部 DNS 解析器变量值。 
options 中常见的配置项有:
配置项 功能 
ndots 所有查询中,如果.的个数少于给定的数值,则会根据search中配置的列表依次在对应域中先进行搜索,如果没有返回,则最后再直接查询域名本身。阈值为 
15。 
timeout 等待 DNS 服务器响应的超时时间,单位为秒。阈值为 30 s。 
attempts 向同一个 DNS 服务器发起重试的次数,单位为次。阈值为 5。 
笔者在本地试验时发现,文件 /etc/resolv.conf 是网络连接时自动生成的,依据是:
 * 当本机处以断网状态时,cat /etc/resolv.conf 返回空文本; 
 * 当本机连上网络时,cat /etc/resolv.conf 返回以下内容: # # macOS Notice # # This file is not 
consulted for DNS hostname resolution, address # resolution, or the DNS query 
routing mechanism used by most # processes on this system. # # To view the DNS 
configuration used by this system, use: # scutil --dns # # SEE ALSO # 
dns-sd(1), scutil(8) # # This file is automatically generated. # nameserver 
58.250.162.58 nameserver 8.8.8.8 
第一个 DNS 服务器是中国联通的,通过访问 https://whois.domaintools.com/58.250.162.58 可知;
第二个 DNS服务器是 Google 的,通过 nslookup 8.8.8.8 或者访问 
https://whois.domaintools.com/8.8.8.8 可知。
Kubernetes 中 DNS 查询原理
Kubernetes 中有两个可选的 DNS 服务插件(处在 kube-system 命名空间):
插件 说明 
kube-dns 其代码已经从 kubernetes 库中分离到单独的仓库维护,见 https://github.com/kubernetes/dns 
CoreDNS 支持 Kubernetes v1.9 及以上;Kubernetes v1.12 起,官方推荐使用来替换 
kube-dns;Kubernetes v1.13 起,成为默认 DNS 服务;占用内存小,查询速度快。 
注意:kube-dns 在 Kubernetes 中有多重含义,要注意区别。
 * 与 CoreDNS 对比时,使用狭义,表示名为 kube-dns 的 DNS 服务; 
 * 当泛指时,表示 Kubernetes 中的 DNS 服务。 
使用 kubeadm 创建 v1.11 及以后的 Kubernetes 集群,默认启用 CoreDNS(处于 GA 状态,见 Software 
release life cycle <https://en.wikipedia.org/wiki/Software_release_life_cycle>
)。(来源 
<https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/>)
笔者通过 aws 提供的 eksctl 工具创建的 v1.15 的集群默认也是启用了 CoreDNS,查阅 eksctl 源码 
<https://github.com/weaveworks/eksctl/tree/master/pkg/addons/default>
可以获取其默认启用的插件。
Kubernetes 通过修改每个 Pod 中每个容器的域名解析配置文件 /etc/resolv.conf 来达到服务发现的目的
。在笔者创建的集群中获取其中一个容器的域名解析配置文件如下:
# /etc/resolv.conf nameserver 10.100.0.10 search cafe.svc.cluster.local 
svc.cluster.local cluster.local us-west-2.compute.internal options ndots:5 
其含义是:DNS 服务器为 10.100.0.10,当查询关键词中 . 的数量少于 5 个,则根据 search 
中配置的域名进行查询,当查询都没有返回正确响应时再尝试直接查询关键词本身。
例如,执行 host -v cn.bing.com 后我们将会看到:
Trying "cn.bing.com.cafe.svc.cluster.local" Trying 
"cn.bing.com.svc.cluster.local" Trying "cn.bing.com.cluster.local" Trying 
"cn.bing.com.us-west-2.compute.internal" Trying "cn.bing.com" ... 
解析过程是如此缓慢,当对某些服务访问频繁时建议额外配置 DNS 记录。
注:获取过程如下
# 1) 查询指定命名空间中的所有 pod kubectl get pods --namespace=cafe # 2) 进入其中一个 pod 的交互终端 
kubectl exec -ti macchiato-6746674bdd-5hmtw sh --namespace=cafe # 3) 查看 
/etc/resolv.conf cat /etc/resolv.conf 
DNS 服务器会监听着集群内所有 Service API,以在服务不可用时移除记录,在新服务创建时插入新记录。
这些记录存放在哪里呢?
答案是:存储在 kube-dns 插件中的 cache 也可配置到 etcd。
存储的 DNS 记录有哪些种类呢?
我们过去或多或少了解到的 DNS 记录 <https://en.wikipedia.org/wiki/List_of_DNS_record_types>
有以下几种:
类别 作用 
A Address record,域名到 IP 地址的记录 
CNAME Canonical name record,别名记录,设置域名的别名 
NS Name server record,域名服务器记录 
MX Mail exchange record,邮件服务记录 
TXT Text record,为某条记录设置说明 
AAAA IPv6 address record,域名到 IPv6 地址 ( 128 = 32 * 4 )的记录 
这次我们要多认识一个称之为 SRV(Service locator)的 DNS 记录,用来通用化地定位服务。
Kubernetes 的 DNS 服务(简称为 kube-dns)支持 Service 的 A 记录、 SRV 记录和 CNAME 记录。
我们知道 Kubernetes 中的 Service 是 Pod 的逻辑分组,有 Cluster IP 和 Label Selector 有无之别。没有设置 
Cluster IP 的我们称之为 Headless Service,否则称之为 Normal Service。设置了 Label Selector 
的会同时产生一个 Endpoints 对象,声明集群内部 Service 的访问端点。
假定有一个 cafe 命名空间下名为 latte 的 Normal Service,开放了名为 http 的 TCP 端口 8080,kube-dns 
会为其生成以下的 A 记录和 SRV 记录:
latte.cafe.svc.cluster.local. 5 IN A 10.100.71.56 
_http._tcp.latte.cafe.svc.cluster.local. 30 IN SRV 10 100 443 
latte.cafe.svc.cluster.local. 
注:查询 DNS 记录的方法
(1)安装 dig 工具
将下面的部署配置保存到文件 dnsutils.yaml,然后执行 kubectl apply -f dnsutils.yaml 部署。
apiVersion: v1 kind: Pod metadata: name: dnsutils namespace: default spec: 
containers: - name: dnsutils image: tutum/dnsutils command: - sleep - "3600" 
imagePullPolicy: IfNotPresent restartPolicy: Always 
(2)使用 dig 工具获取 DNS 记录
# 用法 dig @<DNS服务器> <记录类型> <域名> <可选值> # 示例 ## 1)获取 DNS 服务地址 kubectl get svc 
kube-dns -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns 
ClusterIP 10.100.0.10 <none> 53/UDP,53/TCP 8d ## 2)进入 dnsutils 的 shell 终端 
kubectl exec -ti dnsutils sh ## 3)查询 latte 服务的 A 记录 dig @10.100.0.10 A 
latte.cafe.svc.cluster.local +noall +answer 
假如有一个 cafe 命名空间下名为 mocha 的 Headless Service,kube-dns 会为其生成以下的 A 记录集(域名到 Pod 
IPs 的映射):
mocha.cafe.svc.cluster.local. 5 IN A 192.168.62.111 
mocha.cafe.svc.cluster.local. 5 IN A 192.168.62.112 
mocha.cafe.svc.cluster.local. 5 IN A 192.168.62.113 
如若有一个 cafe 命名空间下名为 macchiato 的 Headless 但设置了以下 Endpoints 的 Service:
kind: Endpoints apiVersion: v1 metadata: name: macchiato namespace: cafe 
subsets: - addresses: - ip: 1.2.3.4 ports: - port: 9376 
kube-dns 会为其生成以下的 A 记录:
macchiato.cafe.svc.cluster.local. 4 IN A 1.2.3.4 
如果有一个 cafe 命名空间下名为 cappuccino 的 Headless 但设置了以下 ExternalName 的 Service:
kind: Service apiVersion: v1 metadata: name: cappuccino namespace: cafe spec: 
type: ExternalName externalName: cappuccino.cafe.com 
kube-dns 会为其生成以下的 CNAME 记录:
cappuccino.cafe.svc.cluster.local. 10 IN CNAME cappuccino.cafe.com. 
Kubernetes 的 DNS 服务除了支持 Service 的 DNS 记录外,还支持 Pod 的 A 记录,使用 hostname + 
subdomain 方式实现。仔细阅读以下部署配置。
apiVersion: v1 kind: Service metadata: name: default-subdomain spec: selector: 
name: busybox clusterIP: None ports: - name: foo # Actually, no port is needed. 
port: 1234 targetPort: 1234 --- apiVersion: v1 kind: Pod metadata: name: 
busybox1 labels: name: busybox spec: hostname: busybox-1 subdomain: 
default-subdomain containers: - image: busybox:1.28 command: - sleep - "3600" 
name: busybox --- apiVersion: v1 kind: Pod metadata: name: busybox2 labels: 
name: busybox spec: hostname: busybox-2 subdomain: default-subdomain 
containers: - image: busybox:1.28 command: - sleep - "3600" name: busybox 
我们发现其创建了:
 * name 为 busybox1,hostname 为 busybox-1,subdomain 为 default-subdomain 的 Pod; 
 * name 为 busybox2,hostname 为 busybox-2,subdomain 为 default-subdomain 的 Pod; 
 * name 为 default-subdomain 的 Headless Service。 
产生以下 A 记录:
busybox-1.default-subdomain.svc.cluster.local. 4 IN A 192.168.51.119 
busybox-2.default-subdomain.svc.cluster.local. IN A 
busybox-2.default-subdomain.svc.cluster.local. 4 IN A 192.168.36.188 
default-subdomain.svc.cluster.local. 4 IN A 192.168.62.187 
default-subdomain.svc.cluster.local. 4 IN A 192.168.62.188 
这些记录是怎样的一种格式呢?
参见:https://github.com/kubernetes/dns/blob/master/docs/specification.md
调试 DNS 服务
使用 busybox 调试 DNS 服务 
<https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution/>
,因为 busybox 中有 nslookup 工具可以使用。
(1)保存以下文本到文件 busybox.yaml(此处使用命名空间为 cafe )
apiVersion: v1 kind: Pod metadata: name: busybox namespace: cafe spec: 
containers: - name: busybox image: busybox:1.28 command: - sleep - "3600" 
imagePullPolicy: IfNotPresent restartPolicy: Always 
(2)应用 busybox.yaml,并查看状态
kubectl apply -f busybox.yaml --namespace=cafe kubectl get pods busybox 
--namespace=cafe 
(3)进入终端交互界面并支持 nslookup 查询服务 latte
kubectl exec -ti busybox sh --namespace=cafe nslookup latte 
存根域及上游 DNS 
<https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/#effects-on-pods>
当无自定义配置时,不匹配的 DNS 查询(比如上文说的cn.bing.com)会使用从 Node 中继承的 nameserver 进行解析。
当有自定义的配置时,会在 DNS 缓存层查询无果后,根据查询名称后缀决定去往的 DNS 解析器:
 * 查询名称带有集群后缀的(比如 ".cluster.local"),转发到 kube-dns。 
 * 查询名称带有存根域名后缀的(比如 ".acme.local"),转发到 custom DNS。 
 * 查询名称不匹配的(比如 "widget.com"),转发到 upstream DNS。 
以上配置使用 Kubernetes 的ConfigMap 插件,配置如下:
apiVersion: v1 kind: ConfigMap metadata: name: kube-dns namespace: kube-system 
data: stubDomains: | {"acme.local": ["1.2.3.4"]} upstreamNameservers: | 
["8.8.8.8","8.8.4.4"] 
热门工具 换一换