帶你玩轉kubernetes-k8s(第37篇:核心組件運行機制-Kubelete)

Kubelet運行機制解析

   在Kubernetes集羣中,在每個Node(又稱Minion)上都會啓動一個kubelet服務進程。該進程用於處理Master下發到本節點的任務,管理Pod及Pod中的容器。每個kubelet進程都會在API Server上註冊節點自身的信息,定期向Master彙報節點資源的使用情況,並通過cAdvisor監控容器和節點資源。

節點管理

節點通過設置kubelet的啓動參數“--register-node”,來決定是否向API Server註冊自己。如果該參數的值爲true,那麼kubelet將試着通過API Server註冊自己。在自注冊時,kubelet啓動時還包含下列參數。
◎ --api-servers:APIServer的位置。
◎ --kubeconfig:kubeconfig文件,用於訪問API Server的安全配置文件。
◎ --cloud-provider:雲服務商(IaaS)地址,僅用於公有云環境。
     當前每個kubelet都被授予創建和修改任何節點的權限。但是在實踐中,它僅僅創建和修改自己。將來,我們計劃限制kubelet的權限,僅允許它修改和創建所在節點的權限。如果在集羣運行過程中遇到集羣資源不足的情況,用戶就很容易通過添加機器及運用kubelet的自注冊模式來實現擴容。

    在某些情況下,Kubernetes集羣中的某些kubelet沒有選擇自注冊模式,用戶需要自己去配置Node的資源信息,同時告知Node上Kubelet API Server的位置。集羣管理者能夠創建和修改節點信息。如果管理者希望手動創建節點信息,則通過設置kubelet的啓動參數“--register- node=false”即可完成。
     kubelet在啓動時通過API Server註冊節點信息,並定時向API Server發送節點的新消息,API Server在接收到這些信息後,將這些信息寫入etcd。通過kubelet的啓動參數“--node-status-update-frequency”設置kubelet每隔多長時間向API Server報告節點狀態,默認爲10s。

Pod管理

 kubelet通過以下幾種方式獲取自身Node上要運行的Pod清單。
(1)文件:kubelet啓動參數“--config”指定的配置文件目錄下的文件(默認目錄爲“/etc/ kubernetes/manifests/”)。通過--file-check-frequency設置檢查該文件目錄的時間間隔,默認爲20s。
(2)HTTP端點(URL):通過“--manifest-url”參數設置。通過--http-check-frequency設置檢查該HTTP端點數據的時間間隔,默認爲20s。
(3)API Server:kubelet通過API Server監聽etcd目錄,同步Pod列表。
     

     所有以非API Server方式創建的Pod都叫作Static Pod。kubelet將Static Pod的狀態彙報給API Server,API Server爲該Static Pod創建一個Mirror Pod和其相匹配。Mirror Pod的狀態將真實反映Static Pod的狀態。當Static Pod被刪除時,與之相對應的Mirror Pod也會被刪除。在本章中只討論通過API Server獲得Pod清單的方式。kubelet通過API Server Client使用Watch加List的方式監聽“/registry/nodes/$”當前節點的名稱和“/registry/pods”目錄,將獲取的信息同步到本地緩存中。
    kubelet監聽etcd,所有針對Pod的操作都會被kubelet監聽。如果發現有新的綁定到本節點的Pod,則按照Pod清單的要求創建該Pod。
   如果發現本地的Pod被修改,則kubelet會做出相應的修改,比如在刪除Pod中的某個容器時,會通過Docker Client刪除該容器。
如果發現刪除本節點的Pod,則刪除相應的Pod,並通過Docker Client刪除Pod中的容器。
kubelet讀取監聽到的信息,如果是創建和修改Pod任務,則做如下處理。
(1)爲該Pod創建一個數據目錄。
(2)從API Server讀取該Pod清單。
(3)爲該Pod掛載外部卷(External Volume)。
(4)下載Pod用到的Secret。
(5)檢查已經運行在節點上的Pod,如果該Pod沒有容器或Pause容器(“kubernetes/pause” 鏡像創建的容器)沒有啓動,則先停止Pod裏所有容器的進程。如果在Pod中有需要刪除的容器,這刪除這些容器。

(6)用“kubernetes/pause”鏡像爲每個Pod都創建一個容器。該Pause容器用於接管Pod中所有其他容器的網絡。每創建一個新的Pod,kubelet都會先創建一個Pause容器,然後創建其他容器。“kubernetes/pause”鏡像大概有200KB,是個非常小的容器鏡像。
(7)爲Pod中的每個容器做如下處理。
◎ 爲容器計算一個Hash值,然後用容器的名稱去查詢對應Docker容器的Hash值。若查找到容器,且二者的Hash值不同,則停止Docker中容器的進程,並停止與之關聯的Pause容器的進程;若二者相同,則不做任何處理。
◎ 如果容器被終止了,且容器沒有指定的restartPolicy(重啓策略),則不做任何處理。
◎ 調用Docker Client下載容器鏡像,調用Docker Client運行容器。

容器健康檢查

      Pod通過兩類探針來檢查容器的健康狀態。一類是LivenessProbe探針,用於判斷容器是否健康並反饋給kubelet。如果LivenessProbe探針探測到容器不健康,則kubelet將刪除該容器,並根據容器的重啓策略做相應的處理。如果一個容器不包含LivenessProbe探針,那麼kubelet認爲該容器的LivenessProbe探針返回的值永遠是Success;另一類是ReadinessProbe探針,用於判斷容器是否啓動完成,且準備接收請求。如果ReadinessProbe探針檢測到容器啓動失敗,則Pod的狀態將被修改,EndpointController將從Service的Endpoint中刪除包含該容器所在Pod的IP地址的Endpoint條目。
kubelet定期調用容器中的LivenessProbe探針來診斷容器的健康狀況。LivenessProbe包含以下3種實現方式。
(1)ExecAction:在容器內部執行一個命令,如果該命令的退出狀態碼爲0,則表明容器健康。
(2)TCPSocketAction:通過容器的IP地址和端口號執行TCP檢查,如果端口能被訪問,則表明容器健

(3)HTTPGetAction:通過容器的IP地址和端口號及路徑調用HTTP Get方法,如果響應的狀態碼大於等於200且小於等於400,則認爲容器狀態健康。

      LivenessProbe探針被包含在Pod定義的spec.containers.{某個容器}中。下面的例子展示了兩種Pod中容器健康檢查的方式:  HTTP檢查和容器命令執行檢查。下面所列的內容實現了通過容器命令執行檢查:

livenessProbe:
  exec:
    command:
    - cat
    - /tmp/health
  initialDelaySeconds: 15
  timeoutSeconds: 1

  kubeelet在容器中執行“cat /tmp/health”命令,如果該命令返回的值爲0,則表明容器處於健康狀態,否則表明容器處於不健康狀態。

下面所列的內容實現了容器的HTTP檢查:

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 15
  timeoutSeconds: 1

     kubelet發送一個HTTP請求到本地主機、端口及指定的路徑,來檢查容器的健康健康狀況。

cAdvisor資源監控

   在Kubernetes集羣中,應用程序的執行情況可以在不同的級別上監測到,這些級別包括:容器、Pod、Service和整個集羣。作爲Kubernetes集羣的一部分,Kubernetes希望提供給用戶詳細的各個級別的資源使用信息,這將使用戶深入地瞭解應用的執行情況,並找到應用中可能的瓶頸。
cAdvisor是一個開源的分析容器資源使用率和性能特性的代理工具,它是因爲容器而產生的,因此自然支持Docker容器,在Kubernetes項目中,cAdvisor被集成到Kubernetes代碼中,kubelet則通過cAdvisor獲取其所在節點及容器的數據。cAdvisor自動查找所有在其所在Node上的容器,自動採集CPU、內存、文網絡系統和使用的統計信息。在大部分Kubernetes集羣中,cAdvisor通過它所在Node的4194端口暴露一個簡單的UI。
    kubelet作爲連接Kubernetes Master和各Node之間的橋樑,管理運行在Node上的Pod和容器。kubelet將每個Pod都轉換成它的成員容器,同時從cAdvisor獲取單獨的容器使用統計信息,然後通過該REST API暴露這些聚合後的Pod資源使用的統計信息。

    cAdvisor只能提供2~3min的監控數據,對性能數據也沒有持久化,因此在Kubernetes早期版本中需要依靠Heapster來實現集羣範圍內全部容器性能指標的採集和查詢功能。從Kubernetes 1.8版本開始,性能指標數據的查詢接口升級爲標準的Metrics API,後端務則升級爲全新的Metrics Server。因此,cAdvisor在4194端口提供的UI和API服務從Kubernetes 1.10版本開始進入棄用流程,並於1.12版本完全關閉。如果還希望使用cAdvisor的這個特性,則從1.13版本開始可以通過部署一個DaemonSet在每個Node上啓動一個cAdvisor來提供UI和API,請參考cAdvisor在GitHub上的說明(https://github.com/google/cadvisor)。
在新的Kubernetes監控體系中,Metrics Server用於提供Core Metrics(核心指標),包括Node和Pod的CPU和內存使用數據。其他Custom Metrics(自定義指標)則由第三方組件(如Prometheus)採集和存儲。

kube-proxy運行機制解析

    我們在前面已經瞭解到,爲了支持集羣的水平擴展、高可用性,Kubernetes抽象出了Service的概念。Service是對一組Pod的抽象,它會根據訪問策略(如負載均衡策略)來訪問這組Pod。 

     Kubernetes在創建服務時會爲服務分配一個虛擬的IP地址,客戶端通過訪問這個虛擬的IP地址來訪問服務,服務則負責將請求轉發到後端的Pod上。這不就是一個反向代理嗎?沒錯,這就是一個反向代理。但是,它和普通的反向代理有一些不同:首先,它的IP地址是虛擬的,想從外面訪問還需要一些技巧;其次,它的部署和啓停是由Kubernetes統一自動管理的。
     在很多情況下,Service只是一個概念,而真正將Service的作用落實的是它背後的kube-proxy服務進程。只有理解了kube-proxy的原理和機制,我們才能真正理解Service背後的實現邏輯。

     在Kubernetes集羣的每個Node上都會運行一個kube-proxy服務進程,我們可以把這個進程看作Service的透明代理兼負載均衡器,其核心功能是將到某個Service的訪問請求轉發到後端的多個Pod實例上。此外,Service的Cluster IP與NodePort等概念是kube-proxy服務通過iptables的NAT轉換實現的,kube-proxy在運行過程中動態創建與Service相關的iptables規則,這些規則實現了將訪問服務(Cluster IP或NodePort)的請求負載分發到後端Pod的功能。由於iptables機制針對的是本地的kube-proxy端口,所以在每個Node上都要運行kube-proxy組件,這樣一來,在Kubernetes集羣內部,我們可以在任意Node上發起對Service的訪問請求。綜上所述,由於kube-proxy的作用,在Service的調用過程中客戶端無須關心後端有幾個Pod,中間過程的通信、負載均衡及故障恢復都是透明的。
      起初,kube-proxy進程是一個真實的TCP/UDP代理,類似HA Proxy,負責從Service到Pod的訪問流量的轉發,這種模式被稱爲userspace(用戶空間代理)模式。,當某個Pod以Cluster IP方式訪問某個Service的時候,這個流量會被Pod所在本機的iptables轉發到本機的kube-proxy進程,然後由kube-proxy建立起到後端Pod的TCP/UDP連接,隨後將請求轉發到某個後端Pod上,並在這個過程中實現負載均衡功能。

    Kubernetes從1.2版本開始,將iptables作爲kube-proxy的默認模式。iptables模式下的kube-proxy不再起到Proxy的作用,其核心功能:通過API Server的Watch接口實時跟蹤Service與Endpoint的變更信息,並更新對應的iptables規則,Client的請求流量則通過iptables的NAT機制“直接路由”到目標Pod。

   根據Kubernetes的網絡模型,一個Node上的Pod與其他Node上的Pod應該能夠直接建立雙向的TCP/IP通信通道,所以如果直接修改iptables規則,則也可以實現kube-proxy的功能,只不過後者更加高端,因爲是全自動模式的。與第1代的userspace模式相比,iptables模式完全工作在內核態,不用再經過用戶態的kube-proxy中轉,因而性能更強。
       iptables模式雖然實現起來簡單,但存在無法避免的缺陷:在集羣中的Service和Pod大量增加以後,iptables中的規則會急速膨脹,導致性能顯著下降,在某些極端情況下甚至會出現規則丟失的情況,並且這種故障難以重現與排查,於是Kubernetes從1.8版本開始引入第3代的IPVS(IP Virtual Server)模式。

      iptables與IPVS雖然都是基於Netfilter實現的,但因爲定位不同,二者有着本質的差別:iptables是爲防火牆而設計的;IPVS則專門用於高性能負載均衡,並使用更高效的數據結構(Hash表),允許幾乎無限的規模擴張,因此被kube-proxy採納爲第三代模式。

與iptables相比,IPVS擁有以下明顯優勢:
◎ 爲大型集羣提供了更好的可擴展性和性能;
◎ 支持比iptables更復雜的複製均衡算法(最小負載、最少連接、加權等);
◎ 支持服務器健康檢查和連接重試等功能;
◎ 可以動態修改ipset的集合,即使iptables的規則正在使用這個集合。
     由於IPVS無法提供包過濾、airpin-masquerade tricks(地址僞裝)、SNAT等功能,因此在某些場景(如NodePort的實現)下還要與iptables搭配使用。在IPVS模式下,kube-proxy又做了重要的升級,即使用iptables的擴展ipset,而不是直接調用iptables來生成規則鏈。
     iptables規則鏈是一個線性的數據結構,ipset則引入了帶索引的數據結構,因此當規則很多時,可以很高效地查找和匹配。我們可以將ipset簡單理解爲一個IP(段)的集合,這個集合的內容可以是IP地址、IP網段、端口等,iptables可以直接添加規則對這個“可變的集合”進行操作,這樣做的好處在於可以大大減少iptables規則的數量,從而減少性能損耗。
     假設要禁止上萬個IP訪問我們的服務器,則用iptables的話,就需要一條一條地添加規則,會在iptables中生成大量的規則;但是用ipset的話,只需將相關的IP地址(網段)加入ipset集合中即可,這樣只需設置少量的iptables規則即可實現目標

kube-proxy針對Service和Pod創建的一些主要的iptables規則如下。
◎ KUBE-CLUSTER-IP:在masquerade-all=true或clusterCIDR指定的情況下對Service Cluster IP地址進行僞裝,以解決數據包欺騙問題。
◎ KUBE-EXTERNAL-IP:將數據包僞裝成Service的外部IP地址。
◎ KUBE-LOAD-BALANCER、KUBE-LOAD-BALANCER-LOCAL:僞裝Load Balancer 類型的Service流量。

◎ KUBE-NODE-PORT-TCP、KUBE-NODE-PORT-LOCAL-TCP、KUBE-NODE-PORTUDP、KUBE-NODE-PORT-LOCAL-UDP:僞裝NodePort類型的Service流量。

小結:

       到這裏,k8s核心組件機制就講解玩了,這一章的內容,大家瞭解一下,就可以了。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章