目錄
一、定義Service
? 1.1 type=ClusterIP
?? 1.2 type=NodePort
?? 1.3?type=LoadBalancer
?? 1.4?type=ExternalName
?? 1.5 無標簽選擇器的Service
?? 1.6?Headless?Service
二、Kubernetes的服務發現
? ?2.1?環境變量方式
? ?2.2 DNS方式
????????Kubernetes 中 Service 是 將運行在一個或一組?Pod?上的應用程序對外暴露出去的方法。這樣多個相同功能容器可以對外提供統一的訪問入口。
? ? ? ?Service解決了:
- 避免直接訪問Pod,因為Pod的數量和IP都可能發生變化。而通過Service可以提供穩定的統一入口。
- 提供了負載均衡機制(包括后端Pod自動注冊和發現)
一、定義Service
? ? 定義Service的yaml常見字段含義說明如下:
apiVersion: v1
kind: Service
metadata:name: namespace:labels: []anotations: []
spec:selector: [] # 標簽選擇器,選擇滿足條件的后端Podtype: # Service的公開暴露類型,type可取值(首字母大寫):ClusterIP默認、NodePort、LoadBalancer、ExternalNameclusterIP: # 分配給該Service的IP地址列表。type=ClusterIP或NodePort時,若不手動指定IP則自動分配(在kubeadm init指定的網段范圍內);若填值None則為Headless服務sessionAffinity: # 可取值:ClientIP、None默認。若配置為ClientIP表示對收到同個客戶端IP的訪問請求每次都轉發到同個后端Pod上。ports: # 端口列表,列表多個須分別指定端口的name- name: # 端口名。若僅有一個port,可不設置此字段protocol: # 協議。可取值: TCP默認、UDP、SCTPport: # Service在clusterIP上的監聽端口nodePort: # 當type=NodePort或LoadBalancer時,在每個節點機器上的分配端口(端口允許范圍30000-32767)。targetPort: # 轉發至后端Pod上的端口,若未指定則等于port
status: # 當type=LoadBalancer時設置外部負載均衡器地址,比如公有云環境loadBalancer: # 外部負載均衡器ingress: # 外部負載均衡器ip: # 外部負載均衡器的IP地址hostname: # 外部負載均衡器的主機名
? 1.1 type=ClusterIP
在前文【Kubernetes(三):Workload工作負載-CSDN博客】已經創建Deployment的基礎上(Pod的標簽是app=nginx, Pod內容器端口為80)。然后通過以下nginx-service.yaml文件,創建一個Service:
apiVersion: v1
kind: Service
metadata:name: nginx-service
spec:selector: # 標簽選擇器,選擇滿足條件的后端Podapp: nginxtype: ClusterIPports:- port: 55580 # Service在clusterIP上的監聽端口targetPort: 80 # 轉發至后端Pod上的端口,若未指定則等于port
kubectl apply -f nginx-service.yaml# 以下service可簡寫svc
kubectl get service -o widekubectl describe service/nginx-service
注:kube-apiserver本身也是一個Service,名字為kubernetes。 該service的ClusterIP是地址池中第一個IP,端口是HTTPS的443。如上圖。
Service中的Endpoints是后端Pod的地址列表。如果Pod發生變化,Kubernetes會自動維護Service的EndPoints地址列表,自動保持最新。
從Kubernetes集群內(包括集群節點機器、Pod容器內)可通過命令訪問Service的虛擬IP和端口來訪問后端的Pod:curl http://172.16.242.61:55580
默認情況,訪問請求會被Service均衡分發給后端Pod之一。
?? 1.2 type=NodePort
? ? ?在上文type=ClusterIP的例子中,因為值在服務虛擬IP(clusterIP)上監聽,所以只能在Kubernetes集群內(包括集群節點機器及容器內)才能訪問到該Service。
? ? 若希望從Kubernetes集群外也可以訪問Service,則需把改type=NodePort。NodePort是建立在type=ClusterIP的基礎上,并在每個節點機器上分配一個nodePort端口。通過該nodePort端口訪問的請求,Service也可以分發到與后端Pod的Endpoints。
apiVersion: v1
kind: Service
metadata:name: nginx-service
spec:selector: # 必選。標簽選擇器,選擇滿足條件的后端Podapp: nginxtype: NodePortports:- port: 55580 # Service在clusterIP上的監聽端口nodePort: 32000 # 當type=NodePort或LoadBalancer時,在每個節點機器上的分配端口(端口允許范圍默認30000-32767)。targetPort: 80 # 轉發至后端Pod上的端口,若未指定則等于port
kubectl apply -f nginx-service.yaml# 以下service可簡寫svc
kubectl get service -o widekubectl describe service/nginx-service
不僅可以從Kubernetes集群內(包括集群節點機器、Pod容器內)可通過命令訪問Service的虛擬IP和端口來訪問后端的Pod:curl http://172.16.167.197:55580
并且還可以從Kubernetes集群外訪問,訪問地址為:任意一個節點機器IP:nodePort? ?如下圖:
雖然可以通過節點機器IP訪問,但在節點機器操作系統上通過netstat命令是無法看到nodePort偵聽端口。這是因為沒有采用通常的直接監聽方式,而是kube-proxy利用操作系統的代理模式來轉發。
kube-proxy的--proxy-mode選項指定轉發的代理模式,當節點機器是Linux則可為ipvs或iptables模式(不手工指定時,若Linux操作系統加載啟用了IPVS內核,kube-proxy會自動優先選擇IPVS模式,否則切換iptables模式);當節點機器是Windows則為kernelspace模式。
?? 1.3?type=LoadBalancer
? ? ?type=LoadBalancer是基于NodePort的基礎上構建并創建一個外部負載均衡器,通過該外部負載均衡器路由到與 clusterIP 相同的 Endpoints。
? ? type=LoadBalancer主要用于云平臺環境。Service使用云平臺負載均衡器,Kubernetes不直接提供負載均衡。這要求云平臺能夠監控Kubernetes的Service資源對象創建過程,云平臺負載均衡器中upstream值自動配置為Service的Endpoints列表,同時云平臺還能對Kubernetes的Service的Status信息補充配置好的負載均衡器的IP地址。這樣外部客戶端就可以通過云平臺負載均衡器IP及Service端口進行訪問后端Pod服務了。
? ? 默認情況:對于type=LoadBalancer的Service會默認開啟節點機器NodePort,這是為了讓負載均衡器能夠通過Node節點IP來訪問內部Pod。 從v1.24版開始,Service配置中提供了一個布爾類型字段allocateLoadBalancerNodePorts來指定是否分配NodePort(默認為true)。僅當云平臺負載均衡器能夠直接將流量路由到 Pod 而無需使用節點機器端口的情況才設置為false。
?? 1.4?type=ExternalName
? ? ?type=ExternalName是將 Service 映射到指定的目標域名或IP。相當于對目標域名或IP設置別名。
??下面externalname-service.yaml
apiVersion: v1
kind: Service
metadata:name: externalname-service
spec:type: ExternalNameexternalName: www.baidu.com
kubectl apply -f externalname-service.yaml# 以下service可簡寫svc
kubectl get service -o widekubectl describe service/externalname-service
注:type=ExternalName的Service沒有Endpoints,也沒有為服務分配ClusterIP。
Kubernetes對服務自動生成的域名格式為:<ServiceName>.<namespace>.svc.<clusterDomain>?
從Kubernetes集群內(包括集群節點機器、Pod容器內)可通過該服務域名訪問,既自動訪問externalName指定的目標域名。本例中訪問?curl http://externalname-service.default.svc.cluster.local??相當于訪問www.baidu.com
?? 1.5 無標簽選擇器的Service
? ? 上文type=ExternalName的Service沒有Endpoints,也沒有為服務分配ClusterIP。 除上文外,還可以通過基于type=ClusterIP類型來將外部服務定義為Service,這種將外部服務定義為Service也稱為沒有標簽選擇器的Service (Services without selectors)。
? ? ?普通type=ClusterIP的Service通過selector標簽選擇器對后端Pod的Endpoints列表進行了抽象封裝,如果后端的Endpoints不是由Pod提供,而是在Kubernetes之外的其他服務提供。在Kubernetes里的一些應用也需要訪問這些外部服務,典型場景為:
- 非Kubernetes管理的服務。例如單獨部署的數據庫、Redis、或者其他服務。
- 從一個Kubernetes集群訪問另一個Kubernetes集群。
本場景中,在創建Service時不設置selector標簽選擇器(也無后端Pod可選)。由于此 Service 沒有選擇器,因此不會自動創建對應的 EndpointSlice 對象。需要手工在EndpointSlice定義外部服務的IP地址和端口號。
注意:從v1.33版開始,Endpoints已被標記為deprecated。所以低版本Kubernetes可使用Endpoints,高版本Kubernetes應當使用EndpointSlice。
為演示,先通過python啟動簡易Web,模擬已存在的外部服務
# 若python版本為2.x
python -m SimpleHTTPServer 9999# 若python版本為3.x
python -m http.server 9999
下面outside-service.yaml
---
apiVersion: v1
kind: Service
metadata:name: outside-service
spec:ports:- port: 21212 # Service在clusterIP上的監聽端口targetPort: 1111 # 此情況因為不使用targetPort,所以配成什么端口都無所謂
---
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:name: outside-service # 習慣上,將Service名稱作為EndpointSlice名稱前綴labels:# 須設置 "kubernetes.io/service-name" 標簽,以選擇匹配Servicekubernetes.io/service-name: outside-service
addressType: IPv4 # 取值范圍:IPv4、IPv6、FQDN
ports: # 若列表多個須分別指定端口name(且name值須與Service中spec.ports.name字段值匹配一致)- port: 9999
endpoints: # 此列表中的 IP 地址可以按任何順序顯示- addresses:- "192.168.43.49"
kubectl apply -f outside-service.yaml# 以下service可簡寫svc
kubectl get service -o widekubectl describe service/outside-servicekubectl get endpointslices
可以在Kubernetes集群內(包括集群節點機器、Pod容器內)可通過命令訪問Service的虛擬IP和端口來訪問外部服務。curl http://172.16.155.209:21212
?? 1.6?Headless?Service
? ? 在某些場景場景中,不需要Service提供負載均衡功能,也不需要Service的ClusterIP。而是直接對后端Pod進行指定訪問,因此只需要Service為后端每個Pod生成一個子域名。這是可以創建一個與普通Service不同的Headless Service。Headless Service既是沒有入口訪問地址(服務無ClusterIP),kube-proxy不會為其創建負載轉發規則。
? ? 有標簽選擇器的Headless Service
? ? 如果Headless Service配置了標簽選擇器,則Kubernetes根據匹配選擇的后端Pod,自動創建Endpoints列表。此Headless Service會將DNS解析機制設為:①服務域名可得到后端所有Pod的IP列表(而不是單獨的一個ClusterIP);②還會為每個Pod都創建一個子域名,便于對特定某個Pod進行指定訪問或識別。
? ?示例可以參考:https://blog.csdn.net/zyplanke/article/details/150985065?中StatefulSet章節。
? ? 無標簽選擇器的Headless Service
? ? 如果Headless Service沒有配置標簽選擇器,則Kubernetes不會自動創建對應的Endpoints列表。Kubernetes的DNS會根據如下條件嘗試對該Service設置DNS解析:
- 對于 type=ExternalName,將配置的externalName嘗試作為對應的?CNAME 記錄。
- 對所有其他類型的 Service,對Service后端處于Ready狀態的Endpoints既Endpointslices創建DNS記錄。對于 IPv4 端點,DNS 系統創建 A 記錄;?對于 IPv6 端點,DNS 系統創建 AAAA 記錄。
二、Kubernetes的服務發現
? ? 對于在集群內運行的客戶端,Kubernetes 支持兩種Service發現模式:環境變量和DNS。
? ?2.1?環境變量方式
? ??當 Pod 運行時,kubelet 會自動為其注入以“KUBERNETES_”開頭的環境變量。
? ? ?除此環境變量外,根據當前已經存在且活躍?Service,kubelet還會對新創建的Pod自動為已存在的Service生成一組環境變量添加到Pod容器中。 kubelet添加的環境變量包括:
- {SVCNAME}_SERVICE_HOST
- {SVCNAME}_SERVICE_PORT
- {SVCNAME}_PORT
這里 Service 名稱被轉為大寫字母,橫線被轉換成下劃線。
在新創建Pod容器中,可以從環境變量獲取需要訪問的目的Service的地址了。
? ?2.2 DNS方式
? ? 對于環境變量方式,要求Service必須先于Pod創建好。這實際上對部署順序提出了較為苛刻的要求,而使用DNS方式就不存在此問題。 ?對于客戶端而言,DNS域名方式提供了一種穩定的、不變的訪問方式,可以簡化客戶端配置,是Kubernetes推薦的方式。
? ?Service遵從DNS命名規范:
Service的DNS域名命名規則:<ServiceName>.<namespace>.svc.<clusterDomain>?
? ?如果Service中的port端口號指定了名稱name,則該端口號也擁有DNS域名:
Service端口的DNS域名命名規則:_<PortName>._<Protocal>.<ServiceName>.<namespace>.svc.<clusterDomain>?