Kubernetes-Service(服務)


⒈引用

  在Kubernetes中,pod通常需要對來自集群內部的其他pod或來自集群外部的客戶端的HTTP請求做出響應。pod需要一種尋找其他pod的方法來使用其他pod提供的服務,不像在沒有Kubernetes的世界,系統管理員需要在配置文件中明確指出服務的精確的IP地址或者主機名,這種方式在Kubernetes中並不適用,因為
    ·pod是短暫的一—它們隨時會啟動或者關閉,無論是為了給其他pod提供空間而從節點中被移除,或者是減少了pod的數量,又或者是因為節點異常。
    ·Kubernetes在pod啟動前會給已經調度到節點上的pod分配IP地址一—因此客戶端不能提前知道提供服務pod的IP地址。
    ·水平伸縮意味着多個pod可能會提供相同的服務一—每個pod都有自己的IP地址,客戶端無須關心后端提供服務pod的數量,以及各自對應的IP地址。它們無須記錄每個pod的IP地址。相反,所有的pod可以通過一個單一的IP地址進行訪問。
  為了解決上述問題,Kubernetes提供了一種資源類型——服務(service)。

⒉介紹

  Kubenetes服務是一種為一組功能相同的pod提供單一不變的接入點的資源。當服務存在時,它的IP地址和端口不會改變。客戶端通過IP地址和端口號建立連接,這些連接會被路由到提供該服務的任意一個pod上。通過這種方式,客戶端不需要知道每個單獨的提供服務的pod的地址,這樣這些pod就可以在集群中隨時被創建或移除。

⒊創建服務

  1.通過kubectl expose創建服務

  創建服務的最簡單的方法是通過kubectl expose,利用expose命令和pod選擇器來創建服務資源,從而通過單個的IP和端口來訪問所有的pod。

  2.通過YAML描述文件來創建服務

apiVersion: v1
kind: Service
metadata:
  name: coreqi
spec:
  ports:
  - port: 80  #該服務將在80端口接收請求
    targetPort: 8080  #服務將把請求轉發到pod的8080端口
  selector:
    app: coreqi  #具有app=coreqi標簽的pod都屬於該服務

  創建以上描述文件后使用kubectl命令創建服務

kubectl create -f coreqi-svc.yaml

⒋獲取所有服務(當面命名空間下)

kubectl get svc

⒌測試服務

  從內部集群測試服務可以通過以下幾種方法向服務發送請求:
    ·顯而易見的方法是創建一個pod,它將請求發送到服務的集群IP並記錄響應。可以通過查看pod日志檢查服務的響應。
    ·使用ssh遠程登錄到其中一個Kubernetes節點上,然后使用curl命令。
    ·可以通過kubectl exec命令在一個已經存在的pod中執行curl命令。

  以第三種方式為例,可以使用kubectl exec命令遠程地在一個已經存在的pod容器上執行任何命令。這樣就可以很方便地了解pod的內容、狀態及環境。用kubectl get pod命令列出所有的pod,並且選擇其中一個作為exec命令的執行目標。

kubectl exec {podName} --curl -s http://10.111.249.153

 ⒍服務的會話親和性

  如果向一個服務多次執行同樣的命令,每次調用都應該在不同的pod上執行。因為服務代理通常將每個連接隨機指向選中的后端pod中的一個,即使連接來自於同一個客戶端。
  如果希望特定客戶端產生的所有請求每次都指向同一個pod,可以設置服務的 sessionAffinity屬性為 ClientIP(而不是none,none是默認值)。這種方式將會使服務代理將來自同一個 客戶端 IP的所有請求轉發至同一個pod上。

  Kubernetes僅僅支持兩種形式的會話親和性服務:None和 ClientIP,你或許驚訝竟然不支持基於 cookie的會話親和性的選項,但是你要了解 Kubernetes服務不是在HTTP層面上工作。服務處理TP和UDP包,並不關心其中的載荷內容。因為 cookie HTTP是HTTP協議中的一部分,服務並不知道它們,這就解釋了為什么會話親和性不能基於 cookie。

apiVersion: v1
kind: Service
metadata:
  name: coreqi
spec:
  ports:
  - port: 80  #該服務將在80端口接收請求
    targetPort: 8080  #服務將把請求轉發到pod的8080端口
  selector:
    app: coreqi  #具有app=coreqi標簽的pod都屬於該服務
  sessionAffinity: ClientIP #會話親和性,默認值值為None【隨機指向pod】,設置為ClientIP則特定客戶端所產生的所有請求每次都會指向同一個pod,這種方式將會使服務代理將來自同一個client IP的所有請求轉發至同一個pod上。

