K8S核心概念之SVC(易混淆難理解知識點總結)

渡邊徹發表於2021-11-18

本文將結合實際工作當中遇到的一些問題和情況來解析SVC的作用以及一些比較易混淆和難理解的概念,方便日後工作用到或者遺忘時可以直接在自己曾經學習總結的部落格當中直接查詢到。

首先應該清楚SVC的作用是什麼,SVC主要有以下兩個作用:、

一、服務發現

現在工作當中都將微服務專案部署到K8S上,因為每個專案都是很多個服務的集合,每個服務一般又都是由很多個pod組成的,那麼當請求想要訪問這個服務的時候如何將請求能夠很好地找到這些POD並將請求分發給他們呢?
即使是同一組服務他們的pod是在叢集的不同位置的,Ip也就各不相同,SVC就可以有效地將同一組服務繫結在一起,也就是提供了一個統一的服務訪問的入口,無論他們分發到哪個節點,也無論他們被分發了多少個不同的IP,SVC都可以做到將請求轉發到這組服務的其中一個POD中進行處理,k8s在建立SVC時候,會根據標籤選擇器selector(Lable selector)來查詢pod,據此建立與SVC同名的endpoint物件,當pod地址發生變化時,endpoint也會隨之發生變化,SVC接收到前端client請求的時候,就會通過endpoint,找到要轉發到哪個Pod進行訪問網站的地址。(至於要轉發到哪個節點的Pod,由負載均衡kube-proxy起初就決定好了的)
這裡面有幾個點需要說明一下:

1.endpoint是k8s叢集中的一個資源物件,儲存在etcd中,用來記錄一個service對應的所有pod的訪問地址。
2.只有當service配置selector(選擇器) ,endpoint controller才會自動建立對應的endpoint物件,否則,不會生成endpoint物件。
3.例:k8s叢集中建立一個名為hello的service,就會生成一個同名的endpoint物件,endpoint就是service關聯的pod的ip地址和埠。

二、負載均衡

因為每個SVC都是通過Label繫結微服務當中其中一個服務的一組POD,當外部或者叢集其他服務發來請求時,SVC會通過負載均衡,將請求分發到這一組POD當中的其中一個
注:這裡面要強調一下SVC是我們運維人員通過yaml檔案或者是kubectl命令指定規則並建立的,而底層真正實現負載均衡和服務發現的是K8S中執行在每個NODE節點的kube-proxy元件。

那麼SVC是如何通過kube-proxy元件來實現負載均衡和服務發現的呢?

kube-proxy工作原理

kube-proxy存在於各個node節點上,主要用於Service功能的實現(負載均衡和服務發現),具體來說,就是實現叢集內的客戶端pod訪問service,或者是叢集外的主機通過NodePort等方式訪問service。在之前版本的k8s中,kube-proxy預設使用的是iptables模式,通過各個node節點上的iptables規則來實現service的負載均衡,但是隨著service數量的增大,iptables模式由於線性查詢匹配、全量更新等特點,其效能會顯著下降。從k8s的1.8版本開始,kube-proxy引入了IPVS模式,IPVS模式與iptables同樣基於Netfilter,但是採用的hash表,因此當service數量達到一定規模時,hash查表的速度優勢就會顯現出來,從而提高service的服務效能。

注:kube-proxy通過watch的方式監控著kube-APIServer寫入etcd中關於Pod的最新狀態資訊,它一旦檢查到一個Pod資源被刪除了 或 新建,它將立即將這些變化,反應再iptables 或 ipvs規則中,以便iptables和ipvs在排程Clinet Pod請求到Server Pod時,不會出現Server Pod不存在的情況,如下圖所示:

image

如何建立SVC

對叢集內部暴露服務的模式--Cluster

apiVersion: v1
kind: Service
metadata:
  labels:
    app: my-dep
  name: my-dep
spec:
  ports:
  - port: 8000      #設定Serivce對叢集內部暴露的埠.
    protocol: TCP
    targetPort: 80  #設定Pod的埠,即Pod網路的埠。
  selector:
    app: my-dep
  type: ClusterIP

image

對叢集外部暴露服務的模式--NodePort

apiVersion: v1
kind: Service
metadata:
  labels:
    app: my-dep
  name: my-dep
spec:
  ports:
  - port: 8000      #設定Serivce對叢集內部暴露的埠.
    protocol: TCP   
    targetPort: 80  #設定Pod的埠,即Pod網路的埠。
  selector:
    app: my-dep
  type: NodePort


注:如果不在yaml檔案當中指定NodePort埠,則NodePort是在Node節點上的30000-32767之間隨機暴漏埠