概述
作為服務發現機制的基本功能,在集群內需要能夠通過服務名對服務進行訪問,那么就需要一個集群范圍內的DNS服務來完成從服務名到ClusterIP的解析。
DNS服務在kubernetes中經歷了三個階段。
第一階段,在kubernetes 1.2版本時,dns服務使用的是由SkyDNS提供的,由4個容器組成:kube2sky、skydns、etcd和healthz。etcd存儲dns記錄;kube2sky監控service變化,生成dns記錄;skydns讀取服務,提供查詢服務;healthz提供健康檢查
第二階段,在kubernetes 1.4版本開始使用kubedns,有3個容器組成:kubedns、dnsmasq和sidecar。kubedns監控service變化,並記錄到內存(存到內存提高性能)中;dnsmasq獲取dns記錄,提供dns緩存,提供dns查詢服務;sidecar提供健康檢查。
第三階段,從kubernetes 1.11版本開始,dns服務有coredns提供,coredns支持自定義dns記錄及配置upstream dns server,可以統一管理內部dns和物理dns。coredns只有一個coredns容器。下面是coredns的架構
coredns配置解析
下面是coredns的配置模板

.: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 { 172.23.1.3 hub.kellan.com fallthrough } prometheus :9153 forward . /etc/resolv.conf { max_concurrent 1000 } cache 30 loop reload loadbalance }
coredns的主要功能是通過插件系統實現的。它實現了一種鏈式插件的結構,將dns的邏輯抽象成了一個個插件。常見的插件如下:
- loadbalance:提供基於dns的負載均衡功能
- loop:檢測在dns解析過程中出現的簡單循環問題
- cache:提供前端緩存功能
- health:對Endpoint進行健康檢查
- kubernetes:從kubernetes中讀取zone數據
- etcd:從etcd讀取zone數據,可以用於自定義域名記錄
- file:從文件中讀取zone數據
- hosts:使用/etc/hosts文件或者其他文件讀取zone數據,可以用於自定義域名記錄
- auto:從磁盤中自動加載區域文件
- reload:定時自動重新加載Corefile配置文件的內容
- forward:轉發域名查詢到上游dns服務器
- proxy:轉發特定的域名查詢到多個其他dns服務器,同時提供到多個dns服務器的負載均衡功能
- prometheus:為prometheus系統提供采集性能指標數據的URL
- pprof:在URL路徑/debug/pprof下提供運行是的西能數據
- log:對dns查詢進行日志記錄
- errors:對錯誤信息鏡像日志記錄
Pod的dns策略
上面已經描述了dns的服務端,那么pod有什么策略呢
目前的策略如下:
- Default: 繼承Pod所在宿主機的DNS設置
- ClusterFirst:優先使用kubernetes環境的dns服務,將無法解析的域名轉發到從宿主機繼承的dns服務器
- ClusterFirstWithHostNet:和ClusterFirst相同,對於以hostNetwork模式運行的Pod應明確知道使用該策略
- None: 忽略kubernetes環境的dns配置,通過spec.dnsConfig自定義DNS配置
自定義Dns配置可以通過spec.dnsConfig字段進行設置,可以設置如下信息- nameservers:一組dns服務器的列表,最多可設置3個
- searchs:一組用於域名搜索的dns域名后綴,最多6個
- options:配置其他可選參數,例如ndots、timeout等
例如:
1 spec: 2 dnsPolicy: "None" 3 dnsConfig: 4 nameservers: 5 - 1.2.3.4 6 searchs: 7 - xx.ns1.svc.cluster.local 8 - xx.daemon.com 9 options: 10 - name: ndots 11 values: "2"
pod被創建后,容器內的/etc/resolv.conf會根據這個信息進行配置
nodelocaldns
架構圖如下
下面是nodelocaldns的configmap的示例

cluster.local:53 { errors cache { success 9984 30 denial 9984 5 } reload loop bind 169.254.25.10 forward . 10.233.0.3 { force_tcp } prometheus :9253 health 169.254.25.10:9254 } in-addr.arpa:53 { errors cache 30 reload loop bind 169.254.25.10 forward . 10.233.0.3 { force_tcp } prometheus :9253 } ip6.arpa:53 { errors cache 30 reload loop bind 169.254.25.10 forward . 10.233.0.3 { force_tcp } prometheus :9253 } .:53 { errors cache 30 reload loop bind 169.254.25.10 forward . /etc/resolv.conf prometheus :9253 }
但是這里要進一步說明下,通過配置可以看出除了cluster.local(即kubernetes集群的解析)外都使用節點的/etc/resolv.conf文件的nameserver。如果將自定義解析加到coredns上是沒有效果的,所以需要修改nodelocaldns的配置,將其他域名的解析轉到coredns上才行。
1 #forward . /etc/resolv.conf 2 forward . 10.233.0.3 { 3 force_tcp 4 }
Service
Service 通過標簽選擇 pod,將各 pod 的 ip 保存到它的 endpoints 屬性中。Service 的收到的請求會被均攤到這一組 endpoints 上。
DNS
在 k8s 中做服務發現,最常用的方式是通過 DNS 解析。
在我們的 k8s 集群配置了 dns 服務(最常用的是 coredns)的前提下,我們就可以直接通過全限定域名(FQDN)來訪問別的服務。
全限定域名的格式如下:
1 # 格式 2 <service-name>.<namespace>.svc.cluster.local # 域名 .svc.cluster.local 是可自定義的 3 4 # 舉例:訪問 default 名字空間下的 nginx 服務 5 nginx.default.svc.cluster.local
如果兩個服務在同一個名字空間內,可以省略掉后面的 .<namespace>.svc.cluster.local
,直接以服務名稱為 DNS 訪問別的服務
P.S. DNS 服務發現的實現方式:修改每個容器的 /etc/resolv.conf,使容器訪問 k8s 自己的 dns 服務器。而該 dns 服務器知道系統中的所有服務,所以它能給出正確的響應。
SRV 記錄
Service 除了會使用最常見的 A 記錄做 Pod 的負載均衡外,還提供一種 SRV 記錄,這種類型的服務被稱為 Headless Service.
將 Service 的 spec.ClusterIP 設為 None,得到的就是一個 Headless Service。
普通的 Service 擁有自己的 ClusterIP 地址,service name 會被 DNS 解析到這個虛擬的 ClusterIP(A 記錄或 CNAME 記錄),然后被 kubectl proxy 轉發到具體的 Pod。
而 Headless Service 沒有自己的 ClusterIP(這個值被指定成了 None),service name 只提供 SRV 記錄的 DNS 解析,返回一個 Pods 的 ip/dns 列表。
SRV 記錄最常見的用途,是在有狀態集群中,給集群的所有 Pod 提供互相發現的功能。
最佳實踐
- 總是使用 Deployment,避免直接使用 ReplicaSet/Pod
- 為 Pod 的 Port 命名(比如命名成 http/https),然后在 Service 的 targetPort 中通過端口名稱(http/https)來指定目標端口(容器端口)。
- 更直觀,也更靈活
- 應用通過 dns 查找/發現其他服務
- 同一名字空間下的應用,可以直接以服務名稱為域名發現別的服務,如通過
http://nginx
訪問 nginx
- 同一名字空間下的應用,可以直接以服務名稱為域名發現別的服務,如通過
- Service 配置會話親和性為 ClientIP 可能會更好(會話將被同一個 pod 處理,不會發生轉移)
- 配置 externalTrafficPolicy: Local 可以防止不必要的網絡跳數,但是可能會導致 pod 之間的流量分配不均。