⒎單個服務暴露多個端口

  創建的服務可以暴露一個端口,也可以暴露多個端口。比如,你的pod監聽兩個端口,比如HTTP監聽8080端口、 ,HTTP HTTPS,監聽8443端口,可以使用一個服務從端口80和443轉發至pod端口8080和8443。在這種情況下,無須創建兩個不同的服務。通過一個集群IP,使用一個服務就可以將多個端口全部暴露出來。
  注意:在創建一個有多個端口的服務的時候必須給每個端口指定名字。

apiVersion: v1
kind: Service
metadata:
  name: coreqi
spec:
  ports:
  - name: http
    port: 80  #該服務將在80端口接收請求
    targetPort: 8080  #服務將把請求轉發到pod的8080端口
  - name: https
    port: 443
    targetPort: 8443  
  selector:
    app: coreqi  #具有app=coreqi標簽的pod都屬於該服務
  sessionAffinity: ClientIP #會話親和性,默認值值為None【隨機指向pod】,設置為ClientIP則特定客戶端所產生的所有請求每次都會指向同一個pod,這種方式將會使服務代理將來自同一個client IP的所有請求轉發至同一個pod上。

  注意:服務的標簽選擇器應用於整個服務,不能對每個端口做單獨的配置。如果不同的pod有不同的端口映射關系,需要創建兩個服務。

⒏為端口號命名(起別名) 

#在pod的定義中指定port名稱
kind: Pod
spec: 
  containers:
  - name: coreqi
    ports:
    - name: http
      containerPort: 8080
    - name: https
      containerPort: 8443
#在Service的定義中引用
apiVersion: v1
kind: Service
spec:
  ports:
  - name: http
    port: 80 
    targetPort: http  
  - name: https
    port: 443
    targetPort: https

  采用命名端口最大的好處就是即使更換端口號也無須更改服務 spec,你的pod現在對http服務用的是8080,但是假設過段時間你決定將端口更換為80呢?
  如果你采用了命名的端口,僅僅需要做的就是改變pod中spec描述的端口號(當然你的端口號的名稱沒有改變)。在你的pod向新端口更新時,根據pod收到的連接(8080端口在舊的pod上、80端口在新的pod上),用戶連接將會轉發到對應的端口號上。

⒐服務發現

   通過創建服務,現在就可以通過一個單一穩定的IP地址訪問到pod。在服務整個生命周期內這個地址保持不變。在服務后面的pod可能刪除重建,它們的IP地址可能改變,數量也會增減,但是始終可以通過服務的單一不變的IP地址訪問到這些pod。

  但客戶端pod如何知道服務的IP和端口?是否需要先創建服務,然后手動查找其IP地址並將IP傳遞給客戶端pod的配置選項?當然不是。Kubernetes為客戶端提供了發現服務的IP和端口的方式。

  1.通過環境變量發現服務

  在pod開始運行的時候,Kubernetes會初始化一系列的環境變量指向現在存在的服務。如果你創建的服務早於客戶端pod的創建,則哭護短pod上的進程可以根據環境變量獲得服務的IP地址和端口號。但是如果服務的創建晚於哭護短pod的創建,那么關於這個服務的環境變量並沒有設置,這個問題也需要解決。

  查看客戶端pod的環境變量

kubectl exec {podName} env

  列表中將顯示集群中所有服務的環境變量(在客戶端pod創建之前所有服務的環境變量),其中環境變量使用HOST保存了該服務的IP地址,使用PORT保存了服務的端口。

  注意:環境變量保存的服務名稱中的橫杠被轉換為下畫線,並且當服務名稱用作環境變量名稱中的前綴時,所有的字母都是大寫的。

  2.通過DNS發現服務 

  默認情況下每一個命名空間下都有一個稱作kube-dns的pod,就像名字的暗示,這個pod運行DNS服務,集群中的其他pod都被配置為使用其作為dns(Kubernetes通過修改每個容器的/etc/resolv.conf文件實現)。集群中的其他pod所運行的DNS查詢都會被該DNS服務器響應,該服務器知道系統中運行的所有服務。
  注意:集群中的pod是否使用內部的DNS服務器是根據pod中spec的dnsPolicy屬性來決定的。
  每個服務從內部DNS服務器中獲得一個DNS條目,客戶端的pod在知道服務名稱的情況下可以通過全限定域名(FQDN)來訪問,而不僅僅只是通過環境變量。


  通過FQDN連接服務
  集群中的其他pod可以通過打開以下FQDN連接來訪問服務:

