Kubernetes的Pod的壽命是有限的,它們不會復活,因此盡管每個Pod都有自己的IP地址,但是這些IP地址是不可靠的,會隨着Pod的消亡而消失。
這就帶來一個問題,如果一些Pod的集合(稱之為backends)為集群的其他的Pod(稱之為frontends),這些frontends應該如何找到並一直知道哪些backends在這樣的集合中呢?
這就需要引入Service, 一個kubernetes的service是一種抽象,它定義了一個Pod的邏輯集合和一個用於訪問它們的策略。一個Service的目標Pod的集合通常是由Label Selector來決定的。
舉個例子,想象一個處理圖片的后端運行了三個副本。這些副本都是可以替代的,前端不關心它們使用的是哪一個后端。盡管實際組成后端集合的Pod可能會變化,前端的客戶端卻不需要知道這個變化,也不需要自己有一個列表來記錄這些后端服務。Service抽象能讓你達到這種解耦。
對於那些Kubernetes原生的應用,Kubernetes提供了一個簡單的Endpoints API,會在Service中的Pod集合發生改變的時候更新。對於非Kubernetes原生的應用,Kubernetes為Service提供了一種基於虛擬IP的橋接方式使其重定向到后端的Pods。
定義一個Service
Kubernetes中的Service是一個REST對象,這點與Pod類似。正如所有的REST對象一樣,向apiserver POST一個Service的定義就能創建一個新的實例。例如,假設你有一組Pods,每一個Pod都開放了9376端口,並且都有一個"app=MyApp"的標簽。
{ "kind": "Service", "apiVersion": "v1", "metadata": { "name": "my-service" }, "spec": { "selector": { "app": "MyApp" }, "ports": [ { "protocol": "TCP", "port": 80, "targetPort": 9376 } ] } }
這個定義會創建一個新的Service對象,名字為”my-service”,它指向所有帶有”app=MyApp”標簽的Pod上面的9376端口。這個Service同時也會被分配一個IP地址(有時被稱作”cluster ip”),它會被服務的代理所使用(見下面)。這個Service的選擇器,會不斷的對Pod進行篩選,並將結果POST到名字同樣為“my-service”的Endpoints對象。
注意一個Service能將一個來源的端口映射到任意的targetPort。默認情況下,targetPort會被設置成與port字段一樣的值。可能更有意思的地方在於,targetPort可以是一個字符串,能引用一個后端Pod中定義的端口名。實際指派給該名稱的端口號在每一個Pod中可能會不同。這為部署和更新你的Service提供了很大的靈活性。例如,你可以在你的后端的下一個版本中更改開放的端口,而無需導致客戶出現故障。
Kubernetes的Service支持TCP和UDP協議。默認是TCP。
發布 services - service的類型
Kubernetes的ServiceTypes能讓你指定你想要哪一種服務。默認的和基礎的是ClusterIP,這會開放一個服務可以在集群內部進行連接。NodePort 和LoadBalancer是兩種會將服務開放給外部網絡的類型。
ServiceType字段的合法值是:
l ClusterIP: 僅僅使用一個集群內部的IP地址 - 這是默認值。選擇這個值意味着你只想這個服務在集群內部才可以被訪問到。
l NodePort: 在集群內部IP的基礎上,在集群的每一個節點的端口上開放這個服務。你可以在任意<NodeIP>:NodePort地址上訪問到這個服務。
l LoadBalancer: 在使用一個集群內部IP地址和在NodePort上開放一個服務之外,向雲提供商申請一個負載均衡器,會讓流量轉發到這個在每個節點上以<NodeIP>:NodePort的形式開放的服務上。
在使用一個集群內部IP地址和在NodePort上開放一個Service的基礎上,還可以向雲提供者申請一個負載均衡器,將流量轉發到已經以NodePort形式開發的Service上。
注意盡管NodePort可以是TCP或者UDP的,對於Kubernetes 1.0來說,LoadBalancer還支持TCP。
NodePort類型
如果你把type字段設置為"NodePort",Kubernetes的master就會從由啟動參數配置的范圍(默認是:30000-32767)中分配一個端口,然后每一個Node都會將這個端口(在每一個Node上相同的端口)代理到你的Service。這個端口會被寫入你的Service的spec.ports[*].nodePort字段中。
如果你想要一個特定的端口號,你可以在nodePort字段中指定一個值,確保系統能為你分配這個端口,否則API請求將會失敗(例如你需要自己處理可能出現的端口沖突)。你指定的值必須在節點端口配置的范圍內。
這給了開發者了設置他們自己的負載均衡器的自由,配置那些沒有被Kubernetes完全支持的雲環境,或者甚至可以直接開放一個或者多個節點的IP。
這種Service可以同時以<NodeIP>:spec.ports[*].nodePort和spec.clusterIp:spec.ports[*].port的形式訪問。
LoadBalancer類型
在那些支持外部負載均衡器的雲提供者上面,將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" } ] } }
從外部負載均衡器的流量將會被引到后端的Pod,然而具體這個如何實現則要看雲提供商。一些雲提供商允許指定loadBalancerIP。在這種場景,負載均衡器將隨用戶指定的loadBalancerIP一起創建。如果字段loadBalancerIP沒有指定,該負載均衡器會被指定一個短暫性的IP。如果指定了loadBalancerIP,但是雲提供商不支持這個特性,這個字段會被忽略。
