通過ReplicaSet來創建一組Pod來提供具有高可用性的服務。雖然每個Pod都會分配一個單獨的Pod IP,然而卻存在如下兩問題:
-
Pod IP僅僅是集群內可見的虛擬IP,外部無法訪問。
-
Pod IP會隨着Pod的銷毀而消失,當ReplicaSet對Pod進行動態伸縮時,Pod IP可能隨時隨地都會變化,這樣對於我們訪問這個服務帶來了難度。
因此,Kubernetes中的Service對象就是解決以上問題的核心關鍵。
Service的創建
Service可以看作是一組提供相同服務的Pod對外的訪問接口
。借助Service,應用可以方便地實現服務發現和負載均衡
。
Service同其他Kubernetes對象一樣,也是通過yaml或json文件進行定義。此外,它和其他Controller對象一樣,通過Label Selector來確定一個Service將要使用哪些Pod。一個簡單的Service定義如下:
apiVersion: v1
kind: Service
metadata:
labels:
run: nginx
name: nginx-service
spec:
ports:
- port: 80
protocol: TCP
targetPort: 81
selector:
app: nginx
type: ClusterIP
下面簡單分析一下上面的Service描述文件:
-
通過spec.selector字段確定這個Service將要使用哪些Label。在本例中,這個名為nginx的Service,將會管理所有具有app: nginxLabel的Pod。
-
spec.ports.port: 80表明此Service將會監聽80端口,並將所有監聽到的請求轉發給其管理的Pod。spec.ports.targetPort: 81表明此Service監聽到的80端口的請求都會被轉發給其管理的Pod的81端口,此字段可以省略,省略后其值會被設置為spec.ports.port的值。
-
type: ClusterIP表面此Service的type,會在下文中講到。
Service的類型
在Serive定義時,我們需要指定spec.type字段,這個字段擁有四個選項:
-
ClusterIP。默認值。給這個Service分配一個Cluster IP,它是Kubernetes系統自動分配的虛擬IP,因此只能在集群內部訪問。
-
NodePort。
將Service通過指定的Node上的端口暴露給外部
。通過此方法,訪問任意一個NodeIP:nodePort
都將路由到ClusterIP,從而成功獲得該服務。 -
LoadBalancer。在 NodePort 的基礎上,借助 cloud provider 創建一個外部的負載均衡器,並將請求轉發到
:NodePort。此模式只能在雲服務器(AWS等)上使用。 -
ExternalName。將服務通過 DNS CNAME 記錄方式轉發到指定的域名(通過 spec.externlName 設定)。需要 kube-dns 版本在 1.7 以上。
注:kubernetes 1.13以后默認集群coreDns,不再使用默認kube-dns,對
ExternalName
支持情況需要進一步確認
NodePort類型
在定義Service時指定spec.type=NodePort,並指定spec.ports.nodePort
的值,Kubernetes就會在集群中的每一個Node上打開你定義的這個端口
,這樣,就能夠從外部通過任意一個NodeIP:nodePort訪問到這個Service
了。
下面是一個簡單的例子:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
labels:
run: nginx
spec:
selector:
app: nginx
ports:
- port: 80
nodePort: 30001
type: NodePort
假如有3個app: nginx Pod運行在3個不同的Node中,那么此時客戶端訪問任意一個Node的30001端口都能訪問到這個nginx服務。
LoadBalancer類型
如果雲服務商支持外接負載均衡器,則可以通過spec.type=LoadBalancer
來定義Service,一個簡單的例子如下:
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
clusterIP: 10.0.171.239
loadBalancerIP: 78.11.24.19
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 146.148.47.155
Service without selector
Service可以抽象訪問Pod集群,同時 Service也可以抽象其他后端
-
在生產環境中使用外部數據庫,在測試環境中使用自己的數據庫
-
將自己的Service指向其他集群或者其他命名空間的Service
-
遷移應用到k8s,但是還是有些應用運行在k8s之
通過定義不包含selector的Service實現
kind: service
apiVersion: v1
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
Service 沒有Selector,K8s不會創建Endpoints,你可以通過手動創建Endpoint指向自己的endpoint
kind: Endpoints
apiVersion: v1
metadata:
name: my-service
subsets:
- addresses:
ip: 1.2.3.4
- ports:
port: 9376
Endpoint的IP不能是loopback(127.0.0.1/8),link-local(169.254.0.0/16), link-local
multicast (224.0.0.0/24).
訪問不含有selector的Service和訪問含有Selector的Service 方式一樣,都會講流向重定向的endpoint
其他命名空間的服務是一個特例,他不會定義ports和endpoint,他只是返回一個訪問外部服務的別名
其它名稱空間
其他命名空間的服務是一個特例,他不會定義ports和endpoint,他只是返回一個訪問外部服務的別名
kind: kind
apiVersion: v1
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
當你訪問服務my-service.prod.svc.CLUSTER時,cluster的dns服務會返回記錄my.database.example.com 的CNAME,這個重定向是發生在dns解析階段。
虛擬IP 和服務代理
代理
K8s集群內每個節點都會運行kube-proxy,負責實現服務的虛擬機IP(不是externalName)。1.0版本的代理模式在是userspace,1.1增加了iptables proxy,從1.2開始 iptables 代理是默認的模式
K8s 1.0的service是三層(TCP/UDP),從k8s1.1開始,增加了Ingress,實現七層(HTTP)服務
用戶空間的代理模式
Kube-proxy監控k8s master節點來發現Service、Endpointd的增加和刪除,對於Service,在本地打開一個隨機端口作為代理端口,任何訪問改代理端口的連接都會被指向Service對象的Pod集合,最終指向哪個Pod取決於Service的SessionAffinity,最后,他會配置iptables,捕獲流向Service 的Cluster IP 和Port的連接,並重定向到這個代理端口。
最終結果,任何到Service Cluster Ip 和port的流量都會指向合適的Pod
默認情況下,都是輪訓算法選擇后端,也可以通過設置service.spec.sessionAffinity 為ClientIP,將選擇算法改為Client-IP based session affinity
Iptables的代理模式
該模式與userspace模式類似,只是沒有這個代理端口
比userspace方式更快更可靠,然后與userspace方式不同,當選擇的pod異常時,該方式無法自動嘗試選擇其他Pod。
-
userspace模式只不適合大規模集群使用(數千個服務)
-
userspace模式隱藏了訪問服務的源IP,iptables模式雖然沒有隱藏源IP,但是也是通過負載均衡或者nodeport 影響了客戶端輸入