本文介绍Kubernetes集群中容器Pod在域名解析过程中的解析策略和缓存策略。
DNS解析链路全景图
以下介绍三种应用部署形式下应用解析域名的链路:
关于图中的timeout、attempts等术语的含义,请参考下文解析策略和缓存策略。
非容器化应用直接运行于ECS之上。
示例:App运行于ECS上。
容器化应用运行于Kubernetes中,DNSPolicy注入ClusterFirst的Pod里。
示例:App运行于Kubernetes容器Pod中。
解析策略
客户端侧
一般情况下,应用解析域名是通过Glibc提供的接口完成的,下表参数为/etc/resolv.conf中暴露的域名解析参数,即Glibc解析域名时的可配置参数。
参数 | 说明 | Glibc中默认值 | ECS | DNSPolicy为ClusterFirst的Pod | DNSPolicy为Default的Pod | DNSPolicy为Default且采用主机网络的Pod |
| 解析域名时使用的DNS服务器 | 空 | VPC DNS服务器② | CoreDNS ClusterIP③ | VPC DNS服务器 | VPC DNS服务器 |
| 请求非完整域名(非FQDN)时,域名会被拼接上 | 空 | 空 | <ns>.svc.cluster.localsvc.cluster.localcluster.local | 空 | 无 |
| 访问的域名字符串内的点字符数量超过ndots值,则认为是完整域名(FQDN),并被直接解析;如果不足ndots值,则追加search段后缀再进行查询。 | 1 | 1 | 5 | 1 | 1 |
| 对于单个域名解析请求的超时时间,单位为秒 | 5 | 2 | 5 | 5 | 2 |
| 解析域名失败时重试的次数 | 2 | 3 | 2 | 2 | 3 |
| 以Round Robin的形式轮询DNS服务器 | 关闭 | 开启 | 关闭 | 关闭 | 开启 |
| 开启该配置后,一旦需要处理同一Socket发送的两次请求时,解析端会在发送第一次请求后关闭Socket,并在发送第二次请求前打开新的socket。 | 关闭 | 开启 | 关闭 | 关闭 | 开启 |
①attempts参数仅在部分场景下起到重试的效果,例如服务端返回SERVFAIL、NOTIMP、REFUSED时,或服务端返回NOERROR,但没有解析结果时。更多信息,请参见attempts参数解析请求说明。
②VPC DNS服务器是指ECS上默认配置的DNS服务器,IP为100.100.2.136和100.100.2.138,负责PrivateZone和权威域名的解析。
③CoreDNS ClusterIP是指Kubernetes集群内默认部署的CoreDNS在kube-system命名空间下提供的kube-dns服务的IP地址,负责集群内部服务域名的解析,以及PrivateZone、权威域名的解析转发。
关于resolv.conf配置的更多信息,请参见resolv.conf。
部分情况下,客户端侧的域名解析策略可能与上述配置不同:
当采用Alpine作为容器镜像时,其内置了Musl库代替Glibc实现,解析行为会有较大不同,例如:
Alpine不遵循/etc/resolv.conf里面的single-request和single-request-reopen。
3.3及更早版本Alpine不支持search参数,不支持搜索域,无法完成服务发现。
并发请求/etc/resolv.conf中配置的多个DNS服务器,导致NodeLocal DNSCache优化失效。
并发使用同一Socket请求A和AAAA记录,在旧版本内核上触发Conntrack源端口冲突导致丢包问题。
说明关于解析行为的更多信息,请参见musl libc。
当使用Golang、NodeJS等编程语言时,应用可能会采用语言内置的域名解析器,其行为也存在较大区别。
集群内DNS服务器
CoreDNS的/etc/resolv.conf默认沿用ECS的配置,但在实际转发DNS请求时,会使用内置的Forward插件完成。
Forward插件的解析策略控制的参数如下表所示。关于CoreDNS Forward插件的更多配置,请参见Forward。
参数 | 说明 | CoreDNS默认值 | NodeLocal DNSCache默认值 |
| 优先使用UDP协议与上游通信 | 开启 | 关闭 |
| 强制使用TCP协议与上游通信 | 关闭 | 开启 |
| 连续多少次健康检查失败就认为upstream不健康 | 2 | 2 |
| 与upstream的链接保持10秒 | 10s | 10s |
| 选择upstream的策略 | random | random |
| 健康检查时间间隔 | 0.5s | 0.5s |
| 最大请求upstream的链接并发数 | 无 | 无 |
| 连接upstream的超时 | 30s,根据实际耗时动态减小 | 30s,根据实际耗时动态减小 |
| 从upstream等待数据的超时 | 2s | 2s |
缓存策略
客户端侧
客户端侧的缓存策略是因容器和应用而异的,实际的缓存策略取决于您的配置。
集群内DNS服务器
参数 | 说明 | CoreDNS社区默认配置 | CoreDNS ACS默认配置 |
success Max TTL | 成功的域名解析结果缓存最大TTL | 3600s | 30s |
success Min TTL | 成功的域名解析结果缓存最小TTL | 5s | 5s |
success Capacity | 成功的域名解析结果缓存数目 | 9984 | 9984 |
denial Max TTL | 失败的域名解析结果缓存最大TTL | 1800s | 30s |
denial Min TTL | 失败的域名解析结果缓存最小TTL | 5s | 5s |
denial Capacity | 失败的域名解析结果缓存数目 | 9984 | 9984 |
ServerError TTL | 上游DNS服务器异常时解析结果TTL | 5s | 0s(CoreDNS低于1.8.4.2版本时默认为5s) |
serve_stale | 允许无法连接上游DNS服务器时使用已过期的本地缓存 | 关闭 | 关闭 |
实际生效的缓存时间TTL由域名解析结果自身TTL、最大TTL、最小TTL共同决定,规则如下:
解析结果TTL>Max TTL时,实际生效的TTL为Max TTL。
解析结果TTL<Min TTL时,实际生效的TTL为Min TTL。
Min TTL<解析结果TTL<Max TTL时,实际生效的TTL为解析结果TTL。
优化建议
本文介绍了Kubernetes集群的解析路径及各环节参数配置,您可以通过修改Pod Yaml、CoreDNS ConfigMap等方式来修改参数,示例如下。
当客户端Pod配置dnsPolicy:Default
时,ECS上VPC DNS服务器会被拷贝至容器内/etc/resolv.conf配置文件中。
apiVersion: v1
kind: Pod
metadata:
name: example
namespace: default
spec:
containers:
- image: registry.cn-hangzhou.aliyuncs.com/example-ns/example:v1
name: example
#Pod YAML中dnsPolicy值为Default。
dnsPolicy: Default
# 此时容器内/etc/resolv.conf。
# cat /etc/resolv.conf
nameserver 100.100.2.136
nameserver 100.100.2.138
容器内对比ECS上,缺少了rotate single-request-reopen timeout:2 attempts:3
的options参数,可能会使一个偶发的网络链路抖动导致业务侧域名解析异常,需要补充这些参数以提升容错能力。调整Pod YAML如下:
apiVersion: v1
kind: Pod
metadata:
name: example
namespace: default
spec:
containers:
- image: registry.cn-hangzhou.aliyuncs.com/example-ns/example:v1
name: example
# Pod YAML中dnsPolicy值为Default。
dnsPolicy: Default
# 增加以下容错配置。
dnsConfig:
options:
- name: timeout
value: "2"
- name: attempts
value: "3"
- name: rotate
- name: single-request-reopen
# 修改后重新部署Pod,容器内/etc/resolv.conf新增了options参数。
# cat /etc/resolv.conf
nameserver 100.100.2.136
nameserver 100.100.2.138
options rotate single-request-reopen timeout:2 attempts:3