serviceName.default.svc.cluster.local

  其中serviceName對應於服務名稱,default表示當前服務所在的命名空間,而svc.cluster.local是在所有集群本地服務名稱中使用的可配置集群域后綴。
  注意:客戶端仍然必須知道服務的端口號。如果服務使用標准端口號(例如,HTTP的80端口或Postgres的5432端口),這樣是沒問題的。如果並不是標准端口,客戶端可以從環境變量中獲取端口號。
  如果其他pod和服務在同一個命名空間下,可以省略svc.cluster.local后綴,甚至命名空間。因此可以使用serviceName來指代服務。這簡單到不可思議。


  在pod容器中運行shell
  可以通過kubectl exec命令在一個pod容器上運行bash(或者其他形式的shell)。通過這種方式,可以隨意瀏覽容器,而無須為每個要運行的命令執行kubectl exec。
  注意:shell的二進制可執行文件必須在容器鏡像中可用才能使用。
  為了正常地使用shell,kubectl exec命令需要添加-it選項

進入容器內部運行bash shell
kubectl exec-it {podName} bash
通過任意一種方式訪問服務
1.curl http://serviceName.default.svc.cluster.local
2.curl http://serviceName.default
3.curl http://serviceName

  在請求的URL中,可以將服務的名稱作為主機名來訪問服務。因為每個pod容器在DNS解析器配置中都默認配置了可以將命名空間和svc.cluster.local后綴省略掉。通過以下命令查看一下pod容器中的/etc/resilv.conf文件就明白了。

cat /etc/resolv.conf

  

  無法ping 通服務IP的原因
  當我們使用curl這個服務時,這個服務是工作的,但是卻ping不通。這是因為服務的集群IP是一個虛擬IP,並且只有在與服務端口結合時才有意義。

 

⒑連接集群外部的服務

  如果我們希望不要讓Kubernetes服務將請求重定向到集群中的pod,而是讓它重定向到外部IP和端口。這樣做的話可以充分利用服務的負載平衡和服務發現。在集群中運行的客戶端pod可以像連接到內部服務一樣連接到外部服務。

  1.介紹服務endpoint

  在講述如何做到這一點之前,先闡述一下服務。服務並不是和pod直接相連的。相反,有一種資源介於兩者之間—-它就是Endpoint資源。如果之前在服務上運行過kubectl describe命令。

kubectl describe svc {serviceName}

  則展示服務的詳細列表內將會出現Selector、Endpoints屬性等等,其中Selector描述了服務用於創建endpoint列表的pod標簽選擇器。而Endpoints則描述了服務的endpoint的pod的IP和端口列表。

  Endpoint 資源就是暴露一個服務所指向的的IP地址和端口的列表,Endpoint資源和其他Kubernetes 資源一樣,所以可以使用kubectl info來獲取它的基本信息。

kubectl get endpoints {serviceName}

  盡管在服務中定義了pod選擇器,但在將請求轉發時不會直接使用它。相反,選擇器用於構建IP和端口列表,然后存儲在Endpoint資源中。當客戶端連接到服務時,服務代理會選擇這些IP和端口列表中的一個,並將傳入的連接重定向到選中的服務器。

  2.手動配置服務的 endpoint

  或許你已經意識到一點,只有將服務的endpoint與服務解耦后,才可以分別手動配置和更新它們。只有創建了不包含pod選擇器的服務,Kubernetes才不會創建Endpoint資源(畢竟,缺少選擇器,服務就不知道應該包括哪些pod)。這樣就需要手動創建Endpoint 資源來指定該服務的endpoint列表。
  要使用手動的方式配置服務的endpoint,需要分別創建服務和Endpoint資源。
  一、首先創建一個沒有選擇器的服務YAML描述文件。

#在該服務的描述文件中並沒有定義pod選擇器
apiVersion: v1
kind: Service
metadata:
  name: coreqi-service  #注意服務的名稱必須和Endpoint對象的名稱相匹配
spec:
  ports:
    port: 80  

 創建以上描述文件后使用kubectl命令創建服務

kubectl create -f coreqi-svc.yaml

  二、為沒有選擇器的服務創建 Endpoint 資源

  Endpoint是一個單獨的資源並不是服務的一個屬性。由於創建的資源中並不包含選擇器,相關的Endpoints資源並沒有自動創建,所以必須手動創建。

apiVersion: v1
kind: Endpoints
metadata:
  name: coreqi-service  #Endpoint的名稱必須和服務的名稱相匹配
