Kubernetes Pod
是有生命週期的,它們能夠被建立,也能夠被銷燬,而後一旦被銷燬生命就永遠結束。經過ReplicationController
可以動態地建立和銷燬Pod(列如,須要進行擴縮容,或者執行滾動升級);每一個Pod都會獲取它本身的IP地址,即便這些IP地址不老是穩定可依賴的。這會致使一個問題;在Kubernetes集羣中,若是一組Pod(稱爲backend)爲其餘Pod(稱爲frontend)提供服務,那麼哪些frontend該如何發現,並鏈接到這組Pod中的那些backend呢?node
關於Serviceredis
Kubernetes Service
定義了這樣一種抽象:一個Pod的邏輯分組,一種能夠訪問它們不一樣的策略--一般稱爲微服務。這一組Pod可以被Service訪問到,一般是經過Label Sekector
實現的;算法
舉個例子,考慮一個圖片處理backend,它運行了3個副本。這些副本是可交換的 -- frontend不須要關心它們調用了哪一個backend副本。而後組成這一組backend程序的Pod實際上可能會發生變化,frontend客戶端不該該也不必知道,並且也不須要跟蹤這一組backend的狀態。Service定義的抽象可以解耦這種關聯。數據庫
對Kubernetes集羣中的應用,Kubernetes提供了簡單的Endpoints API,只要service中的一組Pod發生變動,應用程序就會被更新。對非Kubernetes集羣中的應用,Kubernetes提供了基本VIP的網橋的方式訪問Service,再由Service重定向到backend Pod。後端
一個Service在Kubernetes中是一個REST對象,和Pod相似。像全部的REST對象同樣,Service定義能夠基於POST方式,請求apiserver建立新的實例。例如,假定有一組Pod,它們對外暴漏了9376端口,同時還被打上「app=MyApp」標籤。api
kind: Service apiVersion: v1 metadata: name: my-service spec: selector: app: MyApp ports: - protocol: TCP port: 80 targetPort: 9376
上述配置將建立一個名稱爲「my-service」的Service對象,它會將請求代理到使用TCP端口9376,而且具備標籤「app=MyApp」的pod上。這個Service將被指派一個IP地址(一般爲「Cluster IP」)它會被服務的代理使用。該Service的selector將會持續評估,處理結果將被POST到一個名稱爲"my-service"的Endpoints對象上。緩存
須要注意的是,Servie可以將一個接收端口映射到任意的targetPort。默認狀況下,targetPort將被設置爲與port字段相同的值。可能更有趣的是,targetPort能夠是一個字符串,引用了backend Pod的一個端口的名稱;可是,實際指派給該端口名稱的端口號,在每一個backend Pod中可能並不相同。對於部署和設計Service,這種方式會提供更大的靈活性。例如,能夠在backend軟件下一個版本中,修改Pod暴露的端口,並不會中斷客戶端的調用。服務器
Kubernetes service可以支持TCP和UDP協議,默認TCP協議網絡
Service抽象了該如何訪問Kubernetes Pod,但也能抽象其餘類型的backend,例如:session
在任何這些場景中,都可以定義沒有selector 的 Service :
kind: Service apiVersion: v1 metadata: name: my-service spec: ports: - protocol: TCP port: 80 targetPort: 9376
因爲這個Service沒有selector,就不會建立相關的Endpoints對象。能夠手動將Service映射到指定的Endpoints:
kind: Endpoints apiVersion: v1 metadata: name: my-service subsets: - addresses: - ip: 1.2.3.4 ports: - port: 9376
注意:Endpoint IP地址不能loopback(127.0.0.0/8),link-local(169.254.0.0/16),或者link-local多播(224.0.0.0/24)。
訪問沒有selector的Service,與selector的Service的原理相同。請求被路由到用戶定義的Endpoint(該示例中爲1.2.3.4:9376)
ExternalName service是Service的特別,它沒有selector,也沒有定義任何的端口和Endpoint。相反地,對於運行在集羣外部的服務,它經過返回該外部服務的別名這種方式來提供服務。
kind: Service apiVersion: v1 metadata: name: my-service namespace: prod spec: type: ExternalName externalName: my.database.example.com
當查詢主機 my-service.prod.svc.CLUSTER
時,集羣的DNS服務將返回一個值爲my.database.example.com
的CNAME記錄,訪問這個服務的功能方式與其餘的相同,惟一不一樣的是重定向發生的DNS層,並且不會進行代理或轉發。若是後續決定要將數據庫遷移到Kubetnetes中,能夠啓動對應的Pod,增長合適的Selector或Endpoint,修改service的typt。
在Kubernetes集羣中,每一個Node運行一個kube-proxy進程。kube-proxy負責爲Service實現了一種VIP(虛擬IP)的形式,而不是ExternalName的形式。在Kubernetes v1.0版本,代理徹底在userspace。在Kubernetes v1.1版本,新增了iptables代理,但並非默認的運行模式。從Kubernetes v1.2起,默認就是iptables代理。
在Kubernetes v1.0版本,Service是「4層」(TCP/UDP over IP)概念。在Kubernetes v1.1版本,新增了 Ingress API(beta版),用來表示「7層」(HTTP)服務。
這種模式,kube-proxy會監視Kubernetes master對service對象和Endpoints對象的添加和移除。對每一個Service,它會在本地Node上打開一個端口(隨機選擇)。任何鏈接到「代理端口」的請求,都會被代理到Service的backend Pods中的某一個上面(如 Endpoints 所報告的同樣)。使用哪一個backend Pod,是基於Service的SessionAffinity來肯定的。最後,它安裝iptables規則,捕獲到達該Service的clusterIP(是虛擬IP)和Port的請求,並重定向到代理端口,代理端口再代理請求到backend Pod。
網絡返回的結果是,任何到達Service的IP:Port的請求,都會被代理到一個合適的backend,不須要客戶端知道關於Kubernetes,service或pod的任何信息。
默認的策略是,經過round-robin算法來選擇backend Pod。實現基於客戶端IP的會話親和性,能夠經過設置service.spec.sessionAffinity
的值爲「ClientIP」(默認值爲「None」);
這種模式,kube-proxy會監視Kubernetes master對象和Endpoinnts對象的添加和移除。對每一個Service,它會安裝iptables規則,從而捕獲到達該Service的clusterIP(虛擬IP)和端口的請求,進而將請求重定向到Service的一組backend中某個上面。對於每一個Endpoints對象,它也會安裝iptables規則,這個規則會選擇一個backend Pod。
默認的策略是,隨機選擇一個backend。實現基於客戶端IP的會話親和性,能夠將service.spec.sessionAffinity
的值設置爲「ClientIP」(默認值爲「None」)
和userspace代理相似,網絡返回的結果是,任何到達Service的IP:Port的請求,都會被代理到一個合適的backend,不須要客戶端知道關於Kubernetes,service或Pod的任何信息。這應該比userspace代理更快,更可靠。然而,不想userspace代理,若是始出選擇的Pod沒有響應,iptables代理不能自動地重試另外一個Pod,因此它須要依賴readiness probes;
https://jimmysong.io/kubernetes-handbook/images/services-iptables-overview.jpg
這種模式,kube-proxy會監視Kubernetes service對象和Endpoints,調用netlink接口以相應地建立ipvs規則並按期與Kubernetes service對象和Endpoints對象同步ipvs規則,以確保ipvs狀態與指望一致。訪問服務時,流量將被重定向到其中一個後端Pod。
與iptables相似,ipvs基於netfilter的hook功能,但使用哈希表做爲底層數據結構並在內核空間中工做。這意味着ipvs能夠更快地重定向流量,而且在同步代理規則時具備更好的性能。此外,ipvs爲負載均衡算法提供了更多的選項,例如:
注意: ipvs模式假定在運行kube-proxy以前在節點上都已經安裝了IPVS內核模塊。當kube-proxy以ipvs代理模式啓動時,kube-proxy將驗證節點上是否安裝了IPVS模塊,若是未安裝,則kube-proxy將回退到iptables代理模式。
不少Service須要暴露多個端口。對於這種狀況,Kubernetes 支持在Service對象中定義多個端口。當使用多個端口時,必須給出全部端口的名稱,這樣Endpoint就不會產生歧義,例如:
kind: Service apiVersion: v1 metadata: name: my-service spec: selector: app: MyApp ports: - name: http protocol: TCP port: 80 targetPort: 9376 - name: https protocol: TCP port: 443 targetPort: 9377
在Service建立的請求中,能夠經過設置spec.cluster IP字段來指定本身的集羣IP地址。好比,但願替換一個已經存在的DNS條目,或者遺留系統已經配置了一個固定IP且很難從新配置。用戶選擇的IP地址必須合法,而且這個IP地址在service-cluster-ip-range CIDR 範圍內,這對 API Server 來講是經過一個標識來指定的。 若是 IP 地址不合法,API Server 會返回 HTTP 狀態碼 422,表示值不合法。
一個不時出現的問題是,爲何咱們都使用VIP的方式,而不使用標準的round-robin DNS,有以下幾個緣由:
咱們盡力阻止用戶作那些對他們沒有好處的事情,若是不少人都來問這個問題,咱們可能會選擇實現它。
Kubernetes 支持2種基本的服務發現模式 —— 環境變量和 DNS。
當Pod運行在NOde上,kubelet會爲每一個活躍的Service添加一組環境變量。它同時支持Docker links兼容變量,簡單的{SVCNAME}_SERVICE_HOST 和 {SVCNAME}_SERVICE_PORT 變量,這裏 Service 的名稱需大寫,橫線被轉換成下劃線。
舉個例子,一個名稱爲 "redis-master" 的 Service 暴露了 TCP 端口 6379,同時給它分配了 Cluster IP 地址 10.0.0.11,這個 Service 生成了以下環境變量:
REDIS_MASTER_SERVICE_HOST=10.0.0.11 REDIS_MASTER_SERVICE_PORT=6379 REDIS_MASTER_PORT=tcp://10.0.0.11:6379 REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379 REDIS_MASTER_PORT_6379_TCP_PROTO=tcp REDIS_MASTER_PORT_6379_TCP_PORT=6379 REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
這意味着須要有順序的要求 —— Pod 想要訪問的任何 Service 必須在 Pod 本身以前被建立,不然這些環境變量就不會被賦值。DNS 並無這個限制。
一個可選(儘管強烈推薦)集羣插件 是 DNS 服務器。 DNS 服務器監視着建立新 Service 的 Kubernetes API,從而爲每個 Service 建立一組 DNS 記錄。 若是整個集羣的 DNS 一直被啓用,那麼全部的 Pod 應該可以自動對 Service 進行名稱解析。
例如,有一個名稱爲 "my-service" 的 Service,它在 Kubernetes 集羣中名爲 "my-ns" 的 Namespace 中,爲 "my-service.my-ns" 建立了一條 DNS 記錄。 在名稱爲 "my-ns" 的 Namespace 中的 Pod 應該可以簡單地經過名稱查詢找到 "my-service"。 在另外一個 Namespace 中的 Pod 必須限定名稱爲 "my-service.my-ns"。 這些名稱查詢的結果是 Cluster IP。
Kubernetes 也支持對端口名稱的 DNS SRV(Service)記錄。 若是名稱爲 "my-service.my-ns" 的 Service 有一個名爲 "http" 的 TCP 端口,能夠對 "_http._tcp.my-service.my-ns" 執行 DNS SRV 查詢,獲得 "http" 的端口號。
Kubernetes DNS 服務器是惟一的一種可以訪問 ExternalName 類型的 Service 的方式。 更多信息能夠查看 DNS Pod 和 Service。
對一些應用(如 Frontend)的某些部分,可能但願經過外部(Kubernetes 集羣外部)IP 地址暴露 Service。
Kubernetes ServiceTypes 容許指定一個須要的類型的 Service,默認是 ClusterIP 類型。
Type 的取值以及行爲以下:
若是設置 type 的值爲 "NodePort",Kubernetes master 將從給定的配置範圍內(默認:30000-32767)分配端口,每一個 Node 將從該端口(每一個 Node 上的同一端口)代理到 Service。該端口將經過 Service 的 spec.ports[*].nodePort 字段被指定。
若是須要指定的端口號,能夠配置 nodePort 的值,系統將分配這個端口,不然調用 API 將會失敗(好比,須要關心端口衝突的可能性)。
這可讓開發人員自由地安裝他們本身的負載均衡器,並配置 Kubernetes 不能徹底支持的環境參數,或者直接暴露一個或多個 Node 的 IP 地址。
須要注意的是,Service 將可以經過
使用支持外部負載均衡器的雲提供商的服務,設置 type 的值爲 "LoadBalancer",將爲 Service 提供負載均衡器。 負載均衡器是異步建立的,關於被提供的負載均衡器的信息將會經過 Service 的 status.loadBalancer 字段被髮布出去。
kind: Service apiVersion: v1 metadata: name: my-service spec: selector: app: MyApp ports: - protocol: TCP port: 80 targetPort: 9376 nodePort: 30061 clusterIP: 10.0.171.239 loadBalancerIP: 78.11.24.19 type: LoadBalancer status: loadBalancer: ingress: - ip: 146.148.47.155
來自外部負載均衡器的流量將直接打到 backend Pod 上,不過實際它們是如何工做的,這要依賴於雲提供商。 在這些狀況下,將根據用戶設置的 loadBalancerIP 來建立負載均衡器。 某些雲提供商容許設置 loadBalancerIP。若是沒有設置 loadBalancerIP,將會給負載均衡器指派一個臨時 IP。 若是設置了 loadBalancerIP,但云提供商並不支持這種特性,那麼設置的 loadBalancerIP 值將會被忽略掉。