目录:
(1)k8s指南-概述
(2)k8s指南-架构
(3)k8s指南-工作负载(1)
(4)k8s指南-工作负载(2)
(5)k8s指南-工作负载(3)
(6)k8s指南-工作负载(4)
(7)k8s指南-Service
(8)k8s指南-Ingress
(9)k8s指南-DNS与服务发现
(10)K8S指南-平滑升级与自动扩缩容
K8S组件包括控制平面,Node组件和插件,而插件中必不可少的就是DNS。
K8S中的DNS服务
在k8s中,ip是随时可能发生变化的,特别是pod的ip,服务的ip也是可以改变的。因此服务或pod之间相互访问一般是通过域名来实现的。
在K8S集群中,Kubernetes为Service和Pod创建DNS记录。
Service DNS
普通的service会以svc-name.namespace.svc.cluster.local
的形式生成DNS记录,并解析到该Service的Cluster IP。其中,namespace
是命名空间,cluster.local
是集群域名。
Headless Service(没有Cluster IP)也会以同样的格式生成DNS记录,只不过其解析的ip地址不是一个,而是一组被选中的pod的IP,当然如果没有backend则不做处理。
在上一篇关于Ingress的blog中创建过my-nginx
的服务,并有关联的pods。下面在pod中查一下域名:
nslookup my-nginx.default.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: my-nginx.default.svc.cluster.local
Address: 10.100.64.53
可以看到确实与Service的集群IP地址相同:
kubectl get service -n default
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx ClusterIP 10.100.64.53 <none> 80/TCP 23h
Pod DNS
Pod会以pod-ip.namespace.pod.cluster.local
的形式生成记录,注意ip地址中点换成横杠。
还是以my-nginx
为例,其pod的IP地址为:10.1.0.82
,随便在k8s中选一个pod,查询该域名:
nslookup 10-1-0-82.default.pod.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: 10-1-0-82.default.pod.cluster.local
Address: 10.1.0.82
事实上,对于以Deployment或Daemonset类型创建的Pod,还会设置另一个域名,格式为pod-ip.{deployment/daemonset-name}.namespace.svc.cluster.local
,如下所示
nslookup 10-1-0-82.my-nginx.default.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: 10-1-0-82.my-nginx.default.svc.cluster.local
Address: 10.1.0.82
解析域名时使用的Server就是K8S的DNS服务地址:
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 133d
Pod自定义hostname和subdomain
默认情况下,Pod的名称就是pod创建时指定的name,但如果是通过RS控制器创建的,还会在name后面加一个随机后缀名。如下所示:
kubectl get pods -n default
NAME READY STATUS RESTARTS AGE
my-nginx-dd5b4b7dd-dt9tz 1/1 Running 0 23h
pod1 1/1 Running 0 2m12s
pod2 1/1 Running 0 2m12s
my-nginx-dd5b4b7dd-dt9tz
是通过deployment创建的,pod1
和pod2
是直接创建的。
Pod的主机名默认为pod的名称,进入my-nginx-dd5b4b7dd-dt9tz
查看/etc/hosts
文件:
# Kubernetes-managed hosts file.
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.1.0.82 my-nginx-dd5b4b7dd-dt9tz
可以看到主机名就是pod的名称,也可以通过hostname
直接获取。
Pod的规范中有一个可选的hostname
字段,可以用来指定主机名,还有一个subdomain
字段,可以用来指定子域名。
假设某pod的主机名设置为foo
,子域名设置为bar
,在命名空间default
中,其完全限定域名FQDN为foo.bar.default.svc.cluster.local
。
下面创建一个pod,分别指定其hostname
和subdomain
:
apiVersion: v1
kind: Service
metadata:
name: busybox-subdomain
spec:
selector:
name: busybox
clusterIP: None
ports:
- name: foo # 实际上不需要指定端口号
port: 1234
---
apiVersion: v1
kind: Pod
metadata:
name: busybox1
labels:
name: busybox
spec:
hostname: busybox-1
subdomain: busybox-subdomain
containers:
- image: busybox:1.28
command:
- sleep
- "3600"
name: busybox
该pod的主机名为busybox-1
, 完全限定域名FQDN为:busybox-1.busybox-subdomain.default.svc.cluster.local
。 登录pod查看/etc/hosts
:
10.1.0.84 busybox-1.busybox-subdomain.default.svc.cluster.local busybox-1
可以看到其完全限定域名和后面的主机名。
默认情况下,
hostname
命令会返回该短主机名,而hostname -f
命令返回FQDN。
DNS 会为FQDN提供一个 A 记录(ipv4)和 AAAA 记录(ipv6),指向该Pod 的 IP。
说明:在 Linux 中,内核的主机名字段(struct utsname 的 nodename 字段)限定最多 64 个字符。
如果 Pod 启用这一特性,而其 FQDN 超出 64 字符,Pod 的启动会失败。 Pod 会一直出于 Pending 状态(通过 kubectl 所看到的 ContainerCreating), 并产生错误事件,例如 “Failed to construct FQDN from Pod hostname and cluster domain, FQDN long-FQDN is too long (64 characters is the max, 70 characters requested).” (无法基于 Pod 主机名和集群域名构造 FQDN,FQDN long-FQDN 过长,至多 64 个字符,请求字符数为 70)。 对于这种场景而言,改善用户体验的一种方式是创建一个准入Webhook 控制器, 在用户创建顶层对象(如 Deployment)的时候控制FQDN 的长度。
CoreDNS
在微服务架构中,服务注册大体上有两种方式。一种是使用Zookeeper等注册中心,另一种是使用DNS服务,Kubernetes就是使用的后一种。
从Kubernetes 1.13开始,CoreDNS就成了Kubernetes的默认DNS服务器。
CoreDNS的一个重要特点就是以插件的形式提供服务,且配置简单。通过命令获取corefile配置文件信息:
kubectl -n kube-system get cm coredns -o yaml
apiVersion: v1
data:
Corefile: |
.:53 {
errors # 错误记录到标准输出
health {
lameduck 5s
}
ready # 在端口8181上提供的一个HTTP末端,当所有能够表达自身的插件都已经就绪时,通过此末端返回200 OK
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
# 将不在集群域内的域名转发到预定义解析器
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30 # 前端缓存
loop # 转发环检测,如果发现死循环则终止CoreDNS进程
reload # 允许自动重新加载已更改的Corefile
loadbalance # 轮转式DNS负载均衡器,随机分配A,AAAA和MX记录的顺序
}
kind: ConfigMap
metadata:
creationTimestamp: "2022-11-16T02:37:11Z"
name: coredns
namespace: kube-system
resourceVersion: "227"
uid: 5520fd16-cbb8-4bc1-abb5-474778c191b4
CoreDNS能够配置存根域和上游域名服务器。比如现在需要将.consul.local
后缀指向一个单独的域名服务器,可以在corefile配置:
consul.local:53 {
errors
cache 30
forward . 10.23.32.113
}
如果域名后缀无法统一,也可以自定义域名服务器,直接修改转发的预定义解析服务器:
...
prometheus :9153
# 将不在集群域内的域名转发到预定义解析器
forward . 10.0.0.10 {
max_concurrent 1000
}
cache 30 # 前端缓存
...
某些场景下需要将域名解析到固定ip时,可以在corefile中配置外部域名解析:
apiVersion: v1
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
hosts {
221.6.16.11 harbor.example.com
221.6.16.13 es.example.com
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
...
}
Pod的DNS策略
Kubernetes支持pod维度的DNS策略设置。通过pod规约中的dnsPolicy
可以设置以下策略:
- Default: Pod从自身所在的Node上继承域名解析配置。
- ClusterFirst: 将优先使用kubernetes环境的dns服务(如coreDNS提供的域名解析服务),将无法解析的域名转发到系统配置的上游(宿主机)DNS服务器。
- ClusterFirstWithHostNet: 对于以hostNetwork方式运行的Pod,应将其DNS策略显式设置为该策略。
- None: 此设置允许忽略Kubernetes环境中的DNS设置,pod会使用其
dnsConfig
字段所提供的DNS设置。
注意,Default并不是默认的DNS策略,ClusterFirst才是。当未设置dnsPolicy时,使用ClusterFirst策略。
下面是一个DNS策略设置示例:
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- image: busybox:1.28
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
name: busybox
restartPolicy: Always
hostNetwork: true // 此方式须指定ClusterFirstWithHostNet策略
dnsPolicy: ClusterFirstWithHostNet
DNS策略最终配置在/etc/resolv.conf文件中,也就是说k8s最终还是通过pod容器中resolv.conf文件来做域名解析的。
CluterFirst策略
不指定任何DNS策略时,默认使用该策略。
查看pod上的/etc/resolv.conf
:
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
nameserver即域名解析服务器,也就是k8s中的DNS服务地址。
search是搜索域,会与实际访问名组合在一起进行解析。例如上文中的my-nginx服务,其完整域名是my-nginx.default.svc.cluster.local
,访问时只需要服务名my-nginx
即可,k8s会自动将服务名与搜索域中的配置组合起来。搜索域主要是为了方便k8s内部服务之间互相访问。
options ndots:5 表示当待解析的域名包含.
的数量大于等于5时,就会将其当成完全限定域名直接解析,如果解析不成功,则会继续匹配search/domain域;而如果域名中包含的.
小于5,则会将其视为非完全限定域名,与search域组合起来进行解析。
举例说明:
如果从pod中访问域名a.b.c.d
,由于该域名中有4个.
,小于5,所以在进行DNS解析时,会将其视为非完全限定域名,和search域的配置组合在一起,按照以下顺序进行解析:
a.b.c.d.default.svc.cluster.local. ->
a.b.c.d.svc.cluster.local. ->
a.b.c.d.cluster.local. ->
当以上域名都无法解析时,才会将a.b.c.d.
作为绝对域名进行解析。也就是说,前三次解析是无效的。
None
dnsConfig
可以与任何DNS策略一起使用。当dnsPolicy
为None时,必须指定dnsConfig
字段。
用户可以在dnsConfig
中指定以下属性:
nameservers
:指定DNS服务器的ip列表,最多3个。当dns策略为None
时,至少要包含1个。searches
: 搜索域列表。Kubernetes最多允许6个搜索域。options
:可选对象列表,其中每个对象可能具有name
(必须)和value
(可选)。
下面是一个示例:
apiVersion: v1
kind: Pod
metadata:
namespace: default
name: dns-example
spec:
containers:
- name: test
image: nginx
dnsPolicy: "None"
dnsConfig:
nameservers:
- 192.0.2.1 # 这是一个示例
searches:
- ns1.svc.cluster-domain.example
- my.dns.search.suffix
options:
- name: ndots
value: "2"
- name: edns0
创建上面的pod后,查看DNS策略的最终配置/etc/resolve.conf
如下:
nameserver 192.0.2.1
search ns1.svc.cluster-domain.example my.dns.search.suffix
options ndots:2 edns0
注意,Windows节点上运行的pod不支持ClusterFirstWithHostNet,Windows将艘有的带有
.
的名称视为全限定域名(FQDN)
参考资料
[1]. https://kubernetes.io/zh-cn/docs/concepts/services-networking/dns-pod-service/
文章评论