Kubernetes 系列六】Kubernetes 服務發現
目錄
- 什麼是服務發現?
- 環境變數
- 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"]