目录
什么是服务发现?
环境变量
DNS 服务
Linux 中 DNS 查询原理
Kubernetes 中 DNS 查询原理
调试 DNS 服务
存根域及上游 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 配置解释如下:
配置项 | 功能 | 备注 |
---|---|---|
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).(来源)
笔者通过 AWS 提供的 eksctl 工具创建的 v1.15 的集群默认也是启用了 CoreDNS, 查阅 eksctl 源码可以获取其默认启用的插件.
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 记录有以下几种:
类别 | 作用 |
---|---|
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 服务, 因为 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
当无自定义配置时, 不匹配的 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"]
来源: https://www.cnblogs.com/lshare/p/11361687.html