subsets:
  - addresses:  #服務將請求重定向到endpoint的ip地址
    - ip: 11.11.11.11
    - ip: 22.22.22.22
    ports:  #endpoint的目標端口
    - port: 80

  創建以上描述文件后使用kubectl命令創建

  Endpoint對象需要與服務具有相同的名稱,並包含該服務的目標IP地址和端口列表。服務和Endpoint資源都發布到服務器后服務就可以像具有pod 選擇器那樣的服務正常使用。在服務創建之后所創建的容器將包含該服務的環境變量,並且與其IP:port對的所有連接都將在服務端點之間進行負載均衡。

  如果稍后決定將外部服務遷移到Kubernetes中運行的pod,可以為服務添加選擇器,從而對Endpoint進行自動管理。反過來也是一樣的一—將選擇器從服務中移除,Kubernetes將停止更新Endpoints。這意味着服務的IP地址可以保持不變,同時服務的實際實現卻發生了改變。

  3.為外部服務創建別名

  除了手動配置服務的Endpoint來代替公開外部服務方法,有一種更簡單的方法,就是通過其完全限定域名(FQDN)訪問外部服務
  創建 ExternalName類型的服務
  要創建一個具有別名的外部服務的服務時,要將創建服務資源的一個type字段設置為ExternalName。例如,設想一下在api.coreqi.cn上有公共可用的API,可以定義一個指向它的服務,如下面的代碼清單所示。

apiVersion: v1
kind: Service
metadata:
  name: coreqi-service
spec:
  type: ExternalName  #type被設置成ExternalName
  externalName: api.coreqi.cn #實際服務的完全限定域名
  ports:
    port: 80  

  服務創建完成后,pod可以通過coreqi-service.default.svc.cluster.local域名(甚至是coreqi-service)連接到外部服務,而不是使用服務的實際FQDN。這隱藏了實際的服務名稱及其使用該服務的pod的位置,如果以后要將其指向不同的服務,只需簡單地修改服務定義externalName屬性,或者將類型重新變回ClusterIP並為服務創建Endpoint--無論是手動創建,還是對服務上指定標簽選擇器使其自動創建。
  ExternalName服務僅在DNS級別實施一—為服務創建了簡單的CNAME DNS記錄。因此,連接到服務的客戶端將直接連接到外部服務,完全繞過服務代理。出於這個原因,這些類型的服務甚至不會獲得集群IP。
  注意:CNAME記錄指向完全限定的域名而不是數字IP地址。

⒒將服務暴露給外部客戶端

  到目前為止,只討論了集群內服務如何被pod使用;但是,還需要向外部公開某些服務。例如前端web服務器,以便外部客戶端可以訪問它們。
  有幾種方式可以在外部訪問服務:

  一、將服務的類型設置成NodePort--每個集群節點都會在節點上打開一個端口,對於NodePort服務,每個集群節點在節點本身(因此得名叫NodePort)上打開一個端口,並將在該端口上接收到的流量重定向到基礎服務。該服務僅在內部集群IP和端口上才可訪問,但也可通過所有節點上的專用端口訪問。
  二、將服務的類型設置成LoadBalance,NodePort類型的一種擴展--這使得服務可以通過一個專用的負載均衡器來訪問,這是由Kubernetes中正在運行的雲基礎設施提供的。負載均衡器將流量重定向到跨所有節點的節點端口。客戶端通過負載均衡器的IP連接到服務。
  三、創建一個Ingress資源,這是一個完全不同的機制,通過一個IP地址公開多個服務——它運行在HTTP層(網絡協議第7層)上,因此可以提供比工作在第4層的服務更多的功能。

 

  1.使用NodePort類型的服務  

  將一組pod公開給外部客戶端的第一種方法是創建一個服務並將其類型設置為NodePort。通過創建NodePort服務,可以讓Kubernetes在其所有節點上保留一個端口(所有節點上都使用相同的端口號),並將傳入的連接轉發給作為服務部分的pod。
  這與常規服務類似(它們的實際類型是ClusterIP),但是不僅可以通過服務的內部集群IP訪問NodePort服務,還可以通過任何節點的IP和預留節點端口訪問NodePort服務。
  當嘗試與NodePort服務交互時,意義更加重大。


  創建NodePort 類型的服務
  現在將創建一個NodePort服務,以查看如何使用它。下面的代碼清單顯示了服務的YAML。

apiVersion: v1
kind: Service
metadata:
  name: coreqi-service
spec:
  type: NodePort  #type被設置成NodePort
  ports:
  - port: 80  
    targetPort: 8080
    nodePort: 30123 #通過集群節點的30123端口可以訪問該服務
  selector:
    app: coreqi

  將類型設置為NodePort並指定該服務應該綁定到的所有集群節點的節點端口。指定端口不是強制性的。如果忽略它,Kubernetes將選擇一個隨機端口。
  注意當在GKE中創建服務時,kubectl將會打印出一個關於必須配置防火牆規則的警告。
  查看NodePort類型的服務
  查看該服務的基礎信息:

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM