1. 服務發現機制與類型
簡單來說,服務發現就是服務或者應用之間互相定位的過程。不過,服務發現並非什么新概念,傳統的單體應用架構時代也會用到,只不過單體應用的動態性不強,更新和重新發布頻度較低,通常以月甚至以年計,基本不會進行自動伸縮,因此服務發現的概念無須顯性強調。在傳統的單體應用網絡位置發生變化時,由IT運維人員手工更新一下相關的配置文件基本就能解決問題。但在微服務應用場景中,應用被拆分成眾多的小服務,它們按需創建且變動頻繁,配置信息基本無法事先寫入配置文件中並及時跟蹤反映動態變化,服務發現的重要性便隨之凸顯。
服務發現機制的基本實現,一般是事先部署好一個網絡位置較為穩定的服務注冊中心(也稱為服務總線),服務提供者(服務端)向注冊中心注冊自己的位置信息,並在變動后及時予以更新,相應地,服務消費者則周期性地從注冊中心獲取服務提供者的最新位置信息從而“發現”要訪問的目標服務資源。復雜的服務發現機制還能夠讓服務提供者提供其描述信息、狀態信息及資源使用信息等,以供消費者實現更為復雜的服務選擇邏輯。
實踐中,根據其發現過程的實現方式,服務發現還可分為兩種類型:客戶端發現和服務端發現。
客戶端發現:由客戶端到服務注冊中心發現其依賴到的服務的相關信息,因此,它需要內置特定的服務發現程序和發現邏輯。
服務端發現:這種方式額外要用到一個稱為中央路由器或服務均衡器的組件;服務消費者將請求發往中央路由器或者負載均衡器,由它們負責查詢服務注冊中心獲取服務提供者的位置信息,並將服務消費者的請求轉發給服務提供者。
由此可見,服務注冊中心是服務發現得以落地的核心組件。事實上,DNS可以算是最為原始的服務發現系統之一,不過,在服務的動態性很強的場景中,DNS記錄的傳播速度可能會跟不上服務的變更速度,因此它不並適用於微服務環境。另外,傳統實踐中,常見的服務注冊中心是ZooKeeper和etcd等分布式鍵值存儲系統。不過,它們只能提供基本的數據存儲功能,距離實現完整的服務發現機制還有大量的二次開發任務需要完成。另外,它們更注重數據一致性,這與有着更高的服務可用性要求的微服務發現場景中的需求不太相吻合。
Netflix的Eureka是目前較流行的服務發現系統之一,它是專門開發用來實現服務發現的系統,以可用性目前為先,可以在多種故障期間保持服務發現和服務注冊的功能可用,其設計原則遵從“存在少量的錯誤數據,總比完全不可用要好”。另一個同級別的實現是Consul,它是由HashiCorp公司提供的商業產品,不過還有一個開源基礎版本提供。它於服務發現的基礎功能之外還提供了多數據中心的部署能力等一眾出色的特性。
盡管傳統的DNS系統不適於微服務環境中的服務發現,但SkyDNS項目(后來稱kubedns)卻是一個有趣的實現,它結合古老的DNS技術和時髦的Go語言、Raft算法並構建於etcd存儲系統之上,為Kubernetes系統實現了一種服務發現機制。Service資源為Kubernetes提供了一個較為穩定的抽象層,這有點類似於服務端發現的方式,於是也就不存在DNS服務的時間窗口的問題。
Kubernetes自1.3版本開始,其用於服務發現的DNS更新為了kubeDNS,而類似的另一個基於較新的DNS的服務發現項目是由CNCF(Cloud Native Computing Foundation)孵化的CoreDNS,它基於Go語言開發,通過串接一組實現DNS功能的插件的插件鏈進行工作。自Kubernetes 1.11版本起,CoreDNS取代kubeDNS成為默認的DNS附件。不過,Kubernetes依然支持使用環境變量進行服務發現。
2. 服務發現方式:環境變量
創建Pod資源時,kubelet會將其所屬名稱空間內的每個活動的Service對象以一系列環境變量的形式注入其中。它支持使用Kubernetes Service環境變量以及與Docker的links兼容的變量。
(1)Kubernetes Service環境變量
Kubernetes為每個Service資源生成包括以下形式的環境變量在內一系列環境變量,在同一名稱空間中創建的Pod對象都會自動擁有這些變量:
{SVCNAME}_SERVICE_HOST
{SVCNAME}_SERVICE_PORT
注意:如果SVCNAME中使用了連接線,kubernetes會在定義為環境變量時將其轉換為下划線。
(2) Docker Link形式的環境變量
Docker使用--link選項實現容器連接時所設置的環境變量形式,具體使用方式請參考Docker的相關文檔。在創建Pod對象時,Kubernetes也會把與此形式兼容的一系列環境變量注入到Pod對象中。
例如,在Service資源myapp-svc創建后創建的Pod對象中查看可用的環境變量,其中以MYAPP_SVC_SERVICE開頭的為kubernetes Service環境變量,名稱中不包含“SERVICE”字符串的環境變量為Docker Link形式的環境變量。
/ # printenv | grep MYAPP
MYAPP_SVC_PORT_80_TCP_ADDR=10.107.208.93
MYAPP_SVC_PORT_80_TCP_PORT=80
MYAPP_SVC_PORT_80_TCP_PROTO=tcp
MYAPP_SVC_PORT_80_TCP=tcp://10.107.208.93:80
MYAPP_SVC_SERVICE_HOST=10.107.208.93
MYAPP_SVC_SERVICE_PORT=80
MYAPP_SVC_PORT=tcp://10.107.208.93:80
基於環境變量的服務發現功能簡單、易用,但存在一定的局限,例如僅那些與創建的Pod對象在同一名稱空間中且事先存在的Service對象的信息才會以環境變量形式注入,那些非同一名稱空間,或者是Pod資源創建之后才創建的Service對象的相關環境變量則不會被添加。幸而,基於DNS的發現機制並不存在此類限制。
3. ClusterDNS和服務發現
Kubernetes系統之上用於名稱解析和服務發現的ClusterDNS是集群的核心附件之一,集群中創建的每個Service對象,都會由其自動生成相關的資源記錄。默認情況下,集群內各Pod資源會自動配置其作為名稱解析服務器,並在其dns搜索列表中包含它所屬名稱空間的域名后綴。
無論使用kubeDNS還是CoreDNS,它們提供的基於DNS的服務發現解決方案都會負責解析以下資源記錄(Resource Record)類型以實現服務發現。
(1)擁有ClusterIP的Service資源,要具有以下類型的資源記錄
A記錄:<service>.<ns>.svc.<zone>. <ttl> IN A <cluster-ip>
SRV記錄:_<port>._<proto>.<service>.<ns>.svc.<zone>. <ttl> IN SRV <weight> <priority> <port-number> <service>.<ns>.svc.<zone>.
PTR記錄:<d>.<c>.<b>.<a>.in-addr.arpa. <ttl> IN PTR <service>.<ns>.svc.<zone>.
(2)Headless類型的Service資源,要具有以下類型的資源記錄
A記錄:<service>.<ns>.svc.<zone>. <ttl> IN A <endpoint-ip>
SRV記錄:_<port>._<proto>.<service>.<ns>.svc.<zone>. <ttl> IN SRV <weight> <priority> <port-number> <hostname>.<service>.<ns>.svc.<zone>.
PTR記錄:<d>.<c>.<b>.<a>.in-addr.arpa. <ttl> IN PTR <hostname>.<service>.<ns>.svc.<zone>
(3)ExternalName類型的Service資源,要具有CNAME類型的資源記錄
CNAME記錄:<service>.<ns>.svc.<zone>. <ttl> IN CNAME <extname>.
名稱解析和服務發現是Kubernetes系統許多功能得以實現的基礎服務,它通常是集群安裝完成后應該立即部署的附加組件。使用kubeadm初始化一個集群時,它甚至會自動進行部署。
4. 服務發現方式:DNS
創建Service資源對象時,ClusterDNS會為它自動創建資源記錄用於名稱解析和服務注冊,於是,Pod資源可直接使用標准的DNS名稱來訪問這些Service資源。每個Service對象相關的DNS記錄有兩個:
{SVCNAME}.{NAMESPACE}.{CLUSTER_DOMAIN}
{SVCNAME}.{NAMESPACE}.svc.{CLUSTER_DOMAIN}
另外,在前面第2章的部署參數中,“--cluster-dns”指定了集群DNS服務的工作地址,“--cluster-domain”定義了集群使用的本地域名,因此,系統初始化時默認會把“cluster.local.”和主機所在的域“ilinux.io.”作為DNS的本地域使用,這些信息會在Pod創建時以DNS配置的相關信息注入到它的/etc/resolv.conf配置文件中。例如,在此前創建的用於交互式Pod資源客戶端中查看其配置:
/
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local ilinux.io
上述search參數中指定的DNS各搜索域,是以次序指定的幾個域名后綴,如下所示。
{NAMESPACE}.svc.{CLUSTER_DOMAIN}:例如default.svc.cluster.local。
svc.{CLUSTER_DOMAIN}:例如svc.cluster.local。
{CLUSTER_DOMAIN}:例如cluster.local。
{WORK_NODE_DOMAIN}:例如ilinux.io。
例如,在此前創建的用於交互式Pod客戶端中嘗試請求解析myapp-svc的相關DNS記錄:
/ # nslookup myapp-svc.default
Server: 10.96.0.10
Name: myapp-svc
Address 1: 10.107.208.93 myapp-svc.default.svc.cluster.local
解析時,“myapp-svc”服務名稱的搜索次序依次是default.svc.cluster.local、svc.cluster.local、cluster.local和ilinux.io,因此基於DNS的服務發現不受Service資源所在的名稱空間和創建時間的限制。上面的解析結果也正是默認的default名稱空間中創建的myapp-svc服務的IP地址。
轉載自: