1. 程式人生 > 其它 >《kubernetes權威指南第五版》讀書筆記

《kubernetes權威指南第五版》讀書筆記

修改kubeadm的預設配置

  kubeadm的初始化控制平面(init)命令和加入節點(join)命令均可以通過指定的配置檔案修改預設引數的值。kubeadm將配置檔案以ConfigMap形式儲存到叢集中,便於後續的查詢和升級工作。kubeadm config子命令提供了對這組功能的支援。

  • kubeadm config print init-defaults:輸出kubeadm init命令預設引數的內容。
  • kubeadm config print join-defaults:輸出kubeadm join命令預設引數的內容。
  • kubeadm config migrate:在新舊版本之間進行配置轉換。
  • kubeadm config images list:列出所需的映象列表。
  • kubeadm config images pull:拉取映象到本地。
kubeadm config print init-defaults >init-default.yaml

  對生成的檔案進行編輯,可以按需生成合適的配置。例如,若需要自定義映象的倉庫地址、需要安裝的Kubernetes版本號及Pod的IP地址範圍

kubeadm config images list:列出所需的映象列表

  如果無法訪問k8s.gcr.io,則可以使用國內映象託管站點進行下載,例如https://1nj0zren.mirror.aliyuncs.com,這可以通過修改Docker服務的配置檔案(預設為/etc/docker/daemon.json)進行設定,例如:
  然後,使用kubeadm config images pull命令或者docker pull命令下載上述映象,

kubeadm config images pull --config=init-config.yaml

  在映象下載完成之後,就可以進行安裝了。

  在開始之前需要注意:kubeadm的安裝過程不涉及網路外掛(CNI)的初始化,因此kubeadm初步安裝完成的叢集不具備網路功能,任何Pod(包括自帶的CoreDNS)都無法正常工作。而網路外掛的安裝往往對kubeadm init命令的引數有一定要求

建立和使用命令列外掛


  為了擴充套件kubectl的功能,Kubernetes從1.8版本開始引入外掛機制,在1.14版本時達到穩定版。
  使用者自定義外掛的可執行檔名需要以“kubectl-”開頭,複製到$PATH中的某個目錄(如/usr/local/bin)下,然後就可以通過kubectl <plugin-name>執行自定義外掛了。
  例如,通過Shell指令碼實現一個名為hello的外掛,其功能為在螢幕上輸出字串“hello world”。建立名為“kubectl-hello”的Shell指令碼檔案
  通過外掛機制,可以將某些複雜的kubectl命令簡化為執行外掛的方式。例如想建立一個命令來檢視當前上下文環境(context)中的使用者名稱,則可以通過kubectl config view命令進行檢視

使用kubectl plugin list命令可以檢視當前系統中已安裝的外掛列表

Pod深入瞭解

pod生命週期和重啟策略

Pod在整個生命週期中被系統定義為各種狀態

  

Pod的重啟策略(RestartPolicy)應用於Pod內的所有容器

Pod的重啟策略包括Always、OnFailure和Never,預設值為Always。

  • Always:當容器失效時,由kubelet自動重啟該容器。
  • OnFailure:當容器終止執行且退出碼不為0時,由kubelet自動重啟該容器。
  • Never:不論容器執行狀態如何,kubelet都不會重啟該容器。

  kubelet重啟失效容器的時間間隔以sync-frequency乘以2n來計算,例如1、2、4、8倍等,最長延時5min,並且在成功重啟後的10min後重置該時間

  Pod的重啟策略與控制方式息息相關,當前可用於管理Pod的控制器包括ReplicationController、Job、DaemonSet,還可以通過kubelet管理(靜態Pod)。每種控制器對Pod的重啟策略要求如下

  • RC和DaemonSet:必須設定為Always,需要保證該容器持續執行
  • Job:OnFailure或Never,確保容器執行完成後不再重啟
  • kubelet:在Pod失效時自動重啟它,不論將RestartPolicy設定為什麼值,也不會對Pod進行健康檢查

Pod常見的狀態轉換場景

  

  RC的繼任者其實並不是Deployment,而是ReplicaSet,因為ReplicaSet進一步增強了RC標籤選擇器的靈活性。之前RC的標籤選擇器只能選擇一個標籤,而ReplicaSet擁有集合式的標籤選擇器,可以選擇多個Pod標籤
  其實,Kubernetes的滾動升級就是巧妙運用ReplicaSet的這個特性來實現的,同時,Deployment也是通過ReplicaSet來實現Pod副本自動控制功能的。我們不應該直接使用底層的ReplicaSet來控制Pod副本,而應該通過管理ReplicaSet的Deployment物件來控制副本

Pod健康檢查和服務可用性檢查

探針的種類

  Kubernetes對Pod的健康狀態可以通過三類探針來檢查:LivenessProbe、ReadinessProbe及StartupProbe,其中最主要的探針為LivenessProbe與ReadinessProbe,kubelet會定期執行這兩類探針來診斷容器的健康狀況。

(1)LivenessProbe探針:用於判斷容器是否存活(Running狀態),如果LivenessProbe探針探測到容器不健康,則kubelet將“殺掉”該容器,並根據容器的重啟策略做相應的處理。如果一個容器不包含LivenessProbe探針,那麼kubelet認為該容器的LivenessProbe探針返回的值永遠是Success。
(2)ReadinessProbe探針:用於判斷容器服務是否可用(Ready狀態),達到Ready狀態的Pod才可以接收請求。對於被Service管理的Pod,Service與Pod Endpoint的關聯關係也將基於Pod是否Ready進行設定。如果在執行過程中Ready狀態變為False,則系統自動將其從Service的後端Endpoint列表中隔離出去,後續再把恢復到Ready狀態的Pod加回後端Endpoint列表。這樣就能保證客戶端在訪問Service時不會被轉發到服務不可用的Pod例項上。需要注意的是,ReadinessProbe也是定期觸發執行的,存在於Pod的整個生命週期中。
(3)StartupProbe探針:某些應用會遇到啟動比較慢的情況,例如應用程式啟動時需要與遠端伺服器建立網路連線,或者遇到網路訪問較慢等情況時,會造成容器啟動緩慢,此時ReadinessProbe就不適用了,因為這屬於“有且僅有一次”的超長延時,可以通過StartupProbe探針解決該問題

探針均可配置以下三種實現方式

(1)ExecAction:在容器內部執行一個命令,如果該命令的返回碼為0,則表明容器健康

  通過執行cat/tmp/health命令來判斷一個容器執行是否正常。在該Pod執行後,將在建立/tmp/health檔案10s後刪除該檔案,而LivenessProbe健康檢查的初始探測時間(initialDelaySeconds)為15s,探測結果是Fail,將導致kubelet“殺掉”該容器並重啟它

(2)TCPSocketAction:通過容器的IP地址和埠號執行TCP檢查,如果能夠建立TCP連線,則表明容器健康。

(3)HTTPGetAction:通過容器的IP地址、埠號及路徑呼叫HTTP Get方法,如果響應的狀態碼大於等於200且小於400,則認為容器健康

對於每種探測方式,都需要設定initialDelaySeconds和timeoutSeconds兩個引數,它們的含義分別如下。

  • initialDelaySeconds:啟動容器後進行首次健康檢查的等待時間,單位為s。
  • timeoutSeconds:健康檢查傳送請求後等待響應的超時時間,單位為s。當超時發生時,kubelet會認為容器已經無法提供服務,將會重啟該容器。

  如下程式碼片段是StartupProbe探針的一個參考配置,可以看到,這個Pod可以有長達30×10=300s的超長啟動時間:

  

pod排程策略

Deployment或RC:全自動排程

  Deployment或RC的主要功能之一就是自動部署一個容器應用的多份副本,以及持續監控副本的數量,在叢集內始終維持使用者指定的副本數量
  Pod由系統全自動完成排程。它們各自最終執行在哪個節點上,完全由Master的Scheduler經過一系列演算法計算得出,使用者無法干預排程過程和結果
  除了使用系統自動排程演算法完成一組Pod的部署,Kubernetes也提供了多種豐富的排程策略,使用者只需在Pod的定義中使用NodeSelector、NodeAffinity、PodAffinity、Pod驅逐等更加細粒度的排程策略設定,就能完成對Pod的精準排程

NodeSelector:定向排程

  除了使用者可以自行給Node新增標籤,Kubernetes也會給Node預定義一些標籤,包括:

  • kubernetes.io/hostname;
  • beta.kubernetes.io/os(從1.14版本開始更新為穩定版,到1.18版本刪除);
  • beta.kubernetes.io/arch(從1.14版本開始更新為穩定版,到1.18版本刪除);
  • kubernetes.io/os(從1.14版本開始啟用);
  • kubernetes.io/arch(從1.14版本開始啟用)。

  NodeSelector通過標籤的方式,簡單實現了限制Pod所在節點的方法。親和性排程機制則極大擴充套件了Pod的排程能力,主要的增強功能如下。

  • 更具表達力(不僅僅是“符合全部”的簡單情況)。
  • 可以使用軟限制、優先採用等限制方式,代替之前的硬限制,這樣排程器在無法滿足優先需求的情況下,會退而求其次,繼續執行該Pod。
  • 可以依據節點上正在執行的其他Pod的標籤來進行限制,而非節點本身的標籤。這樣就可以定義一種規則來描述Pod之間的親和或互斥關係。

  親和性排程功能包括節點親和性(NodeAffinity)和Pod親和性(PodAffinity)兩個維度的設定。節點親和性與NodeSelector類似,增強了上述前兩點優勢;Pod的親和與互斥限制則通過Pod標籤而不是節點標籤來實現,也就是上面第4點內容所陳述的方式,同時具有前兩點提到的優點

NodeAffinity:Node親和性排程

  NodeAffinity意為Node親和性的排程策略,是用於替換NodeSelector的全新排程策略。目前有兩種節點親和性表達。

  • RequiredDuringSchedulingIgnoredDuringExecution:必須滿足指定的規則才可以排程Pod到Node上(功能與nodeSelector很像,但是使用的是不同的語法),相當於硬限制。
  • PreferredDuringSchedulingIgnoredDuringExecution:強調優先滿足指定規則,排程器會嘗試排程Pod到Node上,但並不強求,相當於軟限制。多個優先順序規則還可以設定權重(weight)值,以定義執行的先後順序。

  IgnoredDuringExecution的意思是:如果一個Pod所在的節點在Pod執行期間標籤發生了變更,不再符合該Pod的節點親和性需求,則系統將忽略Node上Label的變化,該Pod能繼續在該節點上執行。
  下面的例子設定了NodeAffinity排程的如下規則。

  • required DuringS chedulingIgnoredDuringExecution:要求只執行在amd64的節點上(beta.kubernetes.io/arch In amd64)。
  • preferredDuringSchedulingIgnoredDuringExecution:要求儘量執行在磁碟型別為ssd(disk-type In ssd)的節點上。

  NodeAffinity規則設定的注意事項如下。

  • 如果同時定義了nodeSelector和nodeAffinity,那麼必須兩個條件都得到滿足,Pod才能最終執行在指定的Node上。
  • 如果nodeAffinity指定了多個nodeSelectorTerms,那麼其中一個能匹配成功即可。
  • 如果在nodeSelectorTerms中有多個matchExpressions,則一個節點必須滿足所有matchExpressions才能執行該Pod。

PodAffinity:Pod親和與互斥排程策略

  與節點親和性類似,Pod親和性的操作符也包括In、NotIn、Exists、DoesNotExist、Gt、Lt。
  原則上,topologyKey可以使用任意合法的標籤Key賦值,但是出於效能和安全方面的考慮,對topologyKey有如下限制。

  • 在Pod親和性和RequiredDuringScheduling的Pod互斥性的定義中,不允許使用空的topologyKey。
  • 如果Admission controller包含了LimitPodHardAntiAffinityTopology,那麼針對Required DuringScheduling的Pod互斥性定義就被限制為kubernetes.io/hostname,要使用自定義的topologyKey,就要改寫或禁用該控制器。
  • 在PreferredDuringScheduling型別的Pod互斥性定義中,空的topologyKey會被解釋為kubernetes.io/hostname、failure-domain.beta.kubernetes.io/zone及failure-domain.beta.kubernetes.io/region的組合。

  如果不是上述情況,就可以採用任意合法的topologyKey了。

  PodAffinity規則設定的注意事項如下

  • 除了設定Label Selector和topologyKey,使用者還可以指定Namespace列表進行限制,同樣,使用Label Selector對Namespace進行選擇。Namespace的定義和Label Selector及topologyKey同級。省略Namespace的設定,表示使用定義了affinity/anti-affinity的Pod所在的名稱空間。如果Namespace被設定為空值(""),則表示所有名稱空間。
  • 在所有關聯requiredDuringSchedulingIgnoredDuringExecution的matchExpressions全都滿足之後,系統才能將Pod排程到某個Node上

Pod Priority Preemption:Pod優先順序排程

  在Kubernetes 1.8版本之前,當叢集的可用資源不足時,在使用者提交新的Pod建立請求後,該Pod會一直處於Pending狀態,即使這個Pod是一個很重要(很有身份)的Pod,也只能被動等待其他Pod被刪除並釋放資源,才能有機會被排程成功。

  Kubernetes 1.8版本引入了基於Pod優先順序搶佔(Pod Priority Preemption)的排程策略,此時Kubernetes會嘗試釋放目標節點上低優先順序的Pod,以騰出空間(資源)安置高優先順序的Pod,這種排程方式被稱為“搶佔式排程”。在Kubernetes 1.11版本中,該特性升級為Beta版本,預設開啟,在後續的Kubernetes 1.14版本中正式Release。如何宣告一個負載相對其他負載更重要?我們可以通過以下幾個維度來定義:Priority:優先順序;QoS:服務質量等級;系統定義的其他度量指標。

  優先順序搶佔排程策略的核心行為分別是驅逐(Eviction)與搶佔(Preemption),這兩種行為的使用場景不同,效果相同。Eviction是kubelet程序的行為,即當一個Node資源不足(under resource pressure)時,該節點上的kubelet程序會執行驅逐動作,此時kubelet會綜合考慮Pod的優先順序、資源申請量與實際使用量等資訊來計算哪些Pod需要被驅逐;當同樣優先順序的Pod需要被驅逐時,實際使用的資源量超過申請量最大倍數的高耗能Pod會被首先驅逐。對於QoS等級為“Best Effort”的Pod來說,由於沒有定義資源申請(CPU/Memory Request),所以它們實際使用的資源可能非常大。Preemption則是Scheduler執行的行為,當一個新的Pod因為資源無法滿足而不能被排程時,Scheduler可能(有權決定)選擇驅逐部分低優先順序的Pod例項來滿足此Pod的排程目標,這就是Preemption機制

  優先順序為100000,數字越大,優先順序越高,超過一億的數字被系統保留,用於指派給系統元件

DaemonSet:在每個Node上都排程一個Pod

  這種用法適合有這種需求的應用。

  • 在每個Node上都執行一個GlusterFS儲存或者Ceph儲存的Daemon程序。
  • 在每個Node上都執行一個日誌採集程式,例如Fluentd或者Logstach。
  • 在每個Node上都執行一個性能監控程式,採集該Node的執行效能資料,例如Prometheus Node Exporter、collectd、New Relic agent或者Ganglia gmond等。

  DaemonSet排程不同於普通的Pod排程,所以沒有用預設的Kubernetes Scheduler進行排程,而是通過專有的**DaemonSet Controller**進行排程。但是隨著Kubernetes版本的改進和排程特性不斷豐富,產生了一些難以解決的矛盾,最主要的兩個矛盾如下。

  • 普通的Pod是在Pending狀態觸發排程並被例項化的,DaemonSet Controller並不是在這個狀態排程Pod的,這種不一致容易誤導和迷惑使用者。
  • Pod優先順序排程是被Kubernetes Scheduler執行的,而DaemonSet Controller並沒有考慮到Pod優先順序排程的問題,也產生了不一致的結果。
  • 從Kubernetes 1.18開始,DaemonSet的排程預設切換到Kubernetes Scheduler進行,**從而一勞永逸地解決了以上問題及未來可能的新問題。因為預設切換到了Kubernetes Scheduler統一排程Pod,因此DaemonSet也能正確處理Taints和Tolerations的問題。

Taints和Tolerations(汙點和容忍)

  Pod的Toleration宣告中的key和effect需要與Taint的設定保持一致,並且滿足以下條件之一。

  • operator的值是Exists(無須指定value)。
  • operator的值是Equal並且value相等。
  • 如果不指定operator,則預設值為Equal。

  另外,有如下兩個特例

  • 空的key配合Exists操作符能夠匹配所有鍵和值。
  • 空的effect匹配所有effect

  幾種特殊情況

  • 如果在剩餘的Taint中存在effect=NoSchedule,則排程器不會把該Pod排程到這一節點上。
  • 如果在剩餘的Taint中沒有NoSchedule效果,但是有PreferNoSchedule效果,則排程器會嘗試不把這個Pod指派給這個節點。

Job:批處理排程

  

  考慮到批處理的並行問題,Kubernetes將Job分以下三種類型

(1)Non-parallel Jobs:通常一個Job只啟動一個Pod,除非Pod異常,才會重啟該Pod,一旦此Pod正常結束,Job將結束。
(2)Parallel Jobs with a fixed completion count:並行Job會啟動多個Pod,此時需要設定Job的.spec.completions引數為一個正數,當正常結束的Pod數量達至此引數設定的值後,Job結束。此外,Job的.spec.parallelism引數用來控制並行度,即同時啟動幾個Job來處理Work item。
(3)Parallel Jobs with a work queue:任務佇列方式的並行Job需要一個獨立的Queue,Work item都在一個Queue中存放,不能設定Job的.spec.completions引數,此時Job有以下特性。

  • 每個Pod都能獨立判斷和決定是否還有任務項需要處理。
  • 如果某個Pod正常結束,則Job不會再啟動新的Pod。
  • 如果一個Pod成功結束,則此時應該不存在其他Pod還在工作的情況,它們應該都處於即將結束、退出的狀態。
  • 如果所有Pod都結束了,且至少有一個Pod成功結束,則整個Job成功結束

Cronjob:定時任務

  首先,確保Kubernetes的版本為1.8及以上
  其次,需要掌握Cron Job的定時表示式,它基本上照搬了Linux Cron的表示式

pod的容災排程

  為了滿足這種容災場景下的特殊排程需求,在Kubernetes 1.16版本中首次引入Even Pod Spreading特性,用於通過topologyKey屬性識別Zone,並通過設定新的引數topologySpreadConstraints來將Pod均勻排程到不同的Zone
  關鍵的引數是maxSkew。maxSkew用於指定Pod在各個Zone上排程時能容忍的最大不均衡數:值越大,表示能接受的不均衡排程越大;值越小,表示各個Zone的Pod數量分佈越均勻。
  為了理解maxSkew,我們需要先理解skew引數的計算公式:skew[topo]=count[topo]-min(count[topo]),即每個拓撲區域的skew值都為該區域包括的目標Pod數量與整個拓撲區域最少Pod數量的差,而maxSkew就是最大的skew值

假如在上面的例子中有3個拓撲區域,分別為Zone A、Zone B及Zone C,有3個目標Pod需要排程到這些拓撲區域,那麼前兩個毫無疑問會被排程到Zone A和Zone B
可以手動計算每個Zone的skew,首先計算出min(count[topo])是0,對應Zone C,於是Zone A的skew=1-0=1,Zone B的skew=1-0=0,Zone C的skew=0-0=0,於是第3個Pod應該被放在Zone C,此時min(count[topo])的值就變成了1,而實際的maxSkew的值為0,符合預期設定。如果我們把maxSkew設定為2,則在這種情況下,第3個Pod被放在Zone A或Zone B都是符合要求的

StatefulSet

  在建立StatefulSet之前,需要確保在Kubernetes叢集中管理員已經建立好共享儲存,並能夠與StorageClass對接,以實現動態儲存供應的模式
  為了完成MongoDB叢集的搭建,需要部署以下三個資源物件。

  • 一個StorageClass:用於StatefulSet自動為各個應用Pod申請PVC。
  • 一個Headless Service(clusterIP: None):用於設定MongoDB例項的域名。
  • 一個StatefulSet

  mongo-sidecar作為MongoDB叢集的管理者,將使用此Headless Service來維護各個MongoDB例項之間的叢集關係,以及叢集規模變化時的自動更新

StatefulSet的常見應用場景

  • 叢集進行擴容,僅需要通過對StatefulSet進行scale操作(或修改yaml檔案)
  • 自動故障修復
    • 例項或其所在主機發生故障,則StatefulSet將會自動重建該例項,並保證其身份(ID)和使用的資料(PVC)不變

Service

Service概念和原理

  Service是Kubernetes實現微服務架構的核心概念,通過建立Service,可以為一組具有相同功能的容器應用提供一個統一的入口地址,並且將請求負載分發到後端的各個容器應用上

  Service用於為一組提供服務的Pod抽象一個穩定的網路訪問地址,是Kubernetes實現微服務的核心概念。通過Service的定義設定的訪問地址是DNS域名格式的服務名稱,對於客戶端應用來說,網路訪問方式並沒有改變(DNS域名的作用等價於主機名、網際網路域名或IP地址)。Service還提供了負載均衡器功能,將客戶端請求負載分發到後端提供具體服務的各個Pod上

  Service主要用於提供網路服務,通過Service的定義,能夠為客戶端應用提供穩定的訪問地址(域名或IP地址)和負載均衡功能,以及遮蔽後端Endpoint的變化,是Kubernetes實現微服務的核心資源

  Service不僅具有標準網路協議的IP地址,還以DNS域名的形式存在。Service的域名錶示方法為<servicename>.<namespace>.svc.<clusterdomain>,servicename為服務的名稱,namespace為其所在namespace的名稱,clusterdomain為Kubernetes叢集設定的域名字尾。服務名稱的命名規則遵循RFC 1123規範

Service負載均衡機制

  kube-proxy的代理模式、會話保持機制和基於拓撲感知的服務路由機制(EndpointSlices)

kube-proxy的代理模式

  kube-proxy提供了以下代理模式(通過啟動引數--proxy-mode設定)

  • userspace模式:使用者空間模式,由kube-proxy完成代理的實現,效率最低,不再推薦使用。
  • iptables模式:kube-proxy通過設定Linux Kernel的iptables規則,實現從Service到後端Endpoint列表的負載分發規則,效率很高。但是,如果某個後端Endpoint在轉發時不可用,此次客戶端請求就會得到失敗的響應,相對於userspace模式來說更不可靠。此時應該通過為Pod設定readinessprobe(服務可用性健康檢查)來保證只有達到ready狀態的Endpoint才會被設定為Service的後端Endpoint。
  • ipvs模式:在Kubernetes 1.11版本中達到Stable階段,kube-proxy通過設定Linux Kernel的netlink介面設定IPVS規則,轉發效率和支援的吞吐率都是最高的。ipvs模式要求Linux Kernel啟用IPVS模組,如果作業系統未啟用IPVS核心模組,kube-proxy則會自動切換至iptables模式。同時,ipvs模式支援更多的負載均衡策略,如下所述。
    • rr:round-robin,輪詢。
    • lc:least connection,最小連線數。
    • dh:destination hashing,目的地址雜湊。
    • sh:source hashing,源地址雜湊。
    • sed:shortest expected delay,最短期望延時。
    • nq:never queue,永不排隊。
  • kernelspace模式:Windows Server上的代理模式
會話保持機制

  Service支援通過設定sessionAffinity實現基於客戶端IP的會話保持機制,即首次將某個客戶端來源IP發起的請求轉發到後端的某個Pod上,之後從相同的客戶端IP發起的請求都將被轉發到相同的後端Pod上,配置引數為service.spec.sessionAffinity

  同時,使用者可以設定會話保持的最長時間,在此時間之後重置客戶端來源IP的保持規則,配置引數為service.spec.sessionAffinityConfig.clientIP.timeoutSeconds

基於拓撲感知的服務路由機制制(EndpointSlices)

將外部服務定義為Service

  普通的Service通過Label Selector對後端Endpoint列表進行了一次抽象,如果後端的Endpoint不是由Pod副本集提供的,則Service還可以抽象定義任意其他服務,將一個Kubernetes叢集外部的已知服務定義為Kubernetes內的一個Service,供叢集內的其他應用訪問,常見的應用場景包括:

  • 已部署的一個叢集外服務,例如資料庫服務、快取服務等;
  • 其他Kubernetes叢集的某個服務;
  • 遷移過程中對某個服務進行Kubernetes內的服務名訪問機制的驗證。

  對於這種應用場景,使用者在建立Service資源物件時不設定Label Selector(後端Pod也不存在),同時再定義一個與Service關聯的Endpoint資源物件,在Endpoint中設定外部服務的IP地址和埠號

  

將Service暴露到叢集外部

目前Service的型別如下。

  • ClusterIP:Kubernetes預設會自動設定Service的虛擬IP地址,僅可被叢集內部的客戶端應用訪問。當然,使用者也可手工指定一個ClusterIP地址,不過需要確保該IP在Kubernetes叢集設定的ClusterIP地址範圍內(通過kube-apiserver服務的啟動引數--service-cluster-ip-range設定),並且沒有被其他Service使用。
  • NodePort:將Service的埠號對映到每個Node的一個埠號上,這樣叢集中的任意Node都可以作為Service的訪問入口地址,即NodeIP:NodePort。
  • LoadBalancer:將Service對映到一個已存在的負載均衡器的IP地址上,通常在公有云環境中使用。
  • ExternalName:將Service對映為一個外部域名地址,通過externalName欄位進行設定

Service支援的網路協議

  • TCP:Service的預設網路協議,可用於所有型別的Service。
  • UDP:可用於大多數型別的Service,LoadBalancer型別取決於雲服務商對UDP的支援。
  • HTTP:取決於雲服務商是否支援HTTP和實現機制。
  • PROXY:取決於雲服務商是否支援HTTP和實現機制。
  • SCTP:從Kubernetes 1.12版本引入,到1.19版本時達到Beta階段,預設啟用,如需關閉該特性,則需要設定kube-apiserver的啟動引數--feature-gates=SCTPSupport=false進行關閉  

  Kubernetes從1.17版本開始,可以為Service和Endpoint資源物件設定一個新的欄位“AppProtocol”,用於標識後端服務在某個埠號上提供的應用層協議型別,例如HTTP、HTTPS、SSL、DNS等,該特性在Kubernetes 1.19版本時達到Beta階段,計劃於Kubernetes 1.20 版本時達到GA階段。要使用AppProtocol,需要設定kube-apiserver的啟動引數--feature-gates=ServiceAppProtocol=true進行開啟,然後在Service或Endpoint的定義中設定AppProtocol欄位指定應用層協議的型別

Kubernetes的服務發現機制

環境變數方式

  在一個Pod執行起來的時候,系統會自動為其容器執行環境注入所有叢集中有效Service的資訊。Service的相關資訊包括服務IP、服務埠號、各埠號相關的協議等,通過{SVCNAME}_SERVICE_HOST和{SVCNAME}_SERVICE_PORT格式進行設定。其中,SVCNAME的命名規則為:將Service的name字串轉換為全大寫字母,將中橫線“-”替換為下畫線“_”

DNS方式

  Service在Kubernetes系統中遵循DNS命名規範,Service的DNS域名錶示方法為<servicename>.<namespace>.svc.<clusterdomain>,其中servicename為服務的名稱,namespace為其所在namespace的名稱,clusterdomain為Kubernetes叢集設定的域名字尾(例如cluster.local),服務名稱的命名規則遵循RFC 1123規範的要求

  對於客戶端應用來說,DNS域名格式的Service名稱提供的是穩定、不變的訪問地址,可以大大簡化客戶端應用的配置,是Kubernetes叢集中推薦的使用方式。
  目前由CoreDNS作為Kubernetes叢集的預設DNS伺服器提供域名解析服務

  Service定義中的埠號如果設定了名稱(name),則該埠號也會擁有一個DNS域名,在DNS伺服器中以SRV記錄的格式儲存:_<portname>._<protocol>.<servicename>.<namespace>.svc.<clusterdomain>,其值為埠號的數值

Headless Service

  Headless Service的概念是這種服務沒有入口訪問地址(無ClusterIP地址),kube-proxy不會為其建立負載轉發規則,而服務名(DNS域名)的解析機制取決於該Headless Service是否設定了Label Selector

Headless Service沒有設定Label Selector

如果Headless Service沒有設定Label Selector,則Kubernetes將不會自動建立對應的Endpoint列表。DNS系統會根據下列條件嘗試對該服務名設定DNS記錄:

  • 如果Service的型別為ExternalName,則對服務名的訪問將直接被DNS系統轉換為Service設定的外部名稱(externalName);
  • 如果系統中存在與Service同名的Endpoint定義,則服務名將被解析為Endpoint定義中的列表,適用於非ExternalName型別的Service。

端點分片與服務拓撲

  Service的後端是一組Endpoint列表,為客戶端應用提供了極大的便利。但是隨著叢集規模的擴大及Service數量的增加,特別是Service後端Endpoint數量的增加,kube-proxy需要維護的負載分發規則(例如iptables規則或ipvs規則)的數量也會急劇增加,導致後續對Service後端Endpoint的新增、刪除等更新操作的成本急劇上升。

  舉例來說,假設在Kubernetes叢集中有10000個Endpoint執行在大約5000個Node上,則對單個Pod的更新將需要總計約5GB的資料傳輸,這不僅對叢集內的網路頻寬浪費巨大,而且對Master的衝擊非常大,會影響Kubernetes叢集的整體效能,在Deployment不斷進行滾動升級操作的情況下尤為突出。

  Kubernetes從1.16版本開始引入端點分片(Endpoint Slices)機制,包括一個新的EndpointSlice資源物件和一個新的EndpointSlice控制器。從Kubernetes 1.17版本開始,EndpointSlice機制預設是啟用的(在1.16版本中需要通過設定kube-apiserver和kube-proxy服務的啟動引數--feature-gates="EndpointSlice=true"進行啟用)

  另外,kube-proxy預設仍然使用Endpoint物件,為了提高效能,可以設定kube-proxy啟動引數--feature-gates="EndpointSliceProxying=true"讓kube-proxy使用EndpointSlice,這樣可以減少kube-proxy與master之間的網路通訊並提高效能。Kubernetes從1.19版本開始預設開啟該特性

  EndpointSlice通過對Endpoint進行分片管理來實現降低Master和各Node之間的網路傳輸資料量及提高整體效能的目標。對於Deployment的滾動升級,可以實現僅更新部分Node上的Endpoint資訊,Master與Node之間的資料傳輸量可以減少100倍左右,能夠大大提高管理效率

  

  預設情況下,在由EndpointSlice控制器建立的EndpointSlice中最多包含100個Endpoint,如需修改,則可以通過kube-controller-manager服務的啟動引數--max-endpoints-per-slice設定,但上限不能超過1000

  EndpointSlice的關鍵資訊如下。
(1)關聯的服務名稱:將EndpointSlice與Service的關聯資訊設定為一個標籤kubernetes.io/service-name=webapp,該標籤標明瞭服務名稱。
(2)地址型別AddressType:包括以下3種取值型別

  • IPv4:IPv4格式的IP地址
  • IPv6:IPv6格式的IP地址。
  • FQDN:全限定域名。

(3)在Endpoints列表中列出的每個Endpoint的資訊。

  • Addresses:Endpoint的IP地址。
  • Conditions:Endpoint狀態資訊,作為EndpointSlice的查詢條件。
  • Hostname:在Endpoint中設定的主機名hostname。
  • TargetRef:Endpoint對應的Pod名稱。
  • Topology:拓撲資訊,為基於拓撲感知的服務路由提供資料。

  目前EndpointSlice控制器自動設定的拓撲資訊如下。

  • kubernetes.io/hostname:Endpoint所在Node的名稱。
  • topology.kubernetes.io/zone:Endpoint所在的Zone資訊,使用Node標籤topology.kubernetes.io/zone的值,例如上例中的Node擁有“topology.kubernetes.io/zone:north”標籤。
  • topology.kubernetes.io/region:Endpoint所在的Region資訊,使用Node標籤topology.kubernetes.io/region的值。

(4)EndpointSlice的管理控制器:通過endpointslice.kubernetes.io/managed-by標籤進行設定,用於存在多個管理控制器的應用場景中

DNS服務搭建和配置指南

kubernetes中的dns發展史

  在Kubernetes 1.2版本時,DNS服務是由SkyDNS提供的,它由4個容器組成:kube2sky、skydns、etcd和healthz

  從Kubernetes 1.4版本開始,SkyDNS元件便被KubeDNS替換,主要考慮的是SkyDNS元件之間通訊較多,整體效能不高。KubeDNS由3個容器組成:kubedns、dnsmasq和sidecar,去掉了SkyDNS中的etcd儲存,將DNS記錄直接儲存在記憶體中,以提高查詢效能

  從Kubernetes 1.11版本開始,Kubernetes叢集的DNS服務便由CoreDNS提供。CoreDNS是CNCF基金會孵化的一個專案,是用Go語言實現的高效能、外掛式、易擴充套件的DNS服務端,目前已畢業。

  CoreDNS解決了KubeDNS的一些問題,例如dnsmasq的安全漏洞、externalName不能使用stubDomains進行設定,等等。CoreDNS支援自定義DNS記錄及配置upstream DNS Server,可以統一管理Kubernetes基於服務的內部DNS和資料中心的物理DNS。它沒有使用多個容器的架構,只用一個容器便實現了KubeDNS內3個容器的全部功能

  coreDNS的架構

    

修改每個Node上kubelet的啟動引數,在其中加上以下兩個引數。

◎ --cluster-dns=169.169.0.100:為DNS服務的ClusterIP地址。
◎ --cluster-domain=cluster.local:為在DNS服務中設定的域名。
然後重啟kubelet服務。

部署CoreDNS服務

  部署CoreDNS服務時需要建立3個資源物件:1個ConfigMap、1個Deployment和1個Service。在啟用了RBAC的叢集中,還可以設定ServiceAccount、ClusterRole、ClusterRoleBinding對CoreDNS容器進行許可權設定

  Deployment“coredns”主要設定CoreDNS容器應用的內容,其中,replicas副本的數量通常應該根據叢集的規模和服務數量確定,如果單個CoreDNS程序不足以支撐整個叢集的DNS查詢,則可以通過水平擴充套件提高查詢能力。由於DNS服務是Kubernetes叢集的關鍵核心服務,所以建議為其Deployment設定自動擴縮容控制器,自動管理其副本數量。

  另外,對資源限制部分(CPU限制和記憶體限制)的設定也應根據實際環境進行調整

  Service“kube-dns”是DNS服務的配置,這個服務需要設定固定的ClusterIP地址,也需要將所有Node上的kubelet啟動引數--cluster-dns都設定為這個ClusterIP地址

CoreDNS的配置說明

  CoreDNS的主要功能是通過外掛系統實現的。CoreDNS實現了一種鏈式外掛結構,將DNS的邏輯抽象成了一個個外掛,能夠靈活組合使用

常用的外掛如下。

  • loadbalance:提供基於DNS的負載均衡功能。
  • loop:檢測在DNS解析過程中出現的簡單迴圈問題。
  • cache:提供前端快取功能。
  • health:對Endpoint進行健康檢查。
  • kubernetes:從Kubernetes中讀取zone資料。
  • etcd:從etcd中讀取zone資料,可用於自定義域名記錄。
  • file:從RFC1035格式檔案中讀取zone資料。
  • hosts:使用/etc/hosts檔案或者其他檔案讀取zone資料,可用於自定義域名記錄。
  • auto:從磁碟中自動載入區域檔案。
  • reload:定時自動重新載入Corefile配置檔案的內容。
  • forward:轉發域名查詢到上游DNS伺服器上。
  • prometheus:為Prometheus系統提供採集效能指標資料的URL。
  • pprof:在URL路徑/debug/pprof下提供執行時的效能資料。
  • log:對DNS查詢進行日誌記錄。
  • errors:對錯誤資訊進行日誌記錄。

  域名“cluster.local”設定了一系列外掛,包括errors、health、ready、kubernetes、prometheus、forward、cache、loop、reload和loadbalance,在進行域名解析時,這些外掛將以從上到下的順序依